Issue
I'm familiar with typical layered architecture consisting of services, entities and repositories. Services manipulate annotated entity classes which are being persisted by repositories. In this model entity classes are just anemic data containers with bunch of getters and setters. The business logic resides in the procedural service classes (singletons managed by the spring container).
I'm learning DDD as a hobby project. In DDD entities forms a rich domain model which accommodates majority of the business logic at the aggregate root methods (and value objects). Services are almost mere coordinators of collaborating entities, repositories and other services. Rich domain model enforces business constraints and invariants the true OOP way and promotes the code maintainability. Also, the domain model is the core of the hexagonal architecture, meaning it's just POJOs not depending on technical or framework concerns at the source code level.
But JPA specification mandates the entity bean should have public getters and setters, inherently being an anemic data container, antithesis to the DDD domain model. So should I bundle domain logic inside the JPA entity? Or should I maintain two distinct models and the mapping logic when working with ORM on DDD? Where should these model and the mapping logic live at the project level?
Solution
To maintain DDD and especially decouple your domain model from the database layer (as both may change individually), you need two distinct models.
Then, you need some kind of repository service, which knows (i.e. depends on) your domain model, and can do some kind of two-way mapping. In practice, even if this is against the pure DDD lore, you probably need some kind of assistence in your domain model (i.e. make a dump of internal structures known and restore the state from such a dump.) But it is really hard to store and restore a real black box in a persistent store unless you want to go for simple object serialization.
Regarding your question in the comment:
That's exactly the problem: you either mix your infrastructure into the domain or expose internal data. Somehow every author writing about DDD simply dodged this bullet by just not talking about this problem. Both variants are equally ugly, but as you seem to attempt a rather pure DDD approach, I'd create a DTO object coupled to the domain object, which is accessible by the infrastructure layer (e.g. by using package protected access). However, I would not grant access to the real internal values. This way you limit your "corruption" to a single point and you are free to change the implementation details as you like.
Putting some pseudo code to the answer:
public class Invoice { // Domain class
// implementation details. @Rest of the world: none of your business!
private Person creditor; // other domain objects
private MonetaryAmount amount; // and value objects
// Corruption starts here
toInvoiceDto() {
InvoiceDto res = new InvoiceDto();
res.setCreditorId(creditor.getId()); // mapping into external representation
...
return res;
}
static Invoice fromInvoiceDto(InvoiceDto persistentSource) {
...
}
// Corruption ends here
// do real business :^)
}
public class InvoiceDto {
...
}
public class Repository {
public void saveInvoice(Invoice businessObject) {
// *very* roughly
InvoiceDto dto = businessObject.toInvoiceDto();
InvoiceEntity entity = someKindOfMapper.toEntity(dto);
entityManager.save(entity);
}
}
Answered By - mtj
Answer Checked By - Katrina (JavaFixing Volunteer)