《领域驱动设计:软件核心复杂性应对之道(修订版)》—第1章 1.4节知识丰富的设计

简介: 通过像PCB示例这样的模型获得的知识远远不只是“发现名词”。业务活动和规则如同所涉及的实体一样,都是领域的核心,任何领域都有各种类别的概念。

本节书摘来自异步社区《领域驱动设计:软件核心复杂性应对之道(修订版)》一书中的第1章,第1.4节知识丰富的设计,作者【美】埃里克•埃文斯(Eric Evans), 马利伟 , 万龙,更多章节内容可以访问云栖社区“异步社区”公众号查看。

1.4 知识丰富的设计
通过像PCB示例这样的模型获得的知识远远不只是“发现名词”。业务活动和规则如同所涉及的实体一样,都是领域的核心,任何领域都有各种类别的概念。知识消化所产生的模型能够反映出对知识的深层理解。在模型发生改变的同时,开发人员对实现进行重构,以便反映出模型的变化,这样,新知识就被合并到应用程序中了。

当我们的建模不再局限于寻找实体和值对象时,我们才能充分吸取知识,因为业务规则之间可能会存在不一致。领域专家在反复研究所有规则、解决规则之间的矛盾以及以常识来弥补规则的不足等一系列工作中,往往不会意识到他们的思考过程有多么复杂。软件是无法完成这一工作的。正是通过与软件专家紧密协作来消化知识的过程才使得规则得以澄清和充实,并消除规则之间的矛盾以及删除一些无用规则。

示例 提取一个隐藏的概念

我们从一个非常简单的领域模型开始学习,基于此模型的应用程序用来预订一艘船在一次航程中要运载的货物,如图1-8所示。


e61a466b4f05bb6a854056bc12479d13337a4c0c

我们规定这个应用程序的任务是将每件货物(Cargo)与一次航程(Voyage)关联起来,记录并跟踪这种关系。现在看来一切都还算简单。应用程序代码中可能会有一个像下面这样的方法:
public int makeBooking(Cargo cargo, Voyage voyage) {
   int confirmation = orderConfirmationSequence.next(); 
   voyage.addCargo(cargo, confirmation);
   return confirmation;
}

由于总会有人在最后一刻取消订单,因此航运业的一般做法是接受比其运载能力多一些的货物。这称为“超订”。有时使用一个简单的容量百分比来表示,如预订110%的载货量。有时则采用复杂的规则——主要客户或特定种类的货物优先。

17
这是航运领域的一个基本策略,从事航运业的业务人员都知道它,但在软件团队中可能不是所有技术人员都知道这条规则。

需求文档中包含下面这句话:

允许10%的超订。

现在,类图就应该像图1-9这样,代码如下:

public int makeBooking(Cargo cargo, Voyage voyage) {
   double maxBooking = voyage.capacity() * 1.1;
   if ((voyage.bookedCargoSize() + cargo.size()) > maxBooking) 
       return –1;
   int confirmation = orderConfirmationSequence.next(); 
   voyage.addCargo(cargo, confirmation);
   return confirmation;
}


f2a73ee1eaa620f5141e1300ab07bbe9ff0d9104

现在,一条重要的业务规则被隐藏在上面这段方法代码的一个卫语句②中。第4章将介绍Layered Architecture,它会帮助我们将超订规则转移到领域对象中,但现在我们主要考虑如何把这条规则更清楚地表达出来,并让项目中的每个人都能了解到它。这将使我们得到一个类似的解决方案。

(1) 如果业务规则如上述代码所写,不可能有业务专家会通过阅读这段代码来检验规则,即使在开发人员的帮助下也无法完成。

(2) 非业务的技术人员很难将需求文本与代码联系起来。

如果规则更复杂,情况将更糟。

