内容简介:领域驱动设计最近又火了。概念不断被提及,但是相信对于像笔者一样的很多开发者对于其如何应用都一头雾水。正如《实现领域驱动设计》中作者提到的不同公司的业务能力开发能力和成熟度不一样,
说明
领域驱动设计最近又火了。概念不断被提及,但是相信对于像笔者一样的很多开发者对于其如何应用都一头雾水。
正如《实现领域驱动设计》中作者提到的不同公司的业务能力开发能力和成熟度不一样, DDD为了解决复杂业务为生,并不适合所有的软件项目, 对于很多初创公司而言,业务本身就是模糊的,只是需要做出一个MVP(最小可行性产品)来试探商业模式 ,采用ddd显得过“重”了一点,反而给团队成员带来额外的负担,所以 团队 管理者首先应该关注的是软件系统是否值得做出DDD投入 。
不过不管黑猫白猫,能抓到老鼠就是好猫。 我们以电商业务来演练和实践ddd的部分理论,并解释ddd的概念。
战略设计阶段
领域:即业务是属于哪块,电商领域,保险领域,零售领域,又可细化分为子领域。如电商下(订单交易领域、库存领域、会员领域、物流领域....)
领域专家:一般指熟悉对应领域的产品经理项目经理
子域:子域可细分为核心子域、通用子域和支撑子域,简单理解为哪部分是比较核心的就可称作核心子域,哪些功能偏边缘化叫支撑域。
互联网公司的敏捷开发模式的产品研发流程从收集需求、PRD评审、技术模块拆分这些前期流程。而战略设计即在这个阶段完成,顾名思义侧重于从宏观上对业务进行拆分,和对未来走向的预测。
DDD的目的是为了领域专家更好地与开发进行沟通合作,使得代码更好地传达业务规则。
最初的需求方可能会提一些凌乱的需求。
为了更好地传达规则, 领域专家将需求整合提取出领域的概念, 技术/项目管理者一起划分好子域和限界上下文。各方统一所谓的 通用语言 并在以后的协作中使用 。比如电商业务中双方约定好中商家用户和买家用户的概念,用户和账户的概念,交易订单和支付订单、物流订单、售后订单的概念,以实现后期沟通顺畅。
最终产出:划分出了哪些子领域、上下文映射图是怎样的。
贴一个电商业务的上下文映射图(下单交易上下文为下游,其他皆为上游):
而各个上下文的交互方式,即 集成限界上下文 的方式其实就是我们常说的系统交互方式:RPC调用、REST接口调用、消息队列通信。
怎么理解限界上下文和子域的关系?
限界上下文:是一个显示边界,领域模型即存在于这个边界之内。在边界内,通用语言有特定明确的意义。
ps:是不是觉得每个字都认得,但是不知道表达了什么意思....
在理解限界上下文和子域上确实有点费劲。笔者当时的疑惑主要是:为什么要用上下文映射图而不是子域交互图,子域划分出来不就说明边界已经明确了么,为什么还专门搞一个限界上下文的概念。
关于限界上下文,贴一下个人理解:
接下来要咬文嚼字一些了。
1、从“限界”二字来说。限界上下文明确了业务范围和职责边界。针对上面问题“子域中不是已经有边界的概念了么”。可以思考一个有意思的事,子域有边界,还是说因为有了边界才有子域。听到过一个非常到位的类比: 如果没有细胞壁,如何定义细胞质 ?
2、从“上下文”来说。上下文关注的是两个系统交互时的环境,或者说语境。
举个例子:小学是一个子域,中学是一个子域。升学这个事件动作则要上下文表达。
通用语言要在限界上下文(语境)中保证其明确意义。举个例子,商家管理上下文中,我们(平台)说的用户指的是商家而不是买家,支付上下文中,我们以支付单为核心,语境无需引入物流单、库存等词汇。
通常来说,我们可以近似地认为子域和限界上下文一一对应的。
战术设计阶段
截止到此,我们已经划分好了子域,对开发人员来说,已经拆分好了项目。各个团队可以针对自己的子域进行独立开发了。对于ddd而言,我们开始进行战术设计阶段。战略设计关心做什么,战术设计则更关心技术实现细节,即:怎么做。
先说下ddd中推荐的 六边形架构 :
这里要吐槽一下六边形架构这个命名,搞得好像有六个什么东西一样。其实只是根据视觉形状起的名。现在叫端口与适配器架构。
转换一下是这样的
严格分层架构:某层只能与直接位于其下方的层发生耦合
松散分层架构:允许上方层可以与任意下方层发生耦合。
这里采用松散分层架构。也可按依赖倒置原则, 基础层依赖领域层的一些东西(常见的就是实体Entity)
适配器层:
负责接口转换,即是最外层请求处理类,将外部请求转化为内部API能理解的输入。对于REST接口可能是一个controller类;
对于dubbo调用来说是开放出去的provider服务类;
对于grpc调用来说,是protobuf请求对象转换处理类;
对于消息机制来说,对应的是消息的监听器
如此采用端口适配器模式,可以不影响内部服务,只需在适配器层进行增改。尽管大家不知道六边形架构的定义,但相信很多人是这样做的。无须赘述。
应用层:负责协调领域层的接口实现前端展示或返回需要。
领域层:定义领域实体和逻辑。包括实体、值对象、领域服务、领域事件、资源库。
基础层:如数据库相关。
实体和值对象
有唯一业务标识
有自己的业务属性和行为
属性可变,有自己的生命周期值对象: 可以有唯一业务标识
有自己的业务属性和行为
一旦定义不可改变
二者的关系可总结为: 值对象关心对象是什么样的,实体侧重描述对象是哪个?
提到有自己的业务属性和行为这块,想想这不就是我们年轻时说的面向对象编程的思想码?
但是回顾一下会发现,这个思想好像被很多人抛诸脑后很久了,定义的对象类都成了一个个的pojo,只有属性和属性对应getter和seter方法,也就是ddd中所说的 失血模型。
失血模型:只含属性和对应的getter/setter方法,无业务处理逻辑
贫血模型:包含不依赖持久化的部分领域逻辑,依赖持久的逻辑被放在领域服务层。
充血模型:绝大数业务逻辑都放在其中,包括持久化逻辑。少数不适合的逻辑被提取出来放在领域服务层中。
胀血模型:主张不需要领域服务层,把一些业务逻辑都放在模型对象中处理
领域服务
胀血模型主张把所有的业务逻辑放在模型中处理,但是事实上有些逻辑并不是适合放在某个领域对象中处理。比如
1、领域对象间的转换
2、某些场景下需要多个领域对象作为输入值,结果产生一个值对象。
区别于应用层的服务,领域服务处理的是业务逻辑。应用层负责对领域服务处理结果进行渲染和组装返回给前端。ps: 针对查询类的操作,建议作为应用层的查询服务单独拎出来,因为查询尝尝涉及到多个维度的查询,或把多个领域对象的查询结果组装成一个返回值对象。
举个栗子:订单领域需要向商家(PC端)和买家(APP端),商家端按发货条件时间查询所有买家的订单,操作发货处理售后等流程。买家端完成下单、查询个人订单。在应用层我们抽象三个应用处理出来,公用的查询应用、商家端应用(关联商家权限控制上下文)、买家端应用(关联买家权限控制上下文)。
领域事件
领域事件即领域业务周期中一些关键行为,关键的定义是其他地方需要依赖此事件推送业务流转。如订单被支付这个事件,需要触发库存扣减、商家待结算账户余额增加等操作。关于领域事件处理方法:跨子域处理常用消息队列发布/订阅模式,同项目处理常用注册事件监听器处理。不多描述。
聚合和资源库
领域对象之间常常有依赖关系。比如主订单-子订单(商品)项,子订单依附于主订单存在,二者的关系称作 聚合 ,主订单作为 聚合根。
@Data
public class TradeOrderEntity {
//订单号
String orderNo;
// 商品详情
List<OrderItemDetailEntity> orderItemDetails;// ...
}
我们通过为每一个聚合选择一个根,并通过根来控制所有对边界内的对象的访问。 外部对 象只能持有根的引用; 由于根控制了访问,因此我们无法绕过它去修改内部元素。所以在ddd中,资源库Repository 是面向聚 合根操作的,可对多个dao对象 的组合使用。
@Repository
public class TradeOrderRepository {
@Autowired
TradeOrderMapper tradeOrderMapper;
@Autowired
OrderItemDetailMapper orderItemDetailMapper;
}
关于其中一些思想,笔者也没有想清楚,所谓知行合一,恐怕很多方法论要真的遇到问题了才会理解其价值。先整理到这。最后针对 Java 后端开发同学,提供一个模块分层的框架供参考
思考
无忌,我教你的还记得多少?” “回太师傅,我只记得一大半”
“ 那,现在呢?” “已经剩下一小半了”
“那,现在呢?” “我已经把所有的全忘记了!”
“好,你可以上了…”
END
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 后端开发实践系列——领域驱动设计(DDD)编码实践
- 领域驱动设计框架 Axon 实践
- 推荐系统在房产领域的实践
- 公共安全领域 Kafka 应用实践
- 领域驱动设计(DDD)实践之路
- 【语言模型系列】实践篇:ALBERT在房产领域的实践
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。