领域驱动设计-软件模型要素

软件模型要素

摘自 Eric Evans 《领域驱动设计:软件核心复杂性应对之道》

ENTITY(实体)

ENTITY建模

最基本的职责是确保连续性,使其行为清晰且可预测。

保持实体简练是实现上述职责的关键。

抓住定义实体最核心的特征,尤其是用于识别,查找,或匹配对象的特征。

实体除了标识问题,往往通过协调其关联的对象的操作来完成自己的职责。

示例:
avatar

唯一标识

保持全局唯一

VALUE OBJECT(值对象)

用于描述领域某个方面而本身没有概念标识的对象。

实例化后用来表示一些设计元素,只关心它们是什么,而不用关心它们是谁。

示例:在电力运营公司的软件中,一个地址对应于公司线路和服务的一个目的地。如果几个室友各自打电话申请电力服务,公司需要知道他们其实是住在同一个地方。在这种情况下,地址是一个ENTITY。换种方式,模型可以将电力服务与住处关联起来,住处就是一个带有地址属性的ENTITY了,这里的地址就是一个VALUE OBJECT。

VALUE OBJECT 经常作为参数在对象之间传递消息。

当我们只关心一个模型元素的属性时,应该把它归类为VALUE OBJECT。我们应该使这个模型元素能够表示出其属性的意义,并为它提供相关功能。VALUE OBJECT 是不可变的。不要为它分配任何标识。也不要设计成ENTITY那么复杂。

avatar

策略

复制:可能导致系统被大量的对象阻塞。挡在两个机器之间传递一个副本时,只需发送一条消息,副本到达接收端是独立存在的。

共享:可能会减慢分布式系统的速度。传递的是一个引用,这要求每次交互都要向发送方返回一条消息。

1
2
3
4
5
6
7
8
9
1. 节省数据库空间或减少对象数量是一个关键要求时。
2. 通信开销很低时(如中央服务器中)。
3. 共享的对象被严格限定为不可变时。
3.1 保持VALUE OBJECT 不可变极大简化了实现,并确保共享和引用传递的安全性。
3.2 特殊情况
3.2.1 如果VALUE频繁改变。
3.2.2 如果创建或删除对象的开销很大。
3.2.3 如果替换(而不是修改)将打乱集群。
3.2.4 如果VALUE的共享不多,或者共享不会提高集群性能,或其他某种技术原因。

SERVICE(服务)

SERVICE是作为接口提供的一种操作,他在模型中是独立的,它不像ENTITY和VALUE OBJECT 那样具有封装的状态。

它强调与其他对象的关系。只是定义了能够给客户做什么。是一个动词,以一个活动来命名。

SERVICE有定义的职责,这种职责以及履行它的接口应该是作为领域模型的一部分来加以定义。

特征

与领域概念相关的操作不是ENTITY或VALUE OBJECT 的一个自然组成部分。

接口是根据领域模型的其他元素定义的。

操作是无状态的。
无状态指任何客户都可以使用某个SERVICE的任何实例,不必关心该实例的历史状态。

当领域中的某个重要的过程或转换操作不是ENTITY或VALUE OBJECT 的自然职责时,应该在模型中添加一个作为独立接口的操作,并将其声明为SERICE。定义接口时要使用模型语言,并确保操作名称是UBIQUITOUS LANGUAGE中的术语。此外,应该使SERVICE成为无状态的。

粒度

控制领域层中接口的粒度,并且避免客户端与ENTITY和VALUE OBJECT耦合。

在大型系统中,中等粒度的、无状态的SERVICE更容易被复用,因为它们在简单的接口背后封装了重要的功能。此外,细粒度的对象可能导致分布式系统的消息传递效率低下。

引入领域层服务有助于应用层和领域层之间保持一条明确的界限。

MODULE(模块,也称PACKAGE)

MODULE也是一种表达机制。

它的选择应该取决于被划分到模块中的对象的意义。

如果模型讲述了一个故事,MODULE就是这个故事的各个章节。

一个内聚的概念集合。MODULE之间是低耦合的。

在MODULE选择的早期,有些错误不可避免,这些错误导致了高耦合,从而使MODULE很难进行重构。而缺乏重构又会导致问题变得更加严重。克服这一问题的唯一方法就是接收挑战,仔细地分析问题的要害所在,并据此重新组织MODULE。