两个维度
1.范围维度
软件开发的主要目的是为其他行业的业务问题提供技术解决方案,例如为航空公司的飞机实现自动导航,为汽车公司的汽车生产进行产销平衡,等等。从范围上看,软件开发所覆盖的范围可以划分为两个领域:我们要开发的软件系统属于解决方案域,而软件系统来自于和最终服务于的业务领域(例如飞机导航、汽车生产等等)属于问题域。问题域存在问题,软件系统为问题域的问题提供技术上的解决方案。
这就是我们看待软件开发的第一个维度:范围维度。我们把软件开发的范围划分为问题域和解决方案域。处于问题域中的时候,我们的任务是认识世界,即了解在没有软件系统存在的情况下,现实业务领域是如何运作的;而处于解决方案域中的时候,我们是在改造世界,即通过设计和实现软件系统帮助业务领域运作得更好(更大、更快、更赚钱、更省钱、更透明、更灵活……)。
在软件开发的术语中,“业务”、“领域”是问题域的同义词,“系统”是解决方案域的同义词(“系统”是“软件系统”的简称)。因此,“业务”分析师、”业务”规则、“业务”用例模型、“领域”模型、“领域”专家聚焦于问题域,而“系统”分析师、“系统”用例模型聚焦于解决方案域。
2.内/外维度
除了范围维度之外,我们还可以从另外的维度看待软件开发,其中一个非常有用的维度是内/外维度。从内/外视角看,系统在我们面前可以划分为外部视图和内部视图。外部视图是系统的黑盒视图,我们看不到它的内部组成和运作机制,只能够看到它呈现给外界的功能;而内部视图是系统的白盒视图,揭示了系统的内部组成和运作机制。
四个象限
范围维度和内外维度两者是正交的,你可以把它们看成两条相互垂直相交的数轴——X轴和Y轴,它们把软件开发问题空间分割为四个区域——用数学的术语表示就是四个象限。分别说明如下:
问题域的外部视图——业务用例模型
这个象限从外部视角看待问题域,分析探究业务系统实现了哪些功能,对它的用户提供了哪些业务价值?每一项系统功能体现为一个业务用例。例如银行储蓄业务为储户提供存款、取款、转账和提供对账单等等服务,每一项服务就是一个业务用例。
开发业务用例模型不是软件开发的必须步骤,但本人强烈建议不要忽略这一方面的工作。一方面,开发软件系统的目标就是实现和改进这些业务用例(或业务用例中的某些环节),业务用例为软件开发设定了目标和验收标准;另一方面,通过业务用例分析可以更有效地支持领域建模和系统用例建模,有助于发现领域对象和机制,防止系统用例遗漏或疏失。
问题域的内部视图——领域模型
这个象限从内部视角看待问题域,研究业务领域中固有的组成、结构、行为、机制和规则等等。这方面的工作称为“领域建模”,其产物称为“领域模型”。我们要重点分析研究的是:业务领域中有哪些关键的概念实体(例如银行储蓄业务中的账户、对账单、信用卡、借记卡等等)、各种概念实体之间的关系(关联、泛化、组合、聚合等等),以及它们之间如何交互以实现业务用例模型中规定的各项功能等等。
领域建模是软件开发中最核心的活动,领域模型是软件开发中最有价值的工件。领域建模的质量在很大程度上决定了软件的成败。如果由于对问题域分析理解的偏差,产出错误的领域模型,那么,无论多么先进的技术工具、多么高超的技术手段,都无法挽救软件的失败命运。
解决方案域的外部视图——系统用例模型
这个象限从外部视角看待要构建的软件系统,定义了系统所要实现的所有业务功能,即软件系统能够向它的用户提供的业务价值。这方面的工作称为需求分析(通常只包含功能需求),其产物称为“系统用例模型”,简称为“用例模型”(另一个名字是“功能规格说明书”),每一个系统用例描述一项对用户有价值的系统功能。
系统用例模型定义了软件系统的功能外观,即软件系统能够“干什么”的方面。系统用例模型沟通了两个领域,它定义了要开发的软件系统(解决方案域)必须实现的业务功能(问题域)。
解决方案域的内部视图——设计模型
这个象限从内部视角看待要构建的软件系统,即通过哪些技术组件和怎样的交互手段实现系统用例模型中定义的业务功能?
领域模型中的对象代表问题域中的概念(如信用卡、账户等等),而设计模型中的对象代表软件实体(如记录集、DAO等等)。正如同业务用例模型会映射到系统用例模型一样(通常一个系统用例实现一个业务用例,或者业务用例中的一个步骤),领域模型中的对象也可以而且应该映射到设计模型中的相应软件对象之上,这意味着设计模型中也应该有信用卡、账户这样的对象,它们和领域模型中的同名对象一样,拥有相同或类似的属性和行为,这就是《领域驱动设计》一书的主张——使用同一种语言(业务语言)和基于同一个模型(领域模型)进行分析和设计,开发软件应用。
关系
解决方案域以问题域为出发点和依归,即问题决定解决方案(这是理所当然的,不是吗?如果我想求解3乘2这个问题,你却提供3加2这个解决方案,当然是错误的)。开发人员的一个典型的错误是把关注点集中在解决方案域的实现技术上,却忽视了问题分析和问题定义,因此在实现过程中失焦,刻鹄成鹜,画虎类犬。
内部视图“动于内”,外部视图“形诸外”。系统的外部视图定义了外部可见的行为,而内部视图揭示了系统内部的组织结构和运行机制,系统通过内部视图中的结构和机制实现外部视图中向外界呈现的行为。外部视图表达What,即“做什么”,内部视图表达How,即“怎么做”。
设计模型从两方面获得指导:从系统用例模型中获得“What”方面的指导,确定需要实现哪些功能;以及从领域模型中获得“How”方面的指导,确定如何实现那些功能。在分层架构中,用例模型和领域模型分别映射到系统的应用层和领域层;在Clean架构(Robert C. Martin,Clean架构)中,用例模型和领域模型分别映射到系统的用例层和实体层。
时刻记住你当前所处的位置
在软件开发过程中,请时刻记住你当前处于哪一个象限。我们应该严格划分业务(问题域)和技术(解决方案域),意图(外部视角)和实现(内部视角),要防止业务逻辑渗漏到技术组件中(例如在表示层或持久层中维护取款业务规则),也要防止技术逻辑渗漏到业务逻辑中(例如领域层代码依赖Spring或Hibernate,或使用RabbitMQ进行消息通信),还要防止在只应该表示意图的地方直接提供实现方案(例如在应用层中实现业务逻辑)。
当处于问题域中的时候,我们是在“发现”;当处于解决方案域的时候,我们是在“发明”。当处于外部视图中的时候,我们是在表达意图和行为;当处于内部视图中的时候,我们是在表达实现和机制。具体来说:
- 在业务用例模型中,我们发现业务意图和业务行为;
- 在领域模型中,我们发现业务实体和业务实现机制;
- 在系统用例模型中,我们定义待开发的软件系统要实现的行为和功能;
- 在设计模型中,我们定义待开发的软件系统的内部结构和行为实现机制。