首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >领域驱动设计-领域模型与Hibernate实体

领域驱动设计-领域模型与Hibernate实体
EN

Stack Overflow用户
提问于 2019-04-19 03:24:38
回答 2查看 5.4K关注 0票数 16

Hibernate实体是否与域模型相同?

请参阅下面的示例。

方法1-域模型和实体是同一类。域模型是一个实体

代码语言:javascript
复制
@Entity
@Table(name = "agent")
class Agent
{
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    @Column(name = "agent_number", unique = true, nullable = false)
    private String agentNumber;

    @Column(name = "agent_name", nullable = false)
    private String agentName;

    // Busines logic methods
}

方法2-域和实体是不同的功能。域模型有-an实体

代码语言:javascript
复制
class Agent
{
    // Hibernate entity for this domain model
    private AgentEntity agentEntity;

    // Getters and setters to set the agentEntity attributes

    // Business logic
}

从以上两种方法中,哪一种是实现DDD的正确方法?我认为方法2是正确的方法,因为您实际上控制了对敏感对象的访问,而包围对象(域模型)具有域模型上的所有业务逻辑/操作。

但我在职场的同事们认为,他们本质上是一样的。根据它们,Hibernate实体的目的是表示给定系统中的域模型。将实体建模为领域模型实际上使设计变得更简单。这是因为存储库需要一个实体来执行CRUD操作。

因此,如果模型“有-一个”实体,那么存储库必须被注入到域模型中以保存实体。这将使设计不必要地复杂。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2019-04-19 11:43:29

Hibernate实体是否与域模型相同?

不完全是。在实践中,他们之间的界线可能非常模糊。

领域驱动设计的一个要求是,您可以将持久化关注点从域模型中分离出来。域模型包含某些业务当前状态的内存表示,以及控制业务状态随时间变化的域规则。

存储库充当一种边界,它介于应用程序中认为域实体都存储在某个地方的内存中的部分和代码中了解数据非易失性存储的部分之间。

换句话说,存储库有两个功能:一个知道如何从“聚合”中获取数据和存储,另一个知道如何从存储区读取数据并从存储中构建聚合。

ORM是将外部关系数据库中的数据输入本地内存的一种方法。

所以你的负载故事看起来像是

代码语言:javascript
复制
Use an identifier to load data from the database into a hibernate entity
copy the data from the hibernate entity into an aggregate
return the aggregate

商店看起来就像

代码语言:javascript
复制
Copy data from the aggregate into a hibernate entity
Save the hibernate entity.

实际上,这是一种痛苦。ORM表示通常需要担心诸如代理键、跟踪哪些数据元素是脏的以便能够优化写入等等。

因此,您经常会看到的是,域逻辑最终被写入ORM实体中,然后添加一堆注释,以明确哪些位是存在的,因为它们是hibernate所需要的。

如果您查看DDD货运示例,您将看到他们采用了第二种方法,其中聚合在底部隐藏了一个一点hibernate支持

领域和实体是不同的功能。领域模型“具有”实体

你的同事们是对的:它们在最重要的方面是相当的。域模型取决于您的hibernate实体。

两者都不符合埃文斯在他的书中所描述的。

两支球队看起来都像很多球队在实践中所做的那样。将域逻辑直接放入hibernate实体,据我所知,这是一种常见的方法。

如果您真的将这两者分开,那么您的存储库将类似于

代码语言:javascript
复制
Agent AgentRepository::find(id) {
    AgentEntity e = entityManager.find(id)
    Agent a = domainFactory.create( /* args extracted from e */ )
    return a
}

void AgentRepository::store(Agent a)
    AgentEntity e = entityManager.find(id)
    copy(a, e)
}

// I think this is equivalent
void AgentRepository::store(Agent a)
    AgentEntity e = entityManager.find(id)
    entityManager.detach(e)
    copy(a, e)
    entityManager.merge(e)
}

如果仔细观察,您将看到域模型独立于hibernate模型,但是存储库依赖于两者。如果需要更改持久化策略,域模型将保持不变。

额外的分离程度值得麻烦吗?那得看情况。用于描述领域模型的面向对象模式与无状态执行环境之间存在很强的认知不一致性。

票数 19
EN

Stack Overflow用户

发布于 2019-04-19 07:23:59

由于您在本例中提到了一种技术-- Hibernate,这意味着您要讨论的是一个实现。域驱动设计是关于抽象的,例如模型和它的

模型可以以不同的方式实现。在您的示例中,您展示了两个不同的实现,它们表示相同的模型

文章讨论您所面临的问题。

您询问了域模型是否与Hibernate实体相同。答案是否定的。

Hibernate实体是一种特定于技术的东西,在这种情况下,它是ORM框架的一部分。DDD定义的Hibernate实体DDD实体是不同的东西,因为DDD实体是一个抽象的事物,如果定义了一个概念(一个模式),并且给出了这个想法是什么以及它代表什么的指导方针。Hibernate实体是一个实例化、跟踪、持久化、丢弃和垃圾收集的Java对象。

人们只是对不同的事物使用相同的术语,这可能导致混淆(不能怪他们,命名东西是软件中两个难题之一)。

您可以使用Hibernaty实体或任何其他类型的特定于技术的东西,比如实体框架实体(也就是OO程序中的对象)来实现领域模型。相同的域模型可以使用不同的技术在不同的语言中实现。这些实现将根据技术提供的内容而有所不同。

例如,如果您正在用MongoDB编写NodeJs后端,并且希望使用ORM来实现域模型,那么您将不得不使用活动记录模式 (可能是Mongoose),因为这些是人们实现的唯一框架(如果您找到任何其他框架,请告诉我,至少我找不到其他没有活动记录的框架)。以这种方式实现DDD可能非常棘手(而且可能非常糟糕)。

DDD书中,Evans谈到了技术如何帮助您实现模型或如何与您一路战斗。当它对抗你或没有提供好的机制,你只是如何解决这个问题。

有时候ORM有需求,而且您不希望将这些东西公开给其他代码,所以您可以使用类似于方法2的包装器。其中有些包括诸如public get方法、公共构造函数等。它们大多使用反射,可以有私有的东西,但是仍然存在很多问题,比如有一个没有参数的私有构造函数来满足框架,而且您的代码会被许多与模型无关的东西弄得一团糟,但它们是因为您的框架需要它们而存在的(讨厌!)这也会导致错误。使用默认构造函数而不是具有参数或静态工厂方法的构造函数更容易出错。这个包装器可以表示一个更纯的域模型,而不需要框架所携带的必需的邪恶,这样您就可以使用它们。

在一个项目中,这个问题变得非常糟糕,以至于我们决定在Repositories中使用原始SQL,因此我们不必处理框架的所有内容。这个实现很好,很纯,而且我们做得更快。有些人认为框架加快了速度,而且大多数情况下都是这样,但是当框架与您发生冲突,而且代码是错误的时候,调试就不好玩了,所以编写一个原始的SQL是件轻而易举的事。在这种情况下,遵循DDD的准则,通过使用聚合,我们的模型是很好地解耦的,并且我们没有复杂的查询可以使开发更慢。

票数 20
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/55756194

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档