简体   繁体   English

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

[英]Domain Driven Design - Domain model vs Hibernate Entity

Are Hibernate Entity is same as the domain models? Hibernate 实体是否与域模型相同?

See the following example.请参阅以下示例。

Method 1 - Domain model and Entity are same class.方法 1 - 域模型和实体是同一个类。 Domain model "is-an" entity域模型“是一个”实体

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

Method 2 - Domain and Entity are different functions.方法 2 - 域和实体是不同的功能。 Domain model "has-an" entity域模型“有一个”实体

class Agent
{
    // Hibernate entity for this domain model
    private AgentEntity agentEntity;

    // Getters and setters to set the agentEntity attributes

    // Business logic
}

From the above 2 methods, which of them are the correct way to implement DDD?从以上2种方法中,哪一种是实现DDD的正确方法? I believe method 2 is the right way because you are essentially controlling the access to a sensitive object and the enclosing object (Domain model) has all the business logic/operations on the domain model.我相信方法 2 是正确的方法,因为您实际上是在控制对敏感对象的访问,并且封闭对象(域模型)具有域模型上的所有业务逻辑/操作。 But my workplace colleagues suggests that they are essentially the same.但我的同事们认为它们本质上是一样的。 And according to them the purpose of Hibernate Entity is to represent the domain model in a given system.根据他们的说法,Hibernate Entity 的目的是表示给定系统中的域模型。 Modelling the entity as the domain model actually makes the design simpler.将实体建模为域模型实际上使设计更简单。 This is because the repository takes an Entity to execute CRUD operations.这是因为存储库需要一个实体来执行 CRUD 操作。 So if model "has-an" entity, then the repository must be dependency injected into the domain model to save the entity.因此,如果模型“具有”实体,那么存储库必须依赖注入到域模型中以保存实体。 This will make the design unnecessarily complicated.这将使设计不必要地复杂化。

Since you have mentioned a technology in this case Hibernate, that means that you are talking about an implementation .由于您在本例中提到了 Hibernate 技术,这意味着您在谈论implementation Domain Driven Design is about both the abstract eg the Model and it's Implementation .领域驱动设计是关于抽象的,例如模型和它的实现

Models can be implemented in different ways.模型可以以不同的方式实现。 In your example you have shown two different implementations that represent the same Model .在您的示例中,您展示了代表相同Model 的两个不同实现。

This article talks about the problem that you are facing.这篇文章讨论了您面临的问题。

You asked if the Domain Model is the same as a Hibernate Entity .您询问Domain Model是否与Hibernate Entity相同。 The answer is NO .答案是否定的

Hibernate Entity is a technology specific thing, in this case it's an object that is part of an ORM framework. Hibernate Entity是一个特定于技术的东西,在这种情况下,它是一个对象,它是 ORM 框架的一部分。 Hibernate Entity and DDD Entity as defined by DDD are different things as the DDD Entity is an abstract thing, if defines an idea (a pattern) and gives guidelines of what this idea is and what it represents. DDD定义的Hibernate EntityDDD Entity是不同的东西,因为DDD Entity是一个抽象的东西,如果定义了一个想法(一种模式)并给出了这个想法是什么以及它代表什么的指导方针。 Hibernate Entity is a Java object that is instantiated, tracked, persisted, discarded and garbage collected. Hibernate Entity是一个 Java 对象,它被实例化、跟踪、持久化、丢弃和垃圾收集。

People just use the same term for different things and this can lead to confusion (can't blame them, naming things is one of the two hard problems in software).人们只是对不同的事物使用相同的术语,这会导致混淆(不能责怪他们,命名事物是软件中的两个难题之一)。

You use Hibernaty Entities or any other type of technology specific thing like Entity Framework Entity (that is the same thing, an object in a OO program) to implement a Domain Model .您可以使用Hibernaty 实体或任何其他类型的技术特定事物,如实体框架实体(即同一事物,OO 程序中的对象)来实现域模型 The same Domain Model can be implemented in different languages using different technologies.相同的领域模型可以使用不同的技术以不同的语言实现。 These implementations will vary based on what the technology provides.这些实现将根据技术提供的内容而有所不同。

For example if you are writing a NodeJs backend with a MongoDB and you want to use an ORM to implement a Domain Model you will be stuck with using an Active Record pattern (probably Mongoose) because these are the only ones that people have implemented (at least I couldn't find any other frameworks that are not Active Record, if you find any please let me know).例如,如果您正在使用 MongoDB 编写 NodeJs 后端,并且您想使用 ORM 来实现域模型,那么您将不得不使用Active Record 模式(可能是 Mongoose),因为这些是人们唯一实现的(在至少我找不到任何不是 Active Record 的框架,如果你找到任何请告诉我)。 Implementing DDD in such a way can be very tricky (and can really suck).以这种方式实现 DDD 可能非常棘手(而且真的很糟糕)。

In the DDD book Eric Evans talks about how technology can help you implement a Model or can fight you all the way.DDD 一书中, Eric Evans 谈到了技术如何帮助您实现模型,或者如何与您抗衡。 When it fights you or doesn't provider good mechanisms you just how to work around that.当它与您作斗争或没有提供良好的机制时,您就知道如何解决这个问题。

Sometimes ORMs have requirements and you don't want to expose these things to your other code, so you can use a Wrapper like in your Method 2 .有时 ORM 有要求,您不想将这些东西暴露给您的其他代码,因此您可以使用像方法 2 中的 Wrapper 。 Some of them include things like public get set method, public constructors etc. Most of them use reflection and can have private stuff but still there are many issues like having a private constructor without parameter to satisfy the framework and your code get's messy with a lot of stuff that are not related to your model but are there because your framework needs them (YUCK!).其中一些包括公共 get set 方法、公共构造函数等。它们中的大多数使用反射并且可以拥有私有的东西,但仍然存在许多问题,例如有一个没有参数的私有构造函数来满足框架,并且您的代码变得混乱与您的模型无关但因为您的框架需要它们而存在的东西(YUCK!)。 This can lead to bugs too.这也可能导致错误。 It's easier to make a mistake by having default constructor instead of having nice constructors with parameters or static factory methods.使用默认构造函数而不是使用带有参数或静态工厂方法的漂亮构造函数更容易出错。 This wrapper can represent a more purer domain model without having the necessary evil that frameworks carry so you can use them.这个包装器可以代表一个更纯粹的领域模型,而没有框架所携带的必要的邪恶,所以你可以使用它们。

In one project this got so ugly that we decided to go with raw SQL in Repositories so we don't have to deal with all the stuff of the framework.在一个项目中,这变得非常丑陋,以至于我们决定在Repositories 中使用原始 SQL,这样我们就不必处​​理框架的所有内容。 The implementation was nice, pure and we did it faster.实现很好,很纯粹,而且我们做得更快。 Some people think that a framework speeds things up and it's true most of the time, but when the framework fights you and the code is buggy, debugging is not fun, so writing a raw SQL can be a breeze.有些人认为框架可以加快速度,这在大多数情况下是正确的,但是当框架与您抗争并且代码有缺陷时,调试就不那么有趣了,因此编写原始 SQL 可能是轻而易举的事。 In this case Following the guidelines of DDD by using aggregates our model was nicely decoupled and we didn't have complex queries that can make the development slower.在这种情况下,通过使用聚合遵循 DDD 的指导方针,我们的模型很好地解耦,并且我们没有复杂的查询,这会使开发变慢。

Are Hibernate Entity is same as the domain models? Hibernate 实体是否与域模型相同?

Not really, no.不是真的,不是。 In practice the line between them can be very blurry.在实践中,它们之间的界限可能非常模糊。

One of the claims of domain driven design is that you can separate persistence concerns from your domain model.域驱动设计的主张之一是您可以将持久性问题与域模型分开。 The domain model holds in memory representations of the current state of some business, and the domain rules that govern how that business state changes over time.域模型在内存中保存某些业务当前状态的表示,以及控制业务状态如何随时间变化的域规则。

The repository acts as a sort of boundary, between the parts of your application that think that domain entities are all stored in local memory somewhere, and the parts of the code that know about non-volatile storage of the data.存储库充当一种边界,介于您认为域实体都存储在本地内存中某处的应用程序部分与了解数据非易失性存储的代码部分之间。

In other words, the repository is (in a sense) two functions;换句话说,存储库(在某种意义上)是两个功能; one that knows how to get data out of an "aggregate" and store, another that knows how to read data out of a store and build an aggregate from it.一个知道如何从“聚合”和存储中获取数据,另一个知道如何从存储中读取数据并从中构建聚合。

An ORM is one way to get data from an external relational database into local memory. ORM 是一种将数据从外部关系数据库获取到本地内存的方法。

So your load story might look like所以你的负载故事可能看起来像

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

And store might look like商店可能看起来像

Copy data from the aggregate into a hibernate entity
Save the hibernate entity.

In practice, this is kind of a pain.在实践中,这是一种痛苦。 The ORM representation often has to worry about things like surrogate keys, tracking which data elements are dirty so that it can optimize writes, and so on. ORM 表示通常需要担心诸如代理键、跟踪哪些数据元素是脏的以便优化写入等事情。

So what you will often see instead is that the domain logic ends up being written into the ORM entities, and you throw in a bunch of comments to make it clear which bits are present because they are required by hibernate.因此,您经常会看到,域逻辑最终会被写入 ORM 实体,并且您会添加一堆注释以明确哪些位是存在的,因为它们是 hibernate 所必需的。

If you look at the DDD Cargo shipping example, you'll see that they took this second approach, where the aggregate has a little bit of hibernate support hidden at the bottom.如果您查看DDD Cargo 运输示例,您会发现他们采用了第二种方法,其中聚合在底部隐藏了一点休眠支持

Domain and Entity are different functions.域和实体是不同的功能。 Domain model "has-an" entity域模型“有一个”实体

Your colleagues are right: these are equivalent in most important aspects.你的同事是对的:他们在最重要的方面是等效的。 The domain model depends on your hibernate entities.域模型取决于您的休眠实体。

Neither of them match what Evans described in his book.它们都不符合埃文斯在他的书中描述的内容。

Both of them look like what a lot of teams have done in practice .他们两个看起来都像很多团队在实践中所做的那样 Putting the domain logic directly into the hibernate entity is, as best I can tell, the common approach.就我所知,将域逻辑直接放入休眠实体是常用的方法。

If you were really separating the two, then your repository would look something like如果您真的将两者分开,那么您的存储库看起来像

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

If you look carefully, you'll see that the domain model is independent of the hibernate model, but the repository depends on both.如果仔细观察,您会发现域模型独立于休眠模型,但存储库依赖于两者。 If you need to change your persistence strategy, the domain model is unchanged.如果你需要改变你的持久化策略,领域模型是不变的。

Is the extra degree of separation worth the hassle?额外的分离度值得麻烦吗? It depends.这取决于。 There is strong cognitive dissonance between the object oriented patterns used to describe domain models, and stateless execution environments.用于描述领域模型的面向对象模式与无状态执行环境之间存在强烈的认知失调。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM