[CODE REVIEW] How to improve structure of Service and DAO Layers

370 views
Skip to first unread message

Karan Singh

unread,
Aug 19, 2020, 12:39:27 PM8/19/20
to golang-nuts

I am having a bit of difficulty in designing my Go code.

I'll try to demonstrate things with a very simple example of adding a new user to the database. I have 3 major components in this example: a handler, a service and a dao layer.

  1. My Handler is only doing one thing: Calling a service.

  2. The service is managing all the business logic and then calling the DAO to interact with the database.

  3. The DAO also has only responsibility: Storing in the database.

Also, just for context I am making GraphQL(gqlgen) requests to interact with my ElasticSearch(olivere/elastic) database, but that shouldn't affect the design too much.

MY CODE:

https://play.golang.org/p/Wm9qubLJIGM

I have read a lot of posts about using structs/interfaces to handle the service layer and DAO layer, but I am unable to implement the same in my usecase.

I don't have a background in Java/C# so I'm not well versed with OOP Principles but I did read a lot on MVC based on other answers.

My code works for now, but is it bad code?

I don't want to be passing parameters from one function to another. Is there a way to that with receivers on methods?

Diego Medina

unread,
Oct 10, 2020, 8:03:49 PM10/10/20
to golang-nuts
Hi Karan,

>I don't have a background in Java/C#[...]

This is actually good, there is less for you to "unlearn"

If you don't want to pass parameters to create a user, like passing the userID, name, etc, because it "seems wrong/verbose", then don't worry about it.
In Go (any I apply this to other languages too), it's better to be explicit with the parameters that your functions or methods need, vs being "clever" and try to hide the fact that to create
a models.User instance, you need 10 pieces of data.

When I write Go code or when I do code review in our company, I ask questions like:

* Will future junior devs have a hard time reading/understanding the code?
If the answer is yes, then I need to rewrite it, code should be easy to follow (most of the time, of course there are going to be exceptions, but try to write simple code as much as you can)
* If I get a call at 3am to debug our app, will it take me hours to remember why this code is written like this?
Again, the idea is, code should be essy to follow, no hiding and no adding abstractions just because people do that in Java/etc
P.S. Of course I don't want to even get a 3am call :)

Hope this helps.

Diego

Jesper Louis Andersen

unread,
Oct 12, 2020, 7:57:08 AM10/12/20
to Karan Singh, golang-nuts
On Wed, Aug 19, 2020 at 6:38 PM Karan Singh <karansi...@gmail.com> wrote:

Rough comments:

1. I tend to shy away from packages named "utils" since they often attract functions from all over the place of a project. It is often better to keep such functions in the packages where they are used, or name them after what kind of functions they contain.

2. I find it odd that the service layer knows how to turn a model.User{} into a string and then pass that to the underlying DAO. I'd probably say that such details are relevant for handling in the DAO layer, so the layers above can work mostly with users from the model, and avoid representation details in ElasticSearch.

3. You can toy with the idea of adding a method ESData() to your model.User{} such that you can call this in the CreateUser of the DAO layer. You may be able to do this with an interface too, for certain things that can vary, i.e., updating documents in ES.

4. This is more of a high-level advice: ElasticSearch is not a database. It is a search engine. You should always store your data in a proper database, and then export said data to the search engine afterwards. The two major reasons are that a search engine doesn't generally worry as much about data integrity as a database would. And that a search engine is allowed to answer approximately: if you ask it for all red shoes, then it is allowed to return the most relevant shoes it can find in, say, 200ms. Whereas the same database query is forced to return every match, even if this query takes several minutes to complete. This is also where DAO models come into play: updates to the database should probably queue updates for ES as a side effect.

--
J.

Brian Candler

unread,
Oct 12, 2020, 8:38:20 AM10/12/20
to golang-nuts
On Monday, 12 October 2020 12:57:08 UTC+1, Jesper Louis Andersen wrote:
Rough comments:

1. I tend to shy away from packages named "utils" since they often attract functions from all over the place of a project. It is often better to keep such functions in the packages where they are used, or name them after what kind of functions they contain.

There is a nice article on this topic here: https://blog.golang.org/package-names

Reply all
Reply to author
Forward
0 new messages