Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Separating business rules from entities in domain driven design

While i am practicing DDD in my software projects, i have always faced the question of "Why should i implement my business rules in the entities? aren't they supposed to be pure data models?"

Note that, from my understanding of DDD, domain models could be consist of persistent models as well as value objects.

I have come up with a solution in which i separate my persistent models from my domain models. On the other hand we have data transfer objects (DTO), so we have 3 layers of data mapping. Database to persistence model, persistence model to domain models and domain models to DTOs. In my opinion, my solution is not an efficient one as too much hard effort must be put into it.

Therefore is there any better practice to achieve this goal?

like image 725
Behzad Avatar asked Dec 05 '25 14:12

Behzad


1 Answers

Disclaimer: this answer is a little larger that the question but it is needed to understand the problem; also is 100% based on my experience.

What you are feeling is normal, I had the same feeling some time ago. This is because of a combination of architecture, programming language and used framework. You should try to choose the above tools as such that they give the code that is easiest to change. If you have to change 3 classes for each field added to an entity then this would be nightmare in a large project (i.e. 50+ entity types).

The problem is that you have multiple DTOs per entity/concept.

The heaviest architecture that I used was the Classic layered architecture; the strict version was the hardest (in the strict version a layer may access only the layer that is just before it; i.e. the User interface may access only the Application). It involved a lot of DTOs and translations as the data moved from the Infrastructure to the UI. The testing was also hard as I had to use a lot of mocking.

Classic layeredarchitecture

Then I inverted the dependency, the Domain will not depend on the Infrastructure. For this I defined interfaces in the Domain layer that were implemented in the Infrastructure. But I still needed to use mocking for them. Also, the Aggregates were not pure and they had side effects (because they called the Infrastructure, even it was abstracted by interfaces).

Then I moved the Domain to the very bottom. This made my Aggregates pure. I no longer needed to use mocking. But I still needed DTOs (returned by the Application layer to the UI and those used by the ORM).

enter image description here

Then I made the first leap: CQRS. This splits the models in two: the write model and the read model. The important thing is that you don't need to use DTOs for models anymore. The Aggregate (the write model) can be serialized as it is or converted to JSON and stored in almost any database. Vaughn Vernon has a blog post about this.

enter image description here

But the nicest are the Read models. You can create a read model for each use case. Being a model used only for read/query, it can be as simple/dump as possible. The read entities contain only query related behavior. With the right persistence they can be persisted as they are. For example, if you use MongoDB (or any document database), with a simple reflection based serializer you can have a very thin architecture. Thanks to the domain events, you won't need to use JOINS, you can have full data denormalization (the read entities include all the data they need).

The second leap is Event sourcing. With this you don't need a flat persistence for the Aggregates. They are rehydrated from the Event store each time they handle a command.

You still have DTOs (commands, events, read models) but there is only one DTO per entity/concept.

Regarding the elimination of DTOs used by the Presentation: you can use something like GraphSQL.

All the above can be made worse by the programming language and framework. Strong typed programming languages force you to create a type for each custom returned value. Some frameworks force you to return a custom serializable type in order to return them to REST over HTTP requests (in this way you could have self-described REST endpoints using reflection). In PHP you can simply use arrays with string keys as value to be returned by a REST controller.

P.S.

  • By DTO I mean a class with data and no behavior.
  • I'm not saying that we all should use CQRS, just that you should know that it exists.
like image 74
Constantin Galbenu Avatar answered Dec 08 '25 16:12

Constantin Galbenu



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!