设为首页 加入收藏

TOP

领域驱动设计的基础知识总结(二)
2019-09-17 18:55:21 】 浏览:64
Tags:领域 驱动 设计 基础知识 总结
对象,然后把 Customer 的地址相关的信息转移到 Address 对象上。如果没有 Address 对象,而把这些地址信息直接放在 Customer 对象上,然后对于一些其他的类似Address的信息也都直接放在Customer 上,会导致 Customer 对象很混乱,结构不清晰,最终导致它难以维护和理解。

7.4. 值对象(Value Object)

并不是每一个事物都必须有一个唯一标识。就以上面的地址对象 Address 为例,如果两个 Customer 的地址信息是一样的,我们就会认为这两个 Customer 的地址是同一个。用程序的方式来表达就是:如果两个对象所有属性的值都相同,我们会认为它们是同一个对象,那么就可以把这种对象设计为值对象。

值对象的特征:

  • 值对象 没有唯一标识 ,这是它和实体的最大不同。值对象在判断是否是同一个对象时是通过它们的所有属性是否相同,如果相同则认为是同一个值对象。在区分是否是同一个实体时,只看实体的唯一标识是否相同,而不管实体的属性是否相同。
  • 值对象是 不可变 的,即所有属性都是只读的,所以可以被安全的共享。

应该给值对象设计的尽量简单,不要让它引用很多其他的对象。值对象只是一个值,类似(int a = 3)中的『3』,只不过是用对象来表示。值对象虽然是只读的,是一个完整的不可分割的整体,但是可以被整个替换掉:类似(a = 4)把a的值由『3』替换为为『4』,当修改 Customer 的 Address 对象引用时,不是通过 Customer.Address.Street 这样的方式来修改属性,可以这样做:Customer.Address = new Address(…)

7.5. 领域服务(Domain Service)

领域中的一些概念不太适合建模为对象(实体对象或值对象),因为它们本质上就是一些操作、动作,而不是事物。这些操作往往需要 协调多个领域对象。如果强行将这些操作职责分配给任何一个对象,则被分配的对象就是承担一些不该承担的职责,从而会导致对象的职责不明确很混乱。DDD认为领域服务模式是一个很自然的范式用来对应这种跨多个对象的操作。一般的领域对象都是有状态和行为的,而领域服务没有状态只有行为。

领域服务还有一个很重要的功能就是可以避免领域逻辑泄露到应用层。因为如果没有领域服务,那么应用层会直接调用领域对象完成本该是属于领域服务该做的操作,需要了解每个领域对象的业务功能,以及它可能会与哪些其他领域对象交互等一系列领域知识。这样一来,领域层可能会把一部分领域知识泄露到应用层。对于应用层来说,通过调用领域服务提供的简单易懂且意义明确的接口肯定也要比直接操纵领域对象容易的多。

说到领域服务,还需要提一下软件中一般有三种服务:应用层服务、领域服务、基础服务。从以下的例子中可以清晰的看出每种服务的职责:

应用层服务

  1. 获取输入(如一个XML请求)
  2. 发送消息给领域层服务,要求其实现转帐的业务逻辑
  3. 领域层服务处理成功,则调用基础层服务发送Email通知

领域层服务

  1. 获取源帐号和目标帐号,分别通知源帐号和目标帐号进行扣除金额和增加金额的操作
  2. 提供返回结果给应用层

基础层服务

  1. 按照应用层的请求,发送Email通知

7.6. 聚合及聚合根(Aggregate,Aggregate Root)

聚合定义了一组具有 内聚关系 的相关对象的集合,以及对象之间清晰的所属关系和边界,避免了错综复杂的难以维护的对象关系网的形成。我们把聚合看作是一个修改数据的单元。

