Annotations in domain objects with JPA violates Database is a detail
Asked Answered
J

2

6

What do you think about have persistence models and domain models separated? I've read that you don't have to mix persistence concerns with your business concerns (DDD, Clean Architecture,MartinFowler, Eric Evans and a lot and a lot of much more). Even so, I still see in all projects domain models annotated directly with ORM annotations like that, being the domain model coupled to the persistence mechanism reaching an anemic model and violating other principles.

//PersistenceJPA entity
@Entity
@Table(name="training_cycle")
class TrainingCycle {
    @Id
    private Long id;
    private String name;

    @Column(name="discipline_id")
    @JoinColumn(name="discipline_id")
    private Long disciplineId; //Referencing by Id because Discipline is another Aggregate ROOT

    //EmptyConstructor, getters and setters avoided
}

//PersistenceJPA entity
@Entity
@Table(name="training_cycle")
class Discipline {
    @Id
    private Long disciplineId;
    private String name;
    //EmptyConstructor, getters and setters avoided
} 

So if you want to follow clean principles you need to split Domain and Persistence Model(as shown below)to have Domain model with business behavior(this avoid anemic model and follows the SRP) and because of that you need to map the domain model to the persistence model (with typical methods like mapToEntity(DomainModel DM) and mapToDomain(PersistenceModel PM) maybe in a mapper/tranformer maybe in the repository class) when you want to interact with Datastore and viceversa when you want to retrieve data from database.

class Discipline {
    private DisciplineId disciplineId;
    private String name;

    public Discipline(DisciplineId disciplineId, String name) {
        this.disciplineId = disciplineId;
        this.name = name
    }
}

public class TrainingCycle{
    private TrainingCycleId trainingCycleId;
    private String name;
    private DisciplineId disciplineId;

    public TrainingCycle(TrainingCyleId trainingCycleId, String name, DisciplineId disciplineId) {
        this.trainingCycleId = trainingCycleId;
        this.name = name;
        assignDiscipline(disciplineId);
    }

    public void assignDiscipline(DisciplineId aDisicplineId) {
        if(aDisicplineId == null) {
            throw new IllegalArgumenException("Discipline cannot be null")
        }
        this.disciplineId = aDisicplineId;
    }
}


@Entity
@Table(name="training_cycle")
class TrainingCycleJpa {
    @Id
    private Long id;
    private String name;

    @Column(name="discipline_id")
    @JoinColumn(name="discipline_id")
    private Long disciplineId; //Referencing by Id because Discipline is another Aggregate ROOT

    //EmptyConstructor, getters and setters avoided
}


@Entity
@Table(name="training_cycle")
class DisciplineJpa {

    @Id
    private Long disciplineId;
    private String name;
   //EmptyConstructor, getters and setters avoided
}

class TrainingCyleJpaRepository implements TrainigCycleRepository {

    public void create(TrainingCycle trainingCycle) {
        entityManager.persist(this.mapToEntity(trainingCycle)
    }

    public TrainingCycle create(TrainingCycleId trainingCycleId) {
        return this.mapToDomain(entityManager.find(TrainingCycleId));
    }
}

So the discussion/question is Split or not Split Persistence Model from Domain Model? When to split or when not? In the most projects, not to say in all the ones I've seen as an example, I've seen they couple annotations of persistence model in Domain Model when "gurus are allways hawking" DataStore is a detail.

Thanks a lot.

Jealous answered 13/8, 2020 at 10:42 Comment(0)
I
2

Yes, these annotations are details and should be kept away from the entities of the clean architecture.

Don't be confused about the name entity in the clean architecture and the @Entity annotation from your persistence framework. They are different things.

There is a video of Uncle Bob about clean architecture and at the end he makes it really clear, because he says:

the entities are not instances of database tables. They are usually constructions from many database tables.

In another video he talks about dependency injection and the annotations that these frameworks use. Well, dependency injection has nothing to do with persistence that you asked for, but this video makes clear what Uncle Bob thinks about framework annotations in the use case or entities layer of the clean architechture.

And in this video he makes it really clear that the entities should not have persistence details. It doesn't matter if it is hibernate or JPA.

Intrusion answered 26/8, 2020 at 11:32 Comment(0)
U
3

Please check out this very similar question: Are persistence annotations in domain objects a bad practice?

I think that as engineers we should be pragmatic. Any best practices, principles, or "guru suggestions" should help. They should not make things worse. So I suggest to treat them as guidance, not strict rules. For instance, I generally agree that "database is a detail". But we rarely change that detail.

On the other hand, annotations do not execute any code. And the coupling is not so bad. Your domain object can be a JPA entity at the same time, and it will be very clean and useful. By the way, this does not violate the Single Responsibility Principle (SPR). If you thought it does, check out SOLID explanation by its author Uncle Bob


EDIT

@RenéLink I don't agree: Annotations are interpreted and the code that executes them can be exchanged with a different interpreter even with a no-op interpreter. The annotation stays untouched and doesn't care. It's a declarative element, nothing more.

Annotations are source code dependencies, which means that the source code can not be compiled if the annotation is not present on the compile classpath.

It's right that annotations are not hard runtime dependencies. If the annotation class is not available on the classpath the annotation is also not available. Thus you can use bytecode that was compiled with annotations even if the annotations are not present on the classpath.

So annotations are a less restrictive dependency then normal classes, interfaces and so on.

I always try to minimize the dependencies in my code. Let's take a spring example.

In spring you can either implement InitializingBean or you can define a method annotated with @PostConstruct in this case I would use post construct. Often I don't need a @PostConstruct, because I do constructor injection. But if I move the initialization to a java config I can just call an arbitrary 'post construct' method befor returning the bean instance and I don't need a @PostConstruct annotation at all.

I agree that annotation dependencies are less problematic than other classes or interfaces. But keep in mind that the annotations the OP talks about also have another problem. If you mix domain objects with JPA annotations you violate the single responsibility principle. Your domain objects now have more then one reason to change (domain changes and persistence changes).

You will recognize the problem as soon as you add @Transient annotations or you might get merge conflicts because you changed domain logic and a colleague persistence things.

Upturned answered 13/8, 2020 at 11:3 Comment(2)
annotations do not execute any code, but there is code that scans for annotations and executes other code. So effectively annotations execute code.Renegado
@RenéLink I don't agree: Annotations are interpreted and the code that executes them can be exchanged with a different interpreter even with a no-op interpreter. The annotation stays untouched and doesn't care. It's a declarative element, nothing more.Nightwalker
I
2

Yes, these annotations are details and should be kept away from the entities of the clean architecture.

Don't be confused about the name entity in the clean architecture and the @Entity annotation from your persistence framework. They are different things.

There is a video of Uncle Bob about clean architecture and at the end he makes it really clear, because he says:

the entities are not instances of database tables. They are usually constructions from many database tables.

In another video he talks about dependency injection and the annotations that these frameworks use. Well, dependency injection has nothing to do with persistence that you asked for, but this video makes clear what Uncle Bob thinks about framework annotations in the use case or entities layer of the clean architechture.

And in this video he makes it really clear that the entities should not have persistence details. It doesn't matter if it is hibernate or JPA.

Intrusion answered 26/8, 2020 at 11:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.