Search code examples
domain-driven-designclean-architecture

Clean Architecture And DDD Rich Model Validations


I am new to clean architecture and DDD, watched some courses about them and currently reading DDD the book by Eric Evans, I have read some discussions about where to put the validations in the domain layer or in the application layer (within the commands) and I feel that for many cases it's better to implement it in domain layer however when I think about the update and create use cases, for example in create command maybe a field is required to be sent however in update command maybe it's optional to be sent, so how this could be achieved in the domain layer, or does it better to implement in application layer?


Solution

  • The thing is that in Domain-Driven Design you usually don't have CRUD like (create, read, update, delete) use cases because it is better suited for task-based user interfaces. If you happen to only have such simple cases without complex logic that's fine, but applying patterns of DDD like domain model, aggregates, repositories, etc. might be an overkill.

    If your project's complexity justifies investing in DDD (which is a long-term investment) you will have to think of the real business use cases. And your interactions with your domain layer will follow the domain language.

    Coming back to your question about different parameters for create and update use cases:

    With complex business transactions you will not just have one command to create an entity (aggregate) and another to just update it. There should be very specific operations for interactions and these will be defined through the API of your aggregates and domain services.

    Imagine you have an online shop application and as a user you can perform some specific actions with your shopping basket. For instance, you can add a new item to the basket. When you do that the application will send a REST request to your backend to change the description. There is also some validation logic involved like how many items can be in a basket at max or making sure that adding an item several times would not increase the number of items but rather the quantity of the existing item in the basket.

    In another step the user decreases the quantity of an item, again some business logic is involved like removing the item alltogether if it's set to zero with that action.

    If you would talk to business people they would probably call these actions something like "add an item to the basket" or "change the quantity of an item".

    If you follow the aggregate pattern you would operate the API provided by the Basket aggregate, providing methods such as AddItem(OrderItem item) and ChangeItemQuantity(ItemId itemId, int newQuantity).

    If you just perform CRUD like operations it would just be something like "UpdateShoppingBasket(BasketUpdateData data)" which is very expressive. Or even worse, you could let your application layer (or even worse a controller) decide what fields of the basket aggregate need to be changed. The invariants of a valid shopping basket would not be explicitly modelled in your code and also allow for the trap of duplicating such logic in several places.

    TL;DR;

    DDD is more suited for task based user interactions rather the CRUD like ones. Each specific action should follow the native language of the domain, performing everything like an update of the same object would not allow for explicitly modelling the respective use cases and also have a bad effect on readability, maintainabilty and testability.

    Where the complexity of the project justifies applying DDD the domain layer should be modelled to reflect the business language and encapsulate the respective logic and thus provide an API that allows to call the respective operations. So the application layer would translate the required action to the form understood by the domain layer and just consume the API provided by the domain layer. This also follows the so called Tell, Don't Ask! principle.