,我在公司对支付业务、结算业务、资金业务使用DDD进行领域建模的两年,得到了许多好评,也面对过不少质疑,总体来说还是能收获不少,这对团队成员理解业务起着很大作用。近半年一直在研究DDD的落地实战,如今已修得阶段性成果,迫不及待与大家分享我的落地经验。,DDD分为战略设计与战术设计。一般来说,领域建模是属于战略层的,而DDD工程落地是属于战术层的,两者是否结合使用,视实际情况而定,比如传统的MVC架构也能使用DDD进行领域建模,DDD架构最好是先做DDD领域建模。,最新上线的一个微服务——内部交易中心,我们使用了DDD架构来落地,希望看完对大家有启发。,在工程落地之前,我们有必要先了解下主流的工程架构或架构思想都有哪些,对这些理论有所了解的,也可以直接跳过看下一个部分。,,在该架构中,上层模块可以调用下层模块,反之不行。即:,分层作用:,整洁架构(Clean Architecture)是由Bob大叔在2012年提出的一个架构模型,顾名思义,是为了使架构更简洁。,,依赖规则:用一组同心圆来表示软件的不同领域。一般来说,越深入代表你的软件层次越高。外圆是战术是实现机制,内圆的是核心原则。,这条规则规定软件模块只能向内依赖,而里面的部分对外面的模块一无所知,也就是内部不依赖外部,而外部依赖内部。同样,在外面圈中使用的数据格式不应被内圈中使用,特别是如果这些数据格式是由外面一圈的框架生成的。,这样做的最大好处是当系统的外部模块不得不改变时(比如,替换已有的过时的数据库系统),系统的内层模块不需要做任何改变。,六边形架构(Hexagonal Architecture),又叫做端口适配器模式,是由Alistair Cockburn在2005年提出的。,,六边形架构将系统分为内部(内部六边形)和外部,内部代表了应用的业务逻辑,外部代表应用的驱动逻辑、基础设施或其他应用。内部通过端口和外部系统通信,端口代表了一定协议,以API呈现。,一个端口可能对应多个外部系统,不同的外部系统需要使用不同的适配器,适配器负责对协议进行转换。这样就使得应用程序能够以一致的方式被用户、程序、自动化测试、批处理脚本所驱动,并且,可以在与实际运行的设备和数据库相隔离的情况下开发和测试。,作用于限界上下文的菱形对称架构从领域驱动设计分层架构与六边形架构中汲取了营养,通过对它们的融合形成了以领域为轴心的内外分层对称结构。,,内部以领域层的领域模型为主,外部的网关层则根据方向划分为北向网关与南向网关。通过该架构,可清晰说明整个限界上下文的组成:,限界上下文以领域模型为核心向南北方向对称发散,从而在边界内形成清晰的逻辑层次,前端UI并未包含在限界上下文的边界之内。每个组成元素之间的协作关系表现了清晰直观的自北向南的调用关系。,CQRS(Command Query Responsibility Segregation)意为命令查询职责分离,它是一种与领域驱动设计 (DDD) 和事件溯源相关的架构模式。Greg Young在2010年创造了这个术语,CQRS的内容基于Bertrand Meyer的CQS设计模式。,,CQRS架构将写入和读取分开,它提出了单独的 API,一个专用于更改应用程序状态的命令路由,另一个专用于返回有关应用程序状态信息的查询路由。,基于各个架构有其自己的优缺点,我们结合公司的现状,取其长避其短,融合一套适合自己的架构。,,当然,任何事物有其两面性,融合各个框架后,也有其优缺点——,优点:通过分离业务与技术代码,有利于业务迭代升级维护;业务驱动而非技术/数据驱动,通过写代码就能积累一定的业务知识;将领域知识和技术知识分类,从而提高代码的可重用性。,缺点:对从业人员业务分析能力较高,难以从经典MVC架构转变过来;层级较多,写代码前需考虑清楚逻辑应该写在哪一层;规则较多,没有MVC架构灵活,不适用于简单业务系统;学习成本与转移成本比较高,需要对DDD有更好的理解和更长的设计时间(资金组践行DDD领域建模2年)。,,看代码之前我们先看下领域建模:,通过领域模型分析,内部交易中心分为内部调货、规则中心、内部出入库、内部销售、内部采购这五大模块,每一个模块对应DDD就是一个聚合,所有聚合形成一个DDD的限界上下文(内部交易上下文),之前的文章提到,限界上下文就是我们划分微服务的一个重要依据。,接下来,我们结合DDD架构图与领域建模,看看工程代码应该怎么放。,,基于Maven的DDD工程,顶层结构我们按api、service划分为两个module。,api包的作用:,service包的作用:,此外,针对service包还有另一种主流的module划分方式——直接把service包的api、application、domain、infrastructure作为四个独立的module,优点是能通过pom依赖的方式来限制层与层之间的依赖,开发人员能在编码阶段发现依赖问题及时修正,但缺点也明显——不够灵活,工程也会变得较重。,,接入层又叫用户接入层,主流用interface或api命名,基于包默认按字母排序的原因,我建议使用“api”来命名接入层,但要注意,service包的api层与api包是不同的作用。,,应用层主要作用是业务编排、转发、校验等,处理跨聚合、领域事件逻辑,复杂操作/复杂查询也在此层体现(CQRS)。,,领域层或称为模型层,系统的核心,负责表达业务概念、业务状态信息以及业务规则,包含了该领域所有复杂的业务知识抽象和规则定义。,,因为领域建模最终体现在领域层内,在我们建模时就要考虑领域层的代码如何写。,一些难点:,,基础设施层作为工程的基础设施使用,编写与业务无关的代码,如技术框架、工具类,此外还有一个重要的功能,要写仓库的实现类、外部服务的实现类。,我们再来看一下全貌:,,,通过实际案例,总结以下重要几点:,DDD工程落地考虑的是代码的归类划分问题,重点在于业务边界的识别、业务和技术代码的解耦。写代码前需要考虑清楚不同的代码应该写到哪里,结合前人优秀的工程架构思路与公司当前的技术架构,整合一套灵活的、适合我们自己的DDD,不能照搬,更不能为了DDD而DDD。,其实除了常见的充血模型、贫血模型,还有不常用的失血模型、胀血模型,区别如下:,基于现有的Spring框架,以及个人以往的代码编写经验,在代码落地层面还是以贫血模型进行较恰当。,应用服务在应用层,领域服务在领域层,我怎么知道业务代码该放哪里?,应用服务的作用:,领域服务的作用:,其实,难点在于识别业务代码,考验我们对业务的理解程度与思考程度,如果可以显然预料到未来会发生明显的变化,则应该在设计之初更灵活地设计好;如果对未来的变化把握并不清晰或不确定,满足当前业务需求即可。,我们无法避免过度设计还是设计不足,但如果架构合理,代码清晰,改起来成本不会特别大。这里提倡开发者尽量多与领域专家(业务人员或产品经理)沟通,以把握代码未来的走向。,除了常规的简单业务代码,还涉及到复杂业务代码拆分到不同类的问题,最典型的是运用设计模式。,原则上,核心逻辑在哪一层拆就放在哪一层,避免代码散落到各处。,DDD领域建模三大步:划分边界、统一语言、组织模型。,DDD工程落地四大步:整合框架思想、确定划分思路、模型代码映射、特殊代码归类。,以上,从DDD领域建模到DDD工程落地实战已全篇完结,也欢迎大家一起来探讨,如需要DDD工程的Demo也可以联系我。,我相信80%的技术面试官都会对DDD这块感兴趣,如果你也掌握了DDD,其实就多掌握了一种面向RMB编程。
文章版权声明
1 原创文章作者:cmcc,如若转载,请注明出处: https://www.52hwl.com/21025.html
2 温馨提示:软件侵权请联系469472785#qq.com(三天内删除相关链接)资源失效请留言反馈
3 下载提示:如遇蓝奏云无法访问,请修改lanzous(把s修改成x)
4 免责声明:本站为个人博客,所有软件信息均来自网络 修改版软件,加群广告提示为修改者自留,非本站信息,注意鉴别