ORM Entities vs DDD Entities

Viewed 1148

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?

2 Answers

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);
    }
}

About your quote:

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.

I'm not an export on this topic, but as far as I know, JPA doesn't mandate public getters and setters, although documentation can be vague sometimes.

For example, the Hibernate 5.6 doc says:

2.5.4. Declare getters and setters for persistent attributes

The JPA specification requires this, otherwise, the model would prevent accessing the entity persistent state fields directly from outside the entity itself.

With "requirement quoted above", they probably mean this:

The persistent state of an entity is represented by instance variables, which may correspond to JavaBean-style properties. An instance variable must be directly accessed only from within the methods of the entity by the entity instance itself. The state of the entity is available to clients only through the entity’s accessor methods (getter/setter methods) or other business methods.

These texts aren't very clear whether setters and setters are always required, even though the word "requires" is used. What I think what it basically says (or should say) is that if your clients wants to read and write to an attribute then you should use public getters and setters respectively. (With "your client classes", I mean a class that you have written, as opposed to Hibernate itself as a client of the entity.) But if your clients don't need to read or write to it, then you don't need accessor methods at all, as long as you use JPA field-access attribute annotations (as opposed to JPA method/property-access attribute annotations). Instead of setters, you could also use constructor parameters for non-null attributes. (I have plenty of such cases in my Hibernate code.) Although the JPA/Hibernate doc may have been written originally with a more anemic entity classes in mind, but you aren't required to have such anemic accessors.

About your quote:

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?

Making two distinct models would create a lot of boiler plate code.

The JPA spec says:

In addition to returning and setting the persistent state of the instance, property accessor methods may contain other business logic as well, for example, to perform validation. The persistence provider runtime executes this logic when property-based access is used.

So having business logic in entity classes, should not be a problem. In the ideal situation, entity classes are pure DDD domain classes, in which the ORM applied to it, is only limited to meta data (for example annotations), and the code it self is ORM agnostic.

But having a lot of ORM meta data mixed with a lot of business logic, can hurt readability. A solution to this could be extension methods in Kotlin (a Java based language). A Kotlin extension method is basically a static method in Java, that can be used as an instance method in Kotlin, that lives externally from the class to which it is logically added to.

For example, you could have the following entity class: com.stackoverflow.domain.Question.

And for whatever reason, you would like to add a persist() method to it, but you don't want to have it in the Question class because of separation of concerns, than you could accomplish this with extension method:

In the file com.stackoverflow.persistence.QuestionExt.kt:

package com.stackoverflow.persistence
import com.stackoverflow.domain.Question

fun Question.persist() {
    // read "id" property of question object
    val id = this.id
    // persist the question
}

Then client code can do this:

package com.stackoverflow.persistence.repositories

class QuestionRepository {
    fun save(question: Question) {
        question.persist()
    }
}
Related