企微助手DDD模式探索

一、前言

DDD 这个大家耳熟能详的名词,近些年来越来越出现在开发的视线中,随着功能的不停迭代,功能不停的累加,代码的堆砌,随之带来的功能复杂度提高,代码的维护和再开发的成本不断增加,正好借着这次 WOS 系统的升级改造,企微助手完成了对代码 DDD 架构的升级。

企微助手没有100%完全照搬 DDD 的落地实现方式,重点对业务本质进行了建模,明确了业务与技术之间的统一语言,培养优秀的技术业务专家,沉淀业务白皮书,定义后续业务研发设计标准,明确产研间沟通标准,提升研发效能。而对于 DDD 中一些较为抽象与复杂的落地,进行了大幅度的精简,保证代码简单与高效。下面我们简单的介绍一下我们的做法。

二、DDD是什么

2.1简介

全称叫:Domain-Driven Design,中文名称叫:领域驱动设计。是一种解决复杂问题的软件开发方式。

2.2基础知识

首先在正式使用 DDD 前,我们需要对 DDD 的以下知识有一个基础的认知。

2.2.1领域与子域

DDD 的核心就是领域模型,而领域就是对业务范围的划分,用来限定业务的边界,在这个范围内的业务问题的处理集合就是领域。

同时领域是一个业务的范围,那么这个范围就可大可小,那么一个大的领域就可以被划分为多个小的子域,同时子域按照需要还可以拆分为多个更小的子域。这就是 DDD 领域划分的一个重要思想,通过分治将复杂问题简单化。

其中子域也有核心子域、支撑子域和通用子域,顾名思义,这些不同的名词用来区分子域的不同作用。

2.2.2限界上下文

提到限界上下文,就不得不先介绍通用语言,通用语言存在于 DDD 的整个设计过程,通过与限界上下文的协同,来保证对象在边界内的唯一含义,通过通用语言我们可以将业务模型与系统模型映射起来。

那么限界上下文的作用就是“限界”(具体的领域边界)与“上下文”(提供业务所在的上下文环境),在这个环境中内可以使用通用语言进行交流,保证环境内的一切术语与对象没有二意性。

举个例子,一件商品,在销售的时候,它就是一个商品,在卖出后运输过程中,它就是一件货物,由此可见在不同的业务边界内,通用语言就有了不同的含义,但是再加上了这个界限(销售/运输)后他的语意就是统一的。

2.2.3实体与值对象

实体与值对象是构成领域模型的最基础的对象,但是两者又有一定的区别。实体是一个具有唯一标识 (ID) 的对象,只要唯一 ID 不变,无论属性怎么变化,它都是同一实体。值对象是通过属性值来识别的对象,它是一个关联属性的集合,并且没有唯一标识。

举个例子,在用户域中,用户对象,是一个实体,因为不同的id代表不同的用户,然后用户有地址(省、市、区、具体地址)相关的属性,那这个地址就是值对象,因为不同的地址只有值不一样才能表示不同对象。

2.2.4聚合

聚合其实就是由一些在业务上紧密相连的实体与值对象组合而成的,同时为了保证聚合内的数据操作都遵照聚合内的业务规则执行,聚合对数据的修改必须由聚合根统一组织。那什么是聚合根呢?聚合根是这个业务聚合内所有的所有实体与值对象的管理者。负责在聚合内协调实体和值对象,以统一的业务规则操作数据。

各个聚合之间以聚合根 ID 互相关联,应用服务在需要多个聚合之间协同的时候,通过聚合根 ID 作为参数进行跨领域的服务调用。

所以我们在领域建模过程中一般的顺序:找到聚合根->建立聚合->划分限界上下文->建立领域模型。这里还不是很清晰,下面的实战我们会有更清晰的认知。

2.2.5仓储模式与工厂模式

这两个就比较简单了,是为了配合聚合而产生的设计模式,为了聚合数据的初始化与持久化。

仓储模式顾名思义就是将各类 DB 的操作与业务实现逻辑进行隔离,是聚合与数据之间薄薄的一层,使得聚合可以聚焦于业务的处理。

工厂模式的存在是为了隐藏复杂对象的创建逻辑,从而将领域内对象(尤其是聚合根)的使用与创建进行分离,使得聚合根专注于领域模型,更加清晰。

2.2.6实战

本文就以“企微-获客服务-裂变活动”为切入点,展示整个设计过程。

