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


首先对于实体Entity , 实体核心是用唯一的标识符来定义 , 而不是通过属性来定义 。 即即使属性完全相同也可能是两个不同的对象 。 同时实体本身有状态的 , 实体又演进的生命周期 , 实体本身会体现出相关的业务行为 , 业务行为会实体属性或状态造成影响和改变 。
真正的现实世界 , 每个事物都一定会有唯一的标识 , 关键点是我们实际的业务场景和需求是否需要管理到唯一标识 。
书里面举了一个例子 , 当我们发放的门票上有座位号的时候 , 座位需要作为独立的实体 , 座位号是唯一的标识 。 而当先到先座模式下 , 我们只关心剩余座位数 , 那么座位号并不是唯一标识 。 这跟我们的业务需求有关 。
一个对象不由属性来定义 , 那么看人这个对象 , 身份证号是属性 , 其实也是对于人的唯一标识 。 不考虑本身身份证号的位数升级 , 一个身份证号会跟随你一辈子 。 但是对于人我们一般仍然会作为实体Entity来看待 , 因为人有状态 , 有对象演进的生命周期 , 会主动产生各种行为 。
对于企业内信息系统 , 很多时候我们把员工工卡号作为唯一标识来使用 , 但是要意识到工卡号只是人员的一个属性 。 虽然工卡号本身不会出现两个重复的 , 但是该属性仍然可能演变 , 如果将工卡号作为唯一标识和ID , 那么在该属性变化时候所有其余关联对象都将受到影响 。 从这个层面来看 , 一个唯一的内码ID才是可信的唯一标识 。
而对于值对象Value Object , 它用于描述领域的某个方面本身没有概念标识的对象 , 值对象被实例化后只是提供值或叫设计元素 , 我们只关心这些设计元素是什么?而不关心这些设计元素是谁 。 书里面谈到颜色 , 数字是常见的值对象 。 这种对象无状态 , 本身不产生行为 , 不存在生命周期演进 。
是否为值对象跟实际的业务场景仍然关系密切 。
书里面又举了地址的例子 , 当地址是值对象的时候 , 地址本身无状态 , 可以被多个实际有状态的实体使用 , 地址不存在太多的生命周期演进场景下地址为值对象 。 而对于本身行政区域管理软件中 , 地址本身存在状态 , 存在根据行政区域规划变化而演进的过程 , 因此地址为实体 。
如果从值对象本身无状态 , 不可变 , 并且不分配具体的标识层面来看 。 那么值对象可以仅仅理解为实际的Entity对象的一个属性集合而已 。 该值对象附属在一个实际的实体对象上面 。 值对象本身不存在一个独立的生命周期 , 也一般不会产生独立的行为 。
值对象往往可能是多个属性的聚合 , 本身无唯一标识 , 多个属性最终形成的一个结果值 , 而这个结果值往往又依附在一个实际的实体Entity上面 。 那么如果从这个概念来说 , 值对象往往不会单独进行持久化 , 或形成数据库设计的一张数据表 。 另外一种情况 , 对于简单的数据字典类对象 , 是否考虑作为值对象 , 这种对象需要持久化 , 如纳税属性 , 物料类型 , 它们设计到数据字典中取值 , 这个数据字典无状态 , 无自己的生命周期 , 是可以作为值对象来处理的 。
领域模型-聚合和聚合根
像梦一样奔驰|谈DDD领域驱动设计和建模聚合和聚合根是领域模型里面很重要的一个概念 , 其实我们在从真实世界对业务对象进行识别和概念建模的时候 , 关注的就是聚合根 , 这才是我们真正要管理的业务对象 。
一个对象可能有多个层次 , 也可能有多个子实体 , 但是这些子实体都不可能孤立存在 , 它们必须依附于一个聚合根存在 , 它们和根节点具有同样的生命周期 。
如果一个客户消亡 , 客户联系方式 , 客户的多张银行账户信息将不再有任何意义 。 如果一张采购订单头消失 , 那么采购订单明细没有任何存在的意义 。 客户 , 采购订单 , 发票这些从真实业务中转化过来的业务对象才是真正的领域核心对象 。