Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Seperate Go structs for the repository pattern #37

Closed
emilgelman opened this issue Aug 22, 2021 · 2 comments
Closed

Seperate Go structs for the repository pattern #37

emilgelman opened this issue Aug 22, 2021 · 2 comments

Comments

@emilgelman
Copy link

Hello,

Let me start by saying that I really enjoy reading your book, I find it extremely useful and very well written.

We are trying to follow the clean architecture principles in our team, and we have a question about the correct way to separate between models.

Assuming we have the following layers:

  1. API layer - using http
  2. Service layer - contains business logic
  3. Persistence layer - using the Repository pattern

We have some confusion around the best practices around which models to use where.

For example, what we currently do:

  1. Accept a request in HTTP, decode the request body into a DTO struct which is defined in the api package.
  2. Convert this struct into a "domain" struct (using mapstructure or a plain function), and pass it to the Service layer.
  3. In the service layer, we need to call some function from the Repository layer.

Here comes the problem:
Should the repository layer accept and return a domain model, or a database model?
If it accepts and returns a domain model, each implementation is responsible for decoding it to the whatever it wants, which feels more robust. However, it means that the repository layer is now aware of the domain layer, which doesn't feel right.

So basically my question is: which of the following options is considered more correct:

  1. Pass the repository a service model, which means the implementation will decide on how to use it and return it
type Repository interface {
  Insert(model ServiceModel) (ServiceModel,error)
}
  1. Add a DbModel struct to the database package, which means that the service model instantiates it and passes it to the repository
type Repository interface {
 Insert(model DbModel) (DbModel,error)
}

Appreciate your response!

@m110
Copy link
Member

m110 commented Aug 22, 2021

Hey @emilgelman! I'm really happy you've found the book useful.

Should the repository layer accept and return a domain model, or a database model?

The domain model.

However, it means that the repository layer is now aware of the domain layer, which doesn't feel right.

Actually, that's how it should be. Remember that your repository is not a proxy for database table's CRUD methods. It's responsible for storing and retrieving domain objects, so it needs to have some idea of your domain.

Clean architecture assumes that implementation details can know about the inner layers but not the other way around. If you'd follow the second option, the database model would leak into the service layer, and you don't want that.

image

I'm a bit confused whether you use a separate "service model" and "domain model" or it's the same one?

Convert this struct into a "domain" struct (using mapstructure or a plain function), and pass it to the Service layer.

I'm concerned your domain objects might not be properly encapsulated if you use mapstructure to fill them. Maybe that's why using them in the storage layer doesn't seem right to you? Usually, you'd use only constructors from domain to create them, and their state should change only by the object's methods.

In my recent article, I mentioned a few tips on separating the structs, perhaps it will clear some things out. If not, let me know. 🙂

@emilgelman
Copy link
Author

Hi Miłosz,

Thank you for the response. After reading the article, it all makes sense, thanks again!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants