Evans 分类
2005 年 12 月 14 日
在他的优秀著作 领域驱动设计 中,Eric Evans 创建了一个分类,用于区分您可能遇到的不同类型的领域对象。
- 实体:具有独特身份的对象,该身份贯穿时间和不同的表示形式。您也可能听到它们被称为“引用对象”。
- 值对象:仅作为其属性组合起作用的对象。两个值对象,如果所有属性的值都相同,则被认为相等。我在 值对象 和 P of EAA 中也有描述。
- 服务:域上下文中的独立操作。服务对象将一个或多个服务收集到一个对象中。通常,在您的执行上下文中,每个服务对象类型只有一个实例。
这种分类与我对领域模型中所需内容的经验非常吻合。问题在于这三者很难精确定义,它们属于“我看到它们时就认识它们”的类别。
因此,一些示例可能会有所帮助。实体通常是大型事物,例如客户、船舶、租赁协议。值通常是较小的东西,例如日期、货币、数据库查询。服务通常是访问外部资源,例如数据库连接、消息网关、存储库、产品工厂。
实体和值之间的一个明显区别是,值会覆盖相等方法(以及哈希),而实体通常不会。这是因为您通常不希望在处理上下文中有多个对象代表同一个概念实体,但是您不关心多个“5.0”对象。值可能是基本类型(在区分基本类型的语言中)或具有特殊的语言支持(就像在 .NET 中一样),但它们不必如此。要遵循的一条重要规则是,值对象应该是不可变的(否则您会遇到各种与别名相关的错误)。要更改值(例如我的身高),您不会更改身高对象,而是用一个新的对象替换它。
服务对象通常通过使用全局变量、类字段(在 Robert Martin 的术语中称为单态)或单例来实现。当然,它们通常是单一的,因为您只有一个,但您如何做到这一点则多种多样。通常,单一性是在处理上下文中 - 因此在多线程环境中每个线程一个。无论如何,您应该确保您的实现机制对其他领域对象隐藏,以便您可以轻松地更改它。Eric 在他的书中指出,服务应该是无状态的,尽管我们已经讨论过这个问题,他不再认为这是必要的 - 尽管如果可以做到这一点很好。
这个领域的一个问题是,这种术语虽然很生动,但与其他想法混淆在一起。实体通常用于表示数据库表或与数据库表相对应的对象。服务有整个面向服务的架构在进行,以及应用程序架构中的 服务层。因此,如果我使用这些术语,我必须明确说明我在领域模型的上下文中使用它们,以及它们在 Eric 的书中的含义。因此,要谨慎,不要假设人们正在这样使用这些词 - 它们被严重地重载了。不幸的是,没有太多替代方案。