bxcodec / go-clean-arch

Go (Golang) Clean Architecture based on Reading Uncle Bob's Clean Architecture
MIT License
9.06k stars 1.19k forks source link

Why domain model know about json? #50

Open Bogdaan opened 4 years ago

Bogdaan commented 4 years ago
  1. Why domain model know about json (presentation level)?
  2. Why domain model know about input validation?
    type Article struct {
    ID        int64     `json:"id"`
    Title     string    `json:"title" validate:"required"`
    Content   string    `json:"content" validate:"required"`
    Author    Author    `json:"author"`
    UpdatedAt time.Time `json:"updated_at"`
    CreatedAt time.Time `json:"created_at"`
    }

Maybe better to separate this concerns.

aeharvlee commented 4 years ago

I think it is not strange. It is really helpful using Tags in structure. Domain in the clean architecture means "What kind of data structure will you use in the project?".

With tags, we can interpret that file like "Oh, the project of Bogdaan will use a Article data structure. There is fields which describe details of Article and I can see json tags which will be used when binding request or response. Title and Content of Article must be used when request Article in the project. I see how this domain(=Article) works."

So in my opinion, tags should be in the domain file. Separating files would increase more complex.

devarsh commented 4 years ago

@Bogdaan something I read which cleared my doubts about handling validation, you might want check it out: https://medium.com/@apzuk3/input-validation-in-golang-bc24cdec1835

stackus commented 4 years ago

My take is the json and validation tags, and tags in general do not belong in the domain.

I don't accept or return my domain entities in my delivery layer. Instead delivery specific input and output structs are used and on those I'll use json tags. Using additional structs does add complexity and I am comfortable with that because to me using them has benefits. I'm protected from additions or other changes to my entities being accidentally exposed as a value that can be submitted or displayed. I also may not marshal the entity the same way everywhere.

I would also make the case that the validation struct tags do not belong because they don't describe the business rules they are meant to enforce. Are these used during the article draft creation? Are they used when the article is promoted and published? A NewArticle(title, content string) Article constructor in the domain would be the route I'd take.

Validation would be handled in a couple places and for different concerns. Delivery would validate structures, and garbage input checks but nothing business related. Service/Usecases would validate business rules like uniqueness, ownership, relationships, aggregate stuff. Domain entities would have methods to modify internal state and would also do validation.

The AWS DynamoDB SDK uses dynamodbav:"title" tags for marshalling and to me it is an easy argument that they shouldn't exist within the domain. I choose to apply this to all tags vs making exceptions.

I do have an open mind and a good counter opinion would be considered.

bxcodec commented 4 years ago

Hi everyone, thanks for your opinions and recommendations. I'll answer from my side as the one who made this repository.

I agree with @Bogdaan and @stackus to be honest. I've been thinking about this quite a long time ago. I agree to have a separate data structure in Controller and in Repository.

But IRL, I never met projects that have a complex marshall method. So, to increase the pace for development, I put the struct tag (JSON and validate) in the domain.

But there's a case I forgot which project it is, I have 3 databases, MongoDB, MySQL, AeroSpike. MongoDB has the bson tag. For this case, I made my own struct for the repository layer (MongoDB part). And Aerospike, since the data structure is different, I also define my own struct in the repository. So I will map the domain struct to the repository's struct in the repository layer. So it's the same as @stackus said. But it's only happened once so far since my project is only around REST and Postgres in the past year.

About this current project in this Github repo, yes, I only put the tag in the domain for the sake of its simplicity LOL. I mean, this is only a simple CRUD.

But yeah, I agree with putting the specific tag to its respective layer. JSON and Validate in the controller, BSON, and Dynamodeb tag in the repository.

AlexanderMatveev commented 3 years ago

I want to add one case that forces me to stop using tags in the domain entity. This is a simple differentiation of access: one user can see specific fields of the domain entity, the second cannot.