实现领域驱动设计-实体

2018-12-15  本文已影响0人  marx_yu

唯一标识

实体是重要的领域模型,实体是具有唯一标识的领域模型,跟值对像相对而言。比如两个人可以有很多属性是一样的,比如性别、名字、生日以及穿同样的衣服,但能说两者是同样的实体吗,不行。人,具有社会学标识,比如身份证号,具有生物学的标识,比如基因组,所以人是实体,具有互相区分的唯一标识。对于值对像,我们平常使用的颜色值,如RGB(128,126,129),只要RGB三个分量值一样,使用起来就无差别,都表示同一个颜色。那在领域分析中,一个对象应该处理成实体还是值对象呢?那就得结合业务场景进行分析了,业务逻辑变化了,就可能从实体变成值对像了。

唯一标识,应该算是实体的最重要的特征了。

唯一标识生成方式可以是:用户提供(比如用户输入的登陆用户名),应用程序生成(UUID,以及现在比较流行的snowflake),持久化机制生成的(常见的mysql自增id),另一个限界上下文提供的唯一标识(其实就是跟另一个实体关联,使用其标识)。

那这个唯一标识能不能改呢?一般情况下是不允许修改的,这样可以保持实体在整个生命中标识的稳定性,便于业务流程的跟踪。

那怎么保持不变呢?放在构造函数,不提供或隐藏setter函数。

那如果要支持可以修改呢?对于使用RDB或ORM来说,这种情况就一般使用委托标识了,即在db里有一个id主键,但不属于领域模型里的,用于对db record的标识。为什么不直接修改唯一标识,即db主键呢?一般会到db锁定表和区间,而以id主键为条件更新列,则很容易使用行级锁。

发现实体及其本质特征

揭开实体及其本质特征的神秘面纱:发现并命名实体之后,找到那些起唯一标识作用的属性

挖掘实体的关键行为:从领域专家那获取业务的动作,转化为有意图的接口定义,这就是领域模型的关键行为了,然后再由具体的实体类型来实现接口。重点还是要划分清楚,行为和实体的关系,即那些行为是那些实体才具有的,不要弄错了。比如书中提到的租户实体Tenant和用户实体User,提到用户注册的行为,那到底是租户的行为来注册用户,还是用户的行为注册到租户上呢?那应该看业务行为了,如果业务场景是从用户出发,由用户选择一个租户主动去申请注册,那就应该是User.registerTo(Tenant),而如果是租户管理后,主动选择一个用户,然后把用户注册到企业中,那就应该是Tenant.registerUser(User),对于书中的业务场景就属于后者了。

领域对象扮演多种角色:这里的角色,不是职责与角色的那种,不是RBAC里的权限角色的概念。那是什么呢?“在面向对象编程中,通常由接口来定义实现类的角色。在正确设计的情况下,一个类对于每一个它所实现的接口来说,都存在一种角色”,即一个接口应该代表一种系统的“角色”,对于一个类来说,可以按需要实现多种接口,也就是扮演多种角色。但对于一个接口定义来说,要不要定义多个业务需要的接口,扮演多种“角色”呢?这肯定是不可取的。那一个实现类该不该多种接口呢?这一般看技术实现需要,这不影响领域建模:“不管采用哪种设计方式,我们都应该确保领域语言优先于技术实现。在DDD中,业务领域的模型才是最重要的”,这从另一方面讲,领域建模是基于接口声明,而不是技术实现的。

创建实体:必要的不变的属性的应该作为构造函数的参数

验证:对于属性的前置条件验证应该放在领域对象里;对领域对象整体的验证逻辑应该放在单独的验证器里,验证类可以实现策略模式并在发现非法状态时通知客户方;对对象组合的验证一般是放在领域服务中进行。

跟踪变化:跟踪变化最实用的方法是领域事件和时间存储。

上一篇 下一篇

猜你喜欢

热点阅读