我们可以改变一下设计来更好地捕获这个知识。超订规则是一个策略,如图1-10所示。策略(policy)其实是Strategy模式③[Gamma et al. 1995]的别名。我们知道,使用Strategy的动机一般是为了替换不同的规则,虽然在这里并不需要这么做。但我们要获取的概念的确符合策略的含义,这在领域驱动设计中是同等重要的动机(参见第12章)。


4cd5d1e6b106fa3e3ab7540cdd5cb01f527b771d

修改后的代码如下:
public int makeBooking(Cargo cargo, Voyage voyage) {
   if (!overbookingpolicy.isallowed(cargo, voyage)) return –1;
   int confirmation = orderConfirmationSequence.next(); 
   voyage.addCargo(cargo, confirmation);
   return confirmation;
}

新的Overbooking Policy类包含以下方法:

public boolean isAllowed(Cargo cargo, Voyage voyage) {
   return (cargo.size() + voyage.bookedCargoSize()) <= 
         (voyage.capacity() * 1.1);
}

现在所有人都清楚超订是一个独特的策略,而且超订规则的实现即明确又独立。

现在,我并不建议将这样的精细设计应用到领域的每个细节中。第15章将深入阐述如何关注重点以及如何隔离其他问题或使这些问题最小化。这个例子的目的是说明领域模型和相应的设计可用来保护和共享知识。更明确的设计具有以下优点:

19
(1) 为了实现更明确的设计,程序员和其他各位相关人员都必须理解超订的本质,明白它是一个明确且重要的业务规则,而不只是一个不起眼的计算。

(2) 程序员可以向业务专家展示技术工件,甚至是代码,但应该是领域专家(在程序员指导下)可以理解的,以便形成反馈闭环。

相关文章
|
2月前
|
存储 机器学习/深度学习 设计模式
笔记 - 《领域驱动设计:软件复杂性应对之道》
笔记 - 《领域驱动设计:软件复杂性应对之道》
|
7月前
|
开发者
构建可持续性软件架构:六大设计原则
构建可持续性软件架构:六大设计原则
161 0
|
存储 消息中间件 缓存
系统之技术设计原则
微服务架构-技术设计原则
178 0
|
XML 设计模式 JSON
领域驱动设计总结——如何设计大型系统
本文为领域驱动设计系列总结的第五篇,主要对领域驱动设计概念做个介绍,本系列领域驱动设计总结主要是在Eric Evans 所编写的《领域驱动设计》 一书的基础上进行归纳和总结。本文主要介绍在领域驱动设计中如何设计大型系统。
154 0
|
消息中间件 缓存 数据可视化
领域驱动设计对软件复杂度的应对
领域驱动设计对软件复杂度的应对
领域驱动设计对软件复杂度的应对
|
数据可视化 架构师 前端开发
复杂性应对之道 - 领域建模
复杂性应对之道 - 领域建模
复杂性应对之道 - 领域建模
|
Java
复杂性应对之道——抽象
写本文的原因是,抽象是软件设计中最重要的概念。但抽象这个概念本身又很抽象,我们有必要花一些时间深入理解抽象、抽象的层次性,以及不遗余力的不断提升我们抽象能力。
2687 0
|
关系型数据库
企业应用架构实践(复杂性应对之道)
#前言 从业这么多年,接触过银行的应用,Apple的应用,eBay的应用和现在阿里的应用,虽然分属于不同的公司,使用了不同的架构,但有一个共同点就是都很复杂。导致复杂性的原因有很多,如果从架构的层面看,主要有两点,一个是架构设计过于复杂,层次太多能把人绕晕。另一个是根本就没架构,ServiceImpl作为上帝类包揽一切,一杆捅到DAO(就简单场景而言,这种[Transaction Script]
9984 0
|
程序员 领域建模
《领域驱动设计:软件核心复杂性应对之道(修订版)》—第1章 1.2节知识消化
金融分析师要消化理解的内容是数字。他们筛选大量的详细数字,对其进行组合和重组以便寻求潜在的意义,查找可以产生重要影响的简单表示方式——一种可用作金融决策基础的理解。
1389 0