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



您认为将持久性模型和域模型分离如何?我读到,你不必将持久性问题与业务问题混合在一起(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
} 

因此,如果你想遵循干净的原则,你需要将Domain和Persistence Model(如下所示)拆分为具有业务行为的Domain模型(这可以避免贫血模型并遵循SRP),因此你需要将域模型映射到持久性模型(使用典型的方法,如mapToEntity(DomainModel DM)mapToDomain(PersistenceModel PM)mapper/transformer可能在repository类中),反之亦然。

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

所以讨论/问题是从领域模型中分离持久性模型还是不分离持久性模式?什么时候分手?在大多数项目中,更不用说在我看到的所有项目中,我看到它们在"域模型"中对持久性模型进行了注释;大师们一直在叫卖"DataStore是一个细节。

非常感谢。

请查看这个非常类似的问题:域对象中的持久性注释是一种糟糕的做法吗?

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

另一方面,注释不执行任何代码。耦合也没那么糟糕。您的域对象可以同时是JPA实体,它将非常干净和有用。顺便说一句,这并不违反单一责任原则。如果你认为它确实如此,请查看作者Bob 叔叔的SOLID解释


编辑

@RenéLink我不同意:注释是被解释的,执行它们的代码可以与不同的解释器交换,即使是无操作解释器。注释保持不变,不在乎。这是一个声明性元素,仅此而已。

注释是源代码依赖项,这意味着如果注释不在编译类路径上,则无法编译源代码。

注释不是严格的运行时依赖项,这是正确的。如果注释类在类路径上不可用,则注释也不可用。因此,即使类路径中不存在注释,也可以使用使用注释编译的字节码。

因此,与普通类、接口等相比,注释是一个限制性较小的依赖项

我总是尽量减少代码中的依赖关系。让我们举一个春天的例子。

在spring中,您可以实现InitializingBean,也可以定义一个用@PostConstruct注释的方法——在这种情况下,我将使用post-construct。通常我不需要@PostConstruct,因为我做构造函数注入。但是,如果我将初始化转移到java配置,我可以在返回bean实例之前调用任意的"post-construct"方法,并且我根本不需要@PostConstruct注释。

我同意注释依赖关系比其他类或接口问题更小。但请记住,OP所谈到的注释还有另一个问题。如果将域对象与JPA注释混合使用,则违反了单一责任原则。您的域对象现在有不止一个更改原因(域更改和持久性更改)。

一旦添加@Transient注释,您就会意识到这个问题,或者您可能会因为更改了域逻辑和同事的持久性而导致合并冲突。

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

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

有一段鲍勃叔叔关于清洁建筑的视频,最后他说得很清楚,因为他说:

实体不是数据库表的实例。它们通常是由许多数据库表构造的。

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

在这段视频中,他非常清楚地表明,实体不应该有持久性细节。不管是hibernate还是JPA。

最新更新