Implementing DDD with spring
Abstract
- Understand the domain: talk to the people.
- Split big domain into subdomains.
- Develop an ubiquitous language: close the gap between the domain experts and the code.
- Develop a domain model.
- Separate domain model from implementation details.
Domain model encapsulates:
- domain knowledge,
- domain rules,
- processes,
- constraints,
- behaviors,
- state changes.
Tactical design:
- Entities
- Value Objects
- Domain Services
- Factories
- Aggregates
- Repositories
There’s no one way to do it with DDD, but most often implement a cocentric architecture, so either onion architecture, hexagonal architecture or clean architecture.
In “traditional” architectures with technical layers, we most often have services classes that perform the business logic, whereas the entities are only plain java objects. They are called anemic objects. It’s fine for small projects, but once it’s becoming big, it’s hard to maintain because the responsibilities of the services are not clear.
Demo:
- The speaker is using spring-modulith in his demo.
- He put the
Repository
in the packagedomain
. Interesting.- One way to keep the domain entities pure (i.e. no JPA annotations) will be to:
- create a
BookEntity
containing those JPA annotations in the infrastructure package- have an implementation of the repository and map the domain entity to infrastructure entity and vice versa
- However, that’s not the approach the speaker is leaning for. Instead:
- Add the JPA annotations in the domain entity, e.g.
@EmbeddedId
instead of@Id
for the id class@Embedded
for the value objects
- However, we have to add a default constructor with no parameter (we can put it in package private), because hibernate needs it. It’s the violation that we need to accept if we want to adopt the JPA annotations.
- The speaker suggests using the concept of use cases in clean architecture, i.e. use specific classes to perform business logic instead of a global service object (e.g.
RegisterBook
instead ofBookService
).- Add a
Repository
when creating a new domain entity where we can perform some checks to validate the state of the model entity, e.g.:
- Using spring-modulith, we can add a test to check if there’s any violation between the packages (i.e. no hard coupling between two packages).
- If there’s a call between one package to another, the speaker suggests extends the domain entity with
AbstractAggregateRoot<>
(a spring class) where we can register events that will be triggered on the entity update.
- However, this will introduce eventual consistency as it’s not the same transaction.
- The other package (here
catalog
) still has some coupling on the original package (herelending
) because the former has references to the domain entitiesLoanClosed
andLoanCreated
… Not sure how spring-modulith handles this “violation”.- This event is not published immediately after the call to
registerEvent(new LoanClosed(copyId))
, but after therepository.save(loan)
.- Then, in the other side, in the application layer, we will have a
DomainEventListener
that will listen to those events:Repository: GitHub - maciejwalkowiak/implementing-ddd-with-spring-talk Slides: Implementing Domain Driven Design with Spring - Speaker Deck