Bootstrap

DDD实战(1):从需求到代码实现生鲜电商系统

缘起

开始之前,我想说的是:感谢您在百忙之中,还有兴趣看一位老程序员的分享,尤其是还有点唠叨!

近几年随着云原生技术的发展,微服务如何拆分的问题,越来越成为企业应用架构设计中最为重要的设计决策之一。我在实际工作中,时常碰到客户提出的疑惑:微服务到底要“微”到什么程度才算好?用了微服务架构后,真的能减少业务模块之间的相互耦合、进而提高系统整体可用性吗?为什么我们拆了微服务后,系统反而不如之前的单体应用稳定?不但如此,拆了微服务后,每次需求变更比以前更复杂,不可预测的连带BUG持续出现......等等等等。

基于这样的客户诉求,我开始学习DDD设计方法。但随着学习的深入,越来越发现软件开发团队要落实DDD设计方法并不是一件容易的事。DDD方法鼻祖Eric的经典著作《领域驱动设计 软件核心复杂性应对之道》、以及Vaughn的《实现领域驱动设计》都写得比较抽象,很难落地实施。DDD的难落地程度,导致了程序员圈子甚至出现了一种看法:本来会写代码的,学了DDD后反而不会写代码了——也就是俗称的“学废了”。

为了能够让软件开发团队切实的落实DDD,我决定自己亲身实践一次用DDD设计开发一个实际运行的系统,以便于在实际开发过程中积累相关的经验,进而能够将来指导开发团队落地DDD方法。很显然的,该实例系统要求既不能太简单以至于没有参考价值、也不能太庞大以至于我一个人在最多半年的业余时间内都搞不定。为此,我选择了本人之前做的一个“群买菜”生鲜电商小程序(你可以从微信小程序搜索到它)系统作为本专题的样例。因为该小程序的产品设计也是本人做的,所以比较熟悉也比较容易操刀。而且该系统也在实际应用过程中,也发现时不时的有业务需求变更,往往因为因为当时没有做架构设计、直接就开始事务脚本性质的编码(在本专题后面章节将会看到),所以导致变更需求也非常困难。为此,于公于私,我都应该将“群买菜”用DDD方法重构一遍设计。

在实际开始之前,正好我也看了张逸老师的一本书《解构领域驱动设计》。在该书中,张逸老师体系化地将自己理解的DDD很多方法和实践进行了梳理,并给出了自己的一套标准工作流程——DDDUP(领域驱动设计统一过程)。因此,在本专题后续的内容中,我将主要基于张逸老师的DDDUP来进行操作。在这里,对张逸老师致以深深的感谢!

同时,因为“群买菜”实际上还是一个相对比较大的系统,且DDD其实主要适用于大型软件应用系统的开发,所以我也引入了scrum敏捷开发过程,对整个“群买菜”系统分为5~6个sprint(冲刺)来执行。

本质上,这个专题是我个人在实践DDD过程中的一些提炼和分享,不能算是培训教材,也不能算是专家经验,只能说是一个热爱分享的程序员对这个世界的输出吧!大家也不要当做这个专题为学习,而当做和我一起经历一次完整的DDD需求分析、架构设计、编码实现的旅程吧!

目标读者和预期收获

本专题的目标读者,主要是提升自己在应用软件架构设计方面能力的熟练程序员、开发组长、技术经理等。作为熟练的程序员,你应该已经满足以下列出来的一些要求:

  • 精通java开发,尤其需要熟悉spring-boot的开发;

  • 精通1~2个以上关系数据库和非关系数据库技术,如:mysql/oracle、redis/mongodb等;

  • 有过3年以上应用软件的实际coding经验、有过带领3人以上程序员小组的开发经验;

  • 对常见的设计模式比较熟悉,在实际项目的coding中 ,至少熟练使用过3种以上设计模式;

  • 对架构分析模式有所了解,在实际需求分析中,至少使用过1种以上分析模式;

  • 最后一条,也是最关键的,作为工程师,对学习新技术新方法有足够的动力,认可“加班就应该加在提升自身的稀缺性上”(这个理念来自于刘润老师)的理念,迫切希望成为架构师、或更优秀的架构师,有志于朝着技术线的资深专家(非管理线)发展;

下面说明经历本专题旅程后,您能得到什么。准确来说,我祝愿您能获得如下的收获:

  • 能够理解DDD从需求分析、到架构设计、到编码实现的整个过程,以及其中的工作方法和实用技巧。我们这里所说的“理解”,指的是你能够在自己的团队内部分享、培训、甚至引导团队在项目中使用DDD。

  • 本专题还将实实在在建立一个全开源的“群买菜”生鲜电商系统,这个电商系统你将可以通过github或gitee获得全部源代码。你可以将这些源代码用于自己的任何用途,只需要遵守Apache开源许可证协议即可。

注(内容会随着专题进展而逐步完善):

  • github代码仓库访问地址:

  • gitee代码仓库地址:

DDD能解决什么,不能解决什么?

根据我个人的项目经验、以及实际做DDD设计编码的感受,如果你在项目中遇到下面这3种情况,就考虑可以试试用DDD来解决问题了:

  • 频繁的业务需求变更导致对系统稳定性的影响不可控。需求修改频繁,而感觉每次修改涉及的范围千头万绪,不透明、不可控,经常因为需求给原来的功能带来不可预测的BUG。这个问题,其实可能代表着两类需求(视软件产品化程度而不同):

  • 对于项目定制化软件,希望随着业务的发展,业务需求变更只会引起可控的、小范围的变更;

  • 对于产品化程度较高的软件,希望随着软件产品的发展,每个产品组件可相对独立地演进;

  • 30人以上大项目团队职责划分很纠结。项目很大,团队怎么划分,感觉没有科学依据,凭直觉划分后,总发现一些不可控的耦合或重复开发;

  • 微服务怎么切分很纠结。云原生技术的发展,微服务切分的粒度无法把控。太粗没有意义,太细了又导致运维复杂失控;

