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