自己的更好的文章: DDD 是啥,cqrs是啥
下面是基础文章
如何理解领域设计中的分层,服务编码,各业务对领域的不同处理.
强烈推荐的文章,详解DDD系列(
殷浩详解DDD系列 第一讲 - Domain Primitive
殷浩详解DDD系列 第二讲 - 应用架构
殷浩详解DDD系列 第三讲 - Repository模式
)
类似总结文章 DDD 最好的入门材料
领域设计
= 领域模型(hibernate擅长,但又太重,见repository)
+ 领域实体 (充血模型)
+ repository ( hibernate的关联list,屏蔽了repository概念, 让程序员很容易再内存中过滤,导致性能问题. 为什么不用hibernate的原因. 另外hibernate不能解决外键是hsf的问题,分布式.)
+ 事件流 (即现在的分布式弱依赖模式)
+ 内存中插件流(一个流程涉及到不同的领域类+ 流程上下文 ,before after 接口,见附件图. )
+ 分布式插件式( 一个流程中一个领域类被不同的业务处理, bz上下文+当前crd的DO, 业务校验, 即中台 ,中台需要 levelDb+线上列式存储,才能充分体现价值, 状态无非表现了n种属性值, 方便后续查询. crud . 中台要有能力自动生成接口, 而不是文档式hashmap, 避免内部逻辑重构导致中台插件失败, 如何解耦代码是个问题,要专门搞一层内部context和外部context的转换逻辑,注解.)
流程复用,策略点的复用.
强烈推荐领域配置能力. 基本类配置页面抽取+关联的能力. 详见平台策略点建设, 配置系统. (更好的是领域实体配置平台.)_个人渣记录仅为自己搜索用的博客-CSDN博客
会导致流程模板的嵌套. 例如某个业务流程有很多, 需要依赖某个业务的一个流程.
方案一: 引擎type法. 通过某个type处理不同的业务. 也可以通过异步化解耦.
方案二: 组合法. 另外一个模式是 组合模式. 在入口处先判断业务. 使用不同的业务实体/流程.
代码可维护性-
"权限隔离" 不如 "领域字段隔离" , 一个个充血模型天然就隔离了. 你的处理某领域的代码再也不会放到另外一个领域的manager下去了.
原则:
1.自己的EO不引用别人. 每个字段只能存在于一个EO中,避免封装混乱.
2.组合大于继承 (更灵活,更容易中台化,拆分到其他系统)
使用责任链工具manager 如下图1
因为原则1,如果其他EO的逻辑要用到其他EO中的字段,必须通过形参传入. 理论上只能单向依赖. 另外必须通过EO传递. 如果只有id时,通过manger 从dao中获取并组装.
3. 再次强调 别人的EO不能持有其他的EO,或者其他EO的字段. 也不能通过方法直接从dao里获取数据. 也就是不能有
ApplicationContextUtils类.
原因: 如果是非同jvm内,方法形参通过id传播. 同jvm内,最好通过EO传播,这样的话,可以少生成Eo,减少垃圾回收.
4. 这样做以后, 复杂查询的manager会复杂点. 其他业务manger就很薄了.下沉到领域实体中(EO).
5. 返回的bean和领域实体11对应,多了一些外键id. 这些属性根据不同的外露接口可以赋值,也可以不赋值? 最好单独写新的bean.
通过mapstruct 按需去赋值,字节码. 这样调用方不会被误导.
见qq邮件,如果图片不见后
图1:
图2
遗留两个问题
1. 但充血模型还是有个问题, 外部可以拿到这个领域类,获取其属性,再领域外进行逻辑处理,代码又乱了. 这个是正常的,因为user系统重要透出自己的属性给外部,外部根据user的属性去封装逻辑. 不可能都下沉到user类里来. 所以这时候会有新的领域类出现. 给user加个前缀,或者新赋予一个领域术语. 例如 人 变成了 父亲 . 有可能用继承,也有可能用组合. 建议继承.
Param再转成Entity,再转换成DO很关键
2. 同一个系统下,业务很多, 同一些字段属性处理, 都往user领域里加方法. 越来越多,这个时候就需要进行梳理了,将业务进行分离. 最方便的方法就是加个业务前缀,当然通过同名但通过不同的包也行.但是对后续代码可书写性会较差.
3. 组合还是继承? 继承是中台玩法,插件回调,中台含有扩展存储,流程组装暴露接口只有一个. 各个继承类实现自己的回调即可.
组合呢,中台提供基础能力,没有流程组装. 组合层层封装. 一开始先继承简单快速,少整理接口类, 继承爆炸后改成组合就要把原来的方法抽取出接口. 这样才能保证原有的代码改动最小. 改成组合后,原来的abstract 变成了接口实现了的一个调用.
* 领域实体不能操作数据库等存储层或者hsf层,需要service来组装.
* 领域实体和表(存储的区别)是 领域实体可以继承,存储可以泛化存储,以json,文档型等都可.
* 通过现有代码无法自动化进行架构切割, 通过新增依赖来进行模块划分是一种比较好的方法,增加内聚和依赖的认知成本,确保一个模块的依赖模块最简化. 所以开放需要单独成一个模块,因为要了解开放系统. 根据流程+实体来切割. 滴滴企业打车为啥是专门的系统就是因为增加了企业实体.需要需要专门的部门和系统来支持. 这种划分有助于生产力.
3. 如何和spring单例 dao结合, hsf facade ?
通过spring的ContextAware来实现静态获取. 会有个容器关闭的问题. 所以close容器之前必须先断流.
4. 接口和继承的关系?
流程金字塔,逻辑流程编排通过接口(接口无形参,把参数转成领域属性,不同领域类参数会变. 领域类本质就是封装属性字段). 领域实体必须实现接口, 如果实体之间继承,那么新增领域实体会简单些,但是会存在重载的问题(类似广告里每个广告类型计费方法不同,重载,解决方案是 引入计费接口. 不同的广告类依赖不同的计费接口. )
5. 责任链(before,after切面)的引入进行流程插件化改造,核心不变,新的业务,通过旁路系统或者插件系统引入. 流程编排,通过端上来编排. 每个能力提供扩展点. 类似星环.
一个领域如果有术语就比较简单.
例如: 轮子和框架组合在一起的组装类可以取名为汽车, 还可以继承后变成了小汽车,suv.
这样整个实体的依赖,继承体系就建立了. 轮子可以继承为很多子类,汽车也可以继承为很多子类. suv汽车的很多功能可能是根据各个轮子/轴承的继承组件组合在一起的.使用的功能已经不再是普通轴承的功能(方法)了.
例如会议室预订的领域实体命名.
一个实体如果领域里不能给出具体的术语,就只能自己根据属性来定义名字
例如
会议室预订下面有 会议室和预订列表.
本来纠结了半天 两个都叫 预订,怎么区分?
一种方法是把上面的预订 加个 "组装"词汇. 叫做RoomBookAssembleVo;
另外一个方法是预订item,可以加上重要的时间信息.
Roombook {
Room;
List<BookTimeVo>
}