第一步、事件风暴 首先我们需要召集所有的参与人员,通过头脑风暴把领域事件(业务行为)都贴到墙上(使用不同的贴纸)。对于新的业务可以参考以下思路:

事件风暴思路

因为我们是重构 WOS ,业务已经非常清晰了,所以可以直接确定业务的所有事件:

领域事件集合

第二步、场景分析 通过用例与场景分析,探索出典型场景:

裂变场景分析

第二步、领域建模

聚合过程

从图中我们可以发现客户与奖品在不同的领域内的表示不同,因为在不同的限界上下文内对象含义不同,比方说客户在客户域中就是聚合根是最基础的所有数据的聚合,但是在活动域中只是一份数据的拷贝,所以只是一个值对象。

通过对各个业务的讨论,最终我们通过一次次的分析,确定了限界上下文的同时也确定整个领域中各个子域的实体与能力,划分清楚了所以的领域,然后再次进行整合(比方说客户-通用域,就是在各个子域的分析后完善了领域的最终能力)。

最后我们就可以确定整体业务限界上下文了

企微领域图

2.3 进阶使用

通过上面的一些理念我们已经实现了“高内聚”的设计,那么如何做好“低耦合”呢,就需要接下来的一些设计方式。

2.3.1领域事件

通过领域事件我们可以将领域进行解耦,可以通过消息的发布-订阅方式来实现。

随之而来,带来了一个问题,就是如何保障多个聚合数据的一致性问题。具体的实现方案需要具体考虑业务,至于是保障最终一致性还是通过事务来保障强一致性,不同的业务有不同的选择。

企微助手舍弃了领域事件通过消息的发布-订阅方式实现,通过在 domain service(领域服务)层定义核心领域业务能力的方式,由 service(业务)层进行领域服务的编排,进行了简化处理。

2.3.2六边形架构

六边形结构

在六边形架构中我们可以看到一个重要的概念就是适配器,将所有与外部系统的交互通过适配器进行隔离。

2.4架构思路(参考)

参考以上的理念我们对原有结构进行了一次升级,根据自身需要我们制定了以下架构。

系统架构图

2.3.4 总结

DDD 的存在是为了使用统一的通用语言,消除开发间的沟通障碍,使得领域专家、设计人员、开发间业务沟通更加高效,同时消除业务壁垒。

DDD 设计出来的微服务,业务与应用边界十分清晰,同时也符合“高内聚,低耦合”的设计原理。

DDD的部分实现比较复杂,概念较多,建议落地实现进行简化处理,重点关注业务模型的建设,限界上下文的梳理,业务边界的定义以及统一语言的形成。

三、过程中产生的问题与思考

1.关于什么时候需要走领域,什么时候可以应用层直接调用仓储?

通过领域是为了使用统一的操作,来解决复杂的操作问题,所以在规定的限界上下文内,所有对数据的操作都应该由领域来操作,但是由于查询的多样化,以及查询的效率,再结合人员对 DDD 的理解成本,为了统一开发习惯,对数据的操作必须通过领域才能调用仓储,而查询可以直接通过应用调用仓储。

2.确定领域后如何划分微服务?

最暴力的划分方式就是一个领域就是一个微服务,但是也要考虑到成本以及效率的问题,可以将特别复杂并且高频的业务单独设计微服务,同时结合功能模块的划分,将一些关联性强且使用频率不那么高的业务整合为一个微服务。

3.关于应用服务与领域服务能力的区分?

应用服务:在领域服务之上,处于接口层与领域层中间,主要负责协调多个领域完成服务的组合与编排。领域服务:主要实现限界上下文内,实现单一服务的核心逻辑。

4.任务编排与消息通知何时使用?

对强依赖的业务走任务编排,比如订单依赖支付结果。对一些弱耦合的业务关系可以走消息,比如支付后的积分发送。

作者:孙韩越

来源:微信公众号:微盟技术中心

出处:https://mp.weixin.qq.com/s/mXUqYjnWBieRBkM5JSUCaw

展开阅读全文

页面更新:2024-04-29

标签:限界   上下文   边界   实体   助手   对象   领域   语言   模式   事件   业务   数据

1 2 3 4 5

上滑加载更多 ↓
推荐阅读:
友情链接:
更多:

本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828  

© CopyRight 2008-2024 All Rights Reserved. Powered By bs178.com 闽ICP备11008920号-3
闽公网安备35020302034844号

Top