Domain Driven Design is known since many years. But unfortunately there are many systems that don’t consider that approaches.
Many software services are built on a technical architecture. One example is a classical three-tier architecture. This architecture-style contains services, entities and data repositories (or data access layers). But mostly the business architecture (or name it domain architecture) is missing or not considered.
Let’s talk about a simple order usecase: when a customer take a order, the OrderServices store the order. The OrderEntity contain many articles as OrderLines. This entity is referred to the CustomerEntity.
As shown in this example, there are many references between services and entities. Even under the consideration of some design aspects like avoiding cyclomatic references it’s hard to find a clear domain architecture. If article, order, customer and adress are considered as domain modules, they are not well encapsulated.
In a more domain driven way it may look like the following example:
There are three aggregates: ArticleAggregate, OrderAggregate and CustomerAggregate. The entities contain data and behaviour. Each aggregat is treated like a unit of work. That means their entities get persisted together.
The OrderAggregat contains a OrderlineEntity. It’s very important that the aggregates share nothing between eachother. This affects also the database design. For example the CustomerAggregate contains payment data and adress data. It may be one solution to treat them in the 3rd normal form in the way one adress can be referenced to many customers. But in this example, the adress is part of the customer aggregate. If two customers would have the same adress, then the adress informations would be duplicated in each customer’s aggregate.
This approach has the benefit of having a clear business architecture with clear modules. Each module encapsulates its data and behavior. It’s possible to change one aggregate without having side effects to other aggregates.
Another benefit is when a (micro-) service may become too big it’s easy to divide them into multiple services (-> it means one bounded context would be splitted into different bounded contextes). It’s really easy to do that because the aggregate should not have dependencies between each other. This may be also a good tactic for refactoring bigger monoliths.
As everything in software engineering you don’t get anything for free and also this approach has some disadvantages. One bigger disadvantage to be aware is that on the first approach with less encapsulation it’s possible to make individual database joins between entities and query them together. This is often useful when there is a need for bigger viewmodels that contains data across a bigger part of the domain. On the Domain Driven Design approach this is not possible via the database level since the aggregates should not be coupled. With modern architectures you may load multiple aggregates in parallel and join then them together to a viewmodel inside the heap. For this problems there exists also further design patterns like using a CQRS approach when needed.