我几乎忘了这件事,如今回顾、我发现并没有合适的案例可供参考,现有的案例要么不完整、要么是与业务耦合的特定场景,要么无法支撑研发落地。所以我决定从实际生活中出发,虚拟一个案例场景,以便能够系统性的阐述这个问题。
正文开始
本案例侧重于DDD的实践,从实际业务场景推导软件架构,将业务元素映射为系统元素,让系统本身成为最好的业务文档。在本案例中,我们选择餐厅作为业务场景,但不在意餐厅实现细节,而是以餐厅为主线故事,系统性的阐述DDD落地方法。希望读者能够从中吸取精华,去其糟粕,全文较长、耐心读完、必有收获。
1、领域设计
领域设计的核心是业务驱动的分而治之,旨在缩小软件系统与真实业务的差异,从而减少差异带来的问题。
当业务与系统之间存在差异时,我们无法将业务逻辑和程序逻辑对应起来,从而分不清区域,也分不清职责,因此会觉得混乱。就像你平时不会将枕头和被子放在厨房或卫生间一样,你的床上不会放着大米白面,否则你想睡觉是一件很复杂的事情,软件系统也是如此。
所以、首先要把业务分析清楚,然后设计与业务模型对应的软件模型,这就是DDD的核心思想。
1.1 宏观流程
假如我要设计一个餐厅,由于分而治之的需要,我会首先从宏观流程去分析,可以帮我们迅速找到重要的区域(这是功能相关性的初步划分)。
因此会得到几个明确的行为区域,我将餐厅划分为“菜品域”,“订单域”,“厨房域”,“用餐域”,这是宏观级别的领域划分,后续应该针对每个区域单独分析。
产出物是:宏观流程和参与角色
1.2 统一语言
语言贯穿于整个开发过程,从需求分析到设计、从设计到编码,因此好的语言非常重要,好的语言体现了清晰的业务概念。
在这个阶段,我们需要通过梳理,找到业务中都有哪些实体与行为,对其做一些归纳。我们的核心问题是“谁”通过什么“行为”影响了“谁”,其中的三个要素分别是“角色”、“行为”、“实体”,因此我的建议是先找到 “角色”、“行为”、“实体”,并对他们归类,我常常关注角色以及具体身份、行为以及包含的重要步骤、实体以及具体实例。
角色:是施事主语、是名词,是主动发起行为的一类实体。
行为:是动词、是做了什么事情,是行为本身。
实体:是名词,是除“角色”之外的其他实体。
推荐使用脑图画出来,我认为归纳后的脑图有助于我们识别根本要素,有利于抽象。
产出物是:名词、概念定义、相关脑图。
1.3 用例分析
在这一步、我们使用相对宏观的分析,不需要进入用例的细节分析,主要的目的是掌握角色与行为之间的关系,理清谁在做什么,角色的职责目的是什么,用于指导领域划分以及领域服务设计。
产出物:用例图
以做菜为例,如图
1.4 领域划分
我们在分析宏观流程时,划分了几个行为区域,那是宏观级别的。在那基础之上,我们需要拉进某个区域的视角,再结合之前的用例分析,按照“功能相关性”、“角色相关性”进一步划分领域。我们不仅要知道谁做了什么,还需要知道谁“在哪”做了什么。
功能相关性:也称为业务相关性,业务是由一套用例组成的,一套用例之间是符合高内聚原则的,一套用例构成了一个问题空间,也就构成了一个领域,所以“功能相关性”是划分领域的黄金标准。例如与做菜相关的用例都应该归属于厨房,所以我们确认了厨房域,这也是很自然的事。在这一步,通过划分领域、梳理领域与用例之间的关系。
角色相关性: 角色相关性不可以作为首要参考因素,在特殊情况下用于划分子域,某个区域涉及多个角色参与,可以按照角色的分工,拆分为多个子域,从而满足不同角色的个性化需要。例如厨房的采购人员负责买菜、刀工负责切菜、大厨负责烹饪。我们就会考虑将厨房划分为“采购子域”、“加工子域”、“烹饪子域”。通常来说,子域不具备独立的问题空间,不会作为独立的领域存在。
划分领域的核心原则是保证领域的自治性(最小完备和自我履行),谨慎使用“实体相关性”划分领域,否则有可能将一个功能打散在多个领域上,违反了自治性原则,如果按照功能相关性划分,更容易实现领域的自治性,并且有助于将功能需要的实体聚合在一起。
产出物:领域、子域、领域与用例的关系
以厨