像梦一样奔驰|谈DDD领域驱动设计和建模( 五 )


这些对象可能在领域建模的时候会分解到多个Entity或Value Object , 但是一定要意识到实际的聚合在哪里?我们真正关注的业务对象实体究竟有哪些?
为什么如此强调领域模型 , 强调聚合根的概念 , 因此我们在关注领域模型的时候将有助于我们打破原有的关系型数据库的思维模式 , 转化为对象和领域的思维模式 。
可以看到领域建模和聚合根的思路正是既适合于关系型数据库 , 也适合NoSql数据库的建模思路 。 因为在NoSQL持久化的时候 , 我们看到采购订单就是一个对象 , 其它明细和关联信息都是这个对象下的子实体信息 , 采购订单应该作为一个对象整体进行查询和存储 , 我们并不关心NoSQL会如何去存储这个对象 。 让我们正在关注领域对象 , 而不是去关心如何持久化 。
聚合Aggregate就是一组相关对象的集合 , 我们把它作为数据修改和访问的单元 。 每个聚合都会有一个聚合根和聚合的边界Boundary , 边界定义了在一个聚合里面内部应该有哪些实体 , 哪些子实体对象 。 定义边界的原因是我们期望对一个聚合的访问是通过聚合根点进行的 , 聚合里面的子实体对外界是完全封闭的 。 对于外部对象不应该去访问到一个聚合边界里面的子实体 。
在一些场景下 , 对于一个聚合的访问 , 我们往往只需要查询到头信息 , 而不关心具体的子实体信息 , 这个有点类似于传统O/R Mapping里面的惰性加载 。 在这里也必须要考虑到 。 在实现和设计聚合的时候 , 需要考虑到这种场景 , 即根据需要来加载一个完整聚合中的实体和子实体 , 以满足性能的需要 。 如何对应关系型数据库 , 对一个聚合实际的新增变更处理则可能涉及到多个数据表的多次操作 , 而这已经是仓储接口和仓储实现需要考虑的问题 。 现在对一个聚合的一次操作一定应该在一个完整的事务里面 , 以保障实际的事务完整性要求 。
按实际对象分析思路 , 在领域模型中的领域对象分析应该按照从顶向下的思路进行展开 , 如果这样的话首先识别到的就是聚合根对象 , 然后再考虑对聚合根对象进行展开 , 在聚合根对象的展开过程中进一步细化子实体之间的关联和依赖关系 。
领域模型-规格模式对于Specification模式是在第九章讲解将隐式概念转变为显式概念的时候讲到的 , 首先为何会单独剥离规格类 , 书里面的最重要解释就是对于业务规则通常不适合作为entity或value object本身的职责 , 而且规则的变化和组合也会掩盖领域对象的基本含义 。 但是如果将规则移出领域层 , 那么结果会更加糟糕 , 因为领域代码本身就不再很好的表达业务模型了 。
对于Specification模式的主要应用场景书里面谈到三点 , 即:

  • 验证对象 , 检验对象本身是否满足某些业务要求
  • 从集合中选择符合特定业务规则的对象或对象子集
  • 指定在创建新对象的时候必须要满足某种业务要求
如果从以上几点来看的话 , 对于规格模式可以更多的看做是对业务实现过程中业务规则的单独剥离 , 放到独立的规格类来实现 , 主要就是处理业务规则 。 在谈到这里的时候我们再回顾下领域模型中的几个特定对象:
  • 实体(Entity):拥有唯一标识的对象 。
  • 值对象(Value Object):没有唯一标识的对象 。
  • 工厂(Factory):定义创建实体的方法 。
  • 资源库(Repository):管理实体的集合并封装其持久化过程 。
  • 服务(Service):实现不能指派或封装在一个单一对象上的操作 。
如果基于这些核心对象来看 , 需要增加一些对整个领域模型和模式使用的一些分析 。
对于最简单的业务对象的业务操作 , 比如就一个单表用户信息的维护 , 这种场景下Repository对象下的实体CRUD方法已经够用 , 但是还是要通过Service对象进一步封装再暴露为服务接口 。 只有对于复杂对象的时候才启用聚合和工厂模式 , 这从减轻架构的复杂性上是可以的 。