聚合有以下特点:

  1. 每个聚合有一个根和一个边界:根是聚合内的某个实体;边界定义了一个聚合内部有哪些实体或值对象;
  2. 聚合根是外部可以保持对聚合引用的唯一元素,负责与外部其他对象打交道并维护自己内部的业务规则。聚合内部的对象之间可以相互引用,但是聚合外部如果要访问聚合内部的对象时,必须通过聚合根开始导航,绝对不能绕过聚合根直接访问聚合内的对象;
  3. 聚合内除根以外的其他实体的唯一标识都是本地标识,也就是只要在聚合内部保持唯一即可,因为它们总是从属于这个聚合的;
  4. 聚合内部的对象可以保持对其他聚合根的引用;
  5. 删除一个聚合根时必须同时删除该聚合内的所有相关对象,因为他们都同属于一个聚合,是一个完整的概念;
  6. 基于聚合的以上概念,我们可以推论出从数据库查询时的单元也是以聚合为一个单元,不能直接查询聚合内部的某个非根的对象;

如何识别聚合:

可以从业务的角度分析哪些对象它们的关系是内聚的,可看成一个整体来考虑的,然后这些对象可以放在一个聚合内。关系内聚是指这些对象之间必须保持一个固定规则,固定规则是指在数据变化时必须保持不变的一致性规则。当修改一个聚合时,必须在 事务级别 确保整个聚合内的所有对象满足这个固定规则。聚合尽量不要太大,否则可能带来一定的性能问题。通常在大部分领域模型中,有70%的聚合通常只有一个实体,即聚合根,该实体内部没有包含其他实体,只包含一些值对象;另外30%的聚合中,基本上也只包含两到三个实体。

如何识别聚合根:

如果一个聚合只有一个实体,那么这个实体就是聚合根;如果有多个实体,那么我们可以思考聚合内哪个对象有独立存在的意义并且可以和外部直接进行交互。

7.7. 工厂(Factory)

DDD中的工厂也是一种体现 封装思想 的模式。DDD中引入工厂模式的原因是:有时创建一个领域对象是一件比较复杂的事情,不仅仅是简单的new操作。工厂是用来封装创建一个复杂对象尤其是聚合时所需的知识,将创建对象的细节(如何实例化对象,然后做哪些初始化操作)隐藏起来。

客户传递给工厂一些简单的参数,如果参数符合业务规则,则工厂可以在内部创建出一个相应的领域对象返回给客户;但是如果参数无效,应该抛出异常,以确保不会创建出一个错误的对象。当然也并不总是需要通过工厂来创建对象,事实上大部分情况下领域对象的创建都不会太复杂,只需要简单的使用构造函数就可以了。隐藏创建对象的好处:可以不让领域层的业务逻辑泄露到应用层,同时也减轻了应用层的负担,它只需要简单的调用领域工厂创建出期望的对象即可。

7.8. 仓储(Repository)

仓储被设计出来的原因:领域模型中的对象自从创建后不会一直留在内存活动,当它不活动时会被持久化到DB中,当需要的时候会重建该对象。所以,重建对象是一个和DB打交道的过程,需要提供一种机制,提供类似集合的接口来帮助我们 管理对象

仓储里存放的对象一定是聚合,因为之前提到的领域模型是以聚合的概念来划分边界的。我们 只对聚合设计仓储 ,把整个聚合看成一个整体,要么一起取出来,要么一起被删除,不会单独对某个聚合内的子对象进行单独查询和更新。仓储还有一个重要的特征就是分为仓储定义部分和仓储实现部分,在领域模型中定义仓储的接口,而在基础设施层实现具体的仓储。

8.设计领域模型时一般步骤

  1. 根据需求建立初步的领域模型,识别明显的领域概念和之间的关联(1:1, 1:n的关系),用文字精确没有歧义的描述出每个领域概念的含义;
  2. 分析主要的软件功能,识别主要的应用层的类,这样有助于及早发现哪些是应用层的职责,哪些是领域层的职责;
  3. 进一步分析领域模型,识别出
首页 上一页 1 2 3 下一页 尾页 2/3/3
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇SQLAlchemy外键的使用 下一篇Java 商户管理系统 客户管理 库存..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目