复杂度来自哪里
扩展性
因为我们大部分的系统都是从单一业务开始的。但是随着支持的业务越来越多,代码里面开始出现大量的if-else逻辑,这个时候代码开始有坏味道,没闻到的同学就这么继续往上堆,闻到的同学会重构一下,但因为系统没有统一的可扩展架构,重构的技法也各不相同,这种代码的不一致性也是一种理解上的复杂度。久而久之,系统就变得复杂难维护
面向过程
- 一个是很多同学不了解SOLID原则,不懂设计模式,不会画UML图
- 不会进行领域建模,
DDD最大的好处是将业务语义显现化
分层不合理
- 分层最大的好处就是分离关注点,让每一层只解决该层关注的问题,从而将复杂的问题简化,起到分而治之的作用。
- 我们的原则是不可以没有分层,但是只分有必要的层。
随心所欲
- 缺少规范和约束
复杂性对应之道
- 扩展点设计
- 面向对象
- 领域建模
- 分层设计
- 设计规范
扩展点设计
- 业务身份识别:多租户(TenantId) + 业务码(BizCode) + 扩展点(Extension)
- 抽象扩展点机制:所有的扩展点(ExtensionPoint)必须通过接口申明,扩展实现(Extension)是通过Annotation的方式标注的,Extension里面使用BizCode和TenantId两个属性用来标识身份
面向对象
- 单一职责原则(SRP)
- 开闭原则(OCP)
- 里氏替换原则(LSP)
- 接口隔离原则(ISP)
- 依赖倒置原则(DIP)

领域建模
- 是一种知识丰富的设计(Knowledge Rich Design)从而增加代码的可理解性
- 这里的领域核心不仅仅是业务里的“名词”,所有的业务活动和规则如同实体一样,都需要明确的表达出来。
好处
- 面向对象
- 封装:Account的相关操作都封装在Account Entity上,提高了内聚性和可重用性。
- 多态:采用策略模式的OverdraftPolicy(多态的典型应用)提高了代码的可扩展性。
- 业务语义显性化
- 就是将隐式的业务逻辑从一推if-else里面抽取出来,用通用语言去命名、去写代码、去扩展,让其变成显示概念,比如“透支策略”这个重要的业务概念,按照事务脚本的写法,其含义完全淹没在代码逻辑中没有突显出来
领域事件
因为在现在的分布式环境下,没有一个业务系统是割裂的,而Messaging绝对是系统之间耦合度最低,最健壮,最容易扩展的一种通信机制。因此理论上它是分布式系统的必选项。
- 命名:Domain Name + 动词的过去式 + Event 如 CustomerCreatedEvent
- 内容: 丰富:Event的payload中尽量多多放data,这样consumer就可以自恰
- Event Sourcing: 要有一个Event Store保存所有的Events
聚合根(Aggreagte)
把一组有相同生命周期、在业务上不可分隔的实体和值对象放在一起考虑,只有根实体可以对外暴露引用,也是一种内聚性的表现

领域服务
- 有些领域中的动作,它们是一些动词,看上去却不属于任何对象。它们代表了领域中的一个重要的行为,所以不能忽略它们或者简单地把它们合并到某个实体或者值对象中。
- 当这样的行为从领域中被识别出来时,最佳实践是将它声明成一个服务。
- 这样的对象不再拥有内置的状态。它的作用仅仅是为领域提供相应的功能。Service往往是以一个活动来命名,而不是Entity来命名。例如开篇转账的例子,转账(transfer)
识别服务
- 服务执行的操作代表了一个领域概念,这个领域概念无法自然地隶属于一个实体或者值对象。
- 被执行的操作涉及到领域中的其他的对象。
- 操作是无状态的。
服务划分
- 操作是关于领域对象的,而且确实是与领域有关的、为领域的需要服务,那么它就应该属于领域层
边界上下文(Bounded Context)
- 领域实体是有边界上下文的,比如Apple这个实体不同的上下文,表达的含义就完全不一样,在水果店它就是水果,在苹果专卖店它就是手机。
- Bounded Context明确地限定了模型的应用范围,在Context中,要保证模型在逻辑上统一,而不用考虑它是不是适用于边界之外的情况。
上下文映射
- Shared Kernal(共享内核)
- Conformist(追随者)
- Anti-Corruption(防腐层)
会员这个概念在ICBU网站是指网站上的Buyer,但是在CRM领域是指Customer,虽然很多的属性都是一样的,但是二者在不同的Context下其语义和概念是有差别的,我们需要用AC做一下转换
边界上下文和微服务 抛开以Docker为代表的底层容器化技术不看,微服务和我们之前的SOA么有本质区别。

模型重构
- 模型统一:模型和业务不匹配时候,需要一个新的抽象
- 模型演化:
- 实体在演变:
- 新功能新建接口,让其继承老接口,这样老的代码就可以保持不变了
- 新功能,通过判断区分
- 引入新的抽象:
- 实体在演变:

业务可视化配置化
- 业务逻辑流: 业务逻辑流是响应一次用户请求的业务处理过程,其本身就是业务逻辑,对其编排和可视化的意义并不是很大
- 工作流:完成一项任务所需要不同节点的连接,节点主要分为自动节点和人工节点,其中每个人工节点都需要用户的参与,也就是响应一次用户的请求,比如审批流程中的经理审批节点,CRM销售过程的业务员的处理节点等等
分层设计
- App层主要负责获取输入,组装context,做输入校验,发送消息给领域层做业务处理,监听确认消息,如果需要的话使用MetaQ进行消息通知;
- Domain层主要是通过领域服务(Domain Service),领域对象(Domain Object)的交互,对上层提供业务逻辑的处理,然后调用下层Repository做持久化处理;
- Infrastructure层主要包含Repository,Config和Common,
- Repository负责数据的CRUD操作,这里我们借用了盒马的数据通道(Tunnel)的概念,通过Tunnel的抽象概念来屏蔽具体的数据来源,来源可以是MySQL,NoSql,Search,甚至是HSF等;
- Config负责应用的配置;
- Common是一写工具类;负责message通信的也应该放在这一层。

这里需要注意的是从其他系统获取的数据是有界上下文(Bounded Context)下的数据
为了弥合Bounded Context下的语义Gap
- 用大领域(Big Domain)把两边的差异都合起来
- 增加防腐层(Anticorruption Layer)做转换
规范设计
- 放对位置
- 贴好标签:命名规范
- 方法名约定
- 错误码约定
- Domain Event约定
- 测试约定

整体目标
- 思想:高内聚,低耦合,可扩展,易理解
- 架构:扩展点+元数据+CQRS+DDD