上面说的DDD比较适合解决问题的场景,当然另一面就是DDD也有不适合解决的问题场景。我个人总结了一下,觉得下面这些问题是DDD不能解决的:

  • DDD不能帮助团队决策如何更好的技术栈

  • DDD不能解决系统的性能相关的瓶颈问题;

  • DDD几乎不涉及前端UI的设计方面;

  • DDD不能帮助产品经理提升产品设计水平(虽然产品经理了解点DDD也有帮助),更不能代替产品经理做产品设计。因为产品设计更多是牵涉到心理、商业等非工程化思维,而DDD仅能解决软件在工程化方面的问题;

  • DDD不会手把手的教你如何提升需求分析相关软技能,包括:如何和客户沟通需求、如何画业务流程图、如何识别业务用例、如何学习新的业务知识等。虽然它确实给出了一套相对有参考价值的“硬性”方法框架,但未给出任何关于个人如何提升这些“软”性技能的实际建议,它假设你自己去建立和发展这些技能;

  • DDD不会手把手的教你如何提升软件架构设计相关基础知识和软技能,包括:如何区分哪些逻辑应该放前端实现、哪些放后端实现、前端目前主流技术框架有哪些、后端主要开发技术栈有哪些等等。虽然它确实有一套相对有参考价值的“硬性”方法框架,但同样未给出任何关于个人如何提升这些“软性”基础知识的实际建议,它假设你自己去建立和发展这些技能;

  • DDD不能解决程序员个人自身的代码质量和规范性问题。虽然它确实能够很大程度上解决代码目录结构、模块划分的规范性问题,但解决不了程序员自身编程水平问题;

看我这里的介绍,看起来DDD能解决的问题很有限、而不能解决的问题却很多!我想要说的是:“是的,DDD并不是万能药,几乎不能解决软件项目中遇到的绝大部分问题!”要知道,即使计算机发展到今天、很多冯.诺依曼体系架构下的开发技术已经发展到了极大丰富,但“需求分析、架构设计(往往同时决定了团队结构)、面向对象”这些技能,仍然是程序员圈子的高端技能,一个类似于DDD这样的方法体系,能够解答这些高端技能相关的核心问题,已经是很了不起的方法论。

所以说,无论怎样,DDD都可以说是一个“有点伟大”的软件设计方法论!虽然,网上很多人都在说DDD有很多“伪创新”,但无论“伪”不“伪”,DDD都是随着云原生微服务发展而迅速被重视的、也确实很有帮助的一套方法论。

能用一句话解释DDD核心理念吗?

根据我个人对DDD方法论的实践,让我最能记住的其核心理念,就两个字:同构!

说到“同构”,这跟我个人经常困惑的一个问题息息相关:既然代码世界是现实世界的一个“虚拟”映射,为什么我们在现实业务中,人们的直觉认识往往觉得某个业务其实没有太大变化,但是到我们的实际代码中,却要引起“伤筋动骨”甚至“开天辟地”般的变化呢?最后导致新需求的实现,要么被扭曲了不伦不类、甚至放弃业务需求,要么就付出巨大的建设成本、难以忍受的建设周期呢?

我个人理解,这其实是因为我们用代码实现的“虚拟世界”、跟真实业务的“现实世界”不“同构”导致的。说得粗俗一点,就是可能我们真实世界是“六边形”的,但被我们的软件团队映射成了“三角形”的“虚拟世界”,也就是产生了所谓的“异构映射”。那这样的“异构映射”是怎样产生的呢?有句话说得很好:正确的过程才能导致正确的结果、错误的过程一定导致错误的结果。这其实就是我们的软件需求分析师、架构设计师、程序员合起来对现实业务的“解构”错误导致的!

DDD的核心理念,其实就是试图给出一套方法框架,告诉我们的需求分析师、架构设计师、程序员怎么去尽最大可能地将“真实物理世界”同构化映射为“虚拟代码世界”

本专题解答什么?

说了这么多,我想你可能会问:如果我决定开始学习DDD,你废话这么多的这个专题,到底能解答我哪些问题?

我想,我试图在这个专题中,回答下列的问题:

本专题编写风格

为了方便你评估本专题是否值得继续看下去,我这里澄清下自己的编写风格,以供你选择是否继续——当然,如果你继续赏光阅读我下面的内容,我会非常感谢!也非常乐于随时与您交流你发现的我的错误、或其它任何您觉得需要讨论的问题。

  • 我会尽可能的将理论体系浓缩和简化

  • 我会结合实际的软件实现案例(并且该软件的开源代码实现一定是可运行的)来讲解。在案例中贯穿DDD相关的方法、原则和技巧,而几乎很少大篇幅的介绍理论本身;

  • 我可能会写得比较啰嗦,尤其是一些技术实现细节方面比较抠,希望您能够有耐心。因为我自己是个很笨的人,学习一个东西的速度很慢,而且我一向希望自己能够“但求甚解”,而不是“不求甚解”。我个人一直秉承“慢就是快”的理念,所以还请您稍微忍受这一点;

  • 我会每周更新一篇,整个专题可能会有20~30篇。除了第一篇的大量没有太多技术含量的“废话”外,后面的每一篇,可能需要您每周花费2小时左右去阅读(1小时)和理解(1小时)。所以,还是上一句话说的:需要您有点耐心。