使用 JPA 的域对象中的注释违反了数据库是一个细节

Annotations in domain objects with JPA violates Database is a detail

您如何看待持久性模型和领域模型的分离?我读过,您不必将持久性问题与您的业务问题混为一谈(DDD、Clean Architecture、MartinFowler、Eric Evans 等等)。即便如此,我仍然在所有项目中看到领域模型直接用ORM注解这样,作为领域模型耦合持久化机制达到贫血模型并违反其他原则。

//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
} 

因此,如果您想遵循干净的原则,您需要拆分域模型和持久性模型(如下所示)以使域模型具有业务行为(这避免贫血模型并遵循 SRP),因此您需要将域模型映射到持久性模型(使用 mapToEntity(DomainModel DM)mapToDomain(PersistenceModel PM) 等典型方法可能在 mapper/tranformer 可能在存储库 class) 中,当你想与数据存储交互时,反之亦然,当你想从数据库中检索数据时。

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

那么 discussion/question 是否从域模型中拆分了持久性模型?什么时候分,什么时候不分?在大多数项目中,更不用说在我见过的所有项目中,我已经看到他们在“专家总是叫卖”DataStore 是一个细节时,在领域模型中耦合了持久性模型的注释。

非常感谢。

请查看这个非常类似的问题Are persistence annotations in domain objects a bad practice?

我认为作为工程师我们应该务实。任何最佳实践、原则或“专家建议”都应该有所帮助。他们不应该让事情变得更糟。所以我建议将它们作为指导,而不是严格的规则。例如,我通常同意“数据库是一个细节”。但我们很少更改该细节。

另一方面,注释不执行任何代码。而且耦合还不错。你的域对象可以同时是一个JPA实体,而且会很干净,很有用。顺便说一下,这 并没有违反单一职责原则 (SPR)。如果您认为可以,请查看 SOLID explanation by its author Uncle Bob

是的,这些注释是细节,应该远离干净架构的实体。

不要混淆干净架构中的名称实体和持久性框架中的 @Entity 注释。它们是不同的东西。

Bob 叔叔 a video 关于干净的架构,最后他说得很清楚,因为他说:

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

another video 中,他谈到了依赖注入和这些框架使用的注解。好吧,依赖注入与您要求的持久性无关,但该视频清楚地表明了 Bob 大叔对干净架构的用例或实体层中的框架注释的看法。

并且在 this video 中,他非常清楚地表明实体不应具有持久性细节。是hibernate还是JPA都无所谓