13 更深刻地理解领域知识
捕获行为需求和事件风暴
箭头上的文字表示关系的含义。箭头的方向代表的是读这些文字的方向。比如说,“用例分析”指向“捕获行为需求”,读“……是……的一种方法”的时候,就把“用例分析”代入第一个省略号,把“捕获行为需求”代入第二个省略号,对应的读法是“用例分析是捕获行为需求的一种方法”。
首先,读数据和命令,一般会映射到应用服务的 API。读数据映射为查询功能,命令映射为增、删、改等功能。然后,应用服务会查询或操作领域对象,领域对象的修改则会触发领域事件。最后,领域对象的识别,可以从领域名词开始。执行者,一般会映射成领域对象充当的角色。
14 聚合的概念
聚合的表示法
在一个聚合里,像员工这样代表整体的实体就是聚合根。一个聚合只有一个聚合根。
最后,我们用一个包把这个聚合中的类包起来,从而可以一眼看出这个聚合的边界。一般我们约定,聚合包的名字和聚合根的名字是一样的
对于聚合而言,聚合根要有全局的唯一标识,而从属于聚合根的实体只需要有局部于聚合的标识。例如,员工是聚合根,员工号是全局标识。而工作经验没有必要进行全局编号,只需要在聚合内部编个号就可以了。例如,001 号员工的第 1 份工作经验、第 2 份工作经验等等。
15 聚合的实现(上):对聚合封装
因为聚合的不变规则往往不是单个对象能够处理的。比如说,“同一技能不能录入两次”这个规则,通过查看单独的技能对象是无法验证的,必须查看员工的全部技能,才能判断一条新技能是否重复。所以这种规则必须由聚合根或者相应的领域服务负责验证。
因此,为了保证不绕过规则的校验,非聚合根对象就不能由外界直接创建或修改。这就得出了聚合编程的一个重要原则:聚合外部对象对非聚合根对象只能读,不能写,必须通过聚合根才能对非根对象进行访问。
由于每个聚合都在同一个包里,把技能的 setter 设置成包级权限,就保证了只有在聚合内部的聚合根、领域服务、工厂等才能对他进行修改,外界只能读取。同样,技能对象的构造器也是包级权限,这样,就只有聚合内部才能创建技能对象了