《实现领域驱动设计》读书笔记摘抄(9)—— 模块

Administrator 450 2022-04-07

第9章 模块

本章学习路线图

  • 学习传统的模块和新的部署模块化之间的区别
  • 学习**通过通用语言(1)**来命名模块的重要性
  • 学习机械式模块是如何给建模带来阻碍的
  • 学习SaaSOvation团队是如何设计模块的
  • 学习模块在领域模型之外所扮演的角色,以及何时应该使用新的模块而不是新的界限上下文

在Java中,模块被称为包;在C#中,模块被称为命名空间;在Ruby中,我们可以通过module关键字来达到创建命名空间的效果。

通过模块完成设计

在DDD中,模型中的模块表示了一个命名的容器,用于存放领域中内聚在一起的类。将类放在不同的模块中的目的在于达到松耦合性。由于DDD中的模块并不是一个通用的存储区域,因为对其进行适当的命名是重要的。

在设计模块时,有几条简单的原则(详细解释见书中表格):

  • 模块应该和领域概念保持协调一致
  • 根据通用语言来命名模块
  • 不要机械式的根据通用的组件类型和模式来创建模块
  • 设计松耦合的模块
  • 当同层模块间出现耦合时,我们应该杜绝循环依赖
  • 在父模块和子模块之间放松原则
  • 不要将模块设计成一个静态的概念,而是与模型中的对象一道进行建模

模块的基本命名规范

通常,模块名都以你自己的公司/组织名称开头,其中还包含因特网域名。

com.saasovation

唯一的顶级模块名可以避免与第三方模块的命名冲突。如果公司规定好了顶级模块的命名规范,保持一致即可。

领域模型的命名规范

接下来的一层模块名定位了一个限界上下文。

com.saasovation.identityaccess

接下来又向模块添加了另一层重要的名字,该层用于定位领域中某个特定的模块。

com.saasovation.identityaccess.domain

以上的domain部分可能不直接包含实际的接口/类,而是作为更低层模块的容器。以下是domain的下一次。

com.saasovation.identityaccess.domain.model

在该层中,我们定义模型中的类。接口类和抽象类也位于其中。

如果习惯将领域服务放在domain.model之外,也可以为其创建一个于model同层的模块:

com.saasovation.identityaccess.domain.service

然而还需要记住的是,我们并不是开发一个领域。**领域(2)**表示的是我们所工作的一个业务范围。事实上我们开发的是一个领域中的模型。

其他建议见书中对应章节。

敏捷项目管理上下文中的模块

SaaSOvation的Project团队选用了3个顶层模块:tenant、team个project。

com.saasovation.agilepm.domain.model.tenant
|__TenantId

com.saasovation.agilepm.domain.model.team
|__MemberService
|__ProductOwner
|__Team
|__TeamMember

com.saasovation.agilepm.domain.model.project
|__Product
|--backlogitem
|  |__BacklogItem
|--release
|  |__Release
|--sprint
   |__Sprint

其他层中的模块

在不考虑**架构(4)的情况下,你总需要为架构中的非模型组件所创建的模块并为其命名。本节以分层架构(4)**的命名规范为例。

在一个典型分层架构中,我们将系统分为以下不同的层:用户界面层、应用层、领域层和基础设施层。以**用户界面层(14)**为例,根据提供的数据类型不同,至少需要如下两个模块:

com.saasovation.agilepm.resources
com.saasovation.agilepm.resources.view

应用层可能还有另外的模块,比如:

com.saasovation.agilepm.application.team
com.saasovation.agilepm.application.product

先考虑模块,再是限界上下文

我们应该通过通用语言的需求来划分模型边界。限界上下文不是用来代替模块的。使用模块的目的在于组织那些内聚在一起的领域对象,对于那些内聚性不强或者没有内聚性的领域对象,我们应该将他们划分在不同的模块中。