/ddd-demo

MVC【以mybatis-plus为orm框架】迁移至DDD的demo

Primary LanguageJava

本项目是为了适配掘金小册:《深入浅出DDD》讲解的演示项目。

为了能够更好的理解Demo中的分层与逻辑处理,我强烈建议你配合小册来深入了解DDD。

你可以通过以下方式联系我,进入DDD讨论群

  • 微信号:baiyan_lou
  • 微信公众号:柏炎大叔
  • 掘金

欢迎大家给Demo提出Issue

如果你觉得项目还不错,给个Star吧~

一、为什么我们要使用DDD

我相信基本上 99% 的 Java 开发读者,不管你是计科专业还是跨专业出身,初学 Spring 或 SpringBoot 进行 Web 开发的时候,接触到的代码分层都是 MVC 架构。

MVC,全称 Model View Control(模型-视图-控制器),其分层定义如下。

  • M(模型层/DAO层) :业务数据载体层。

  • V(视图层/Controller层) :展现给用户的数据表示层。

  • C(控制层/Service层) :接受 V(视图层)传递过来的请求进行业务逻辑处理,并将处理后的 M 层(模型层)数据返回给 V(视图层)。

因为 MVC 对于刚刚接触 Web 开发的同学来说有它自身独有的优势:

  • 分层简单易懂;

  • 层级逻辑替换方便;

  • 可以降低层与层之间的依赖;

  • 有利于标准化;

  • 利于层与层逻辑的复用。

但真实情况是这样吗?随着系统功能迭代,业务功能越来越丰富之后,控制层里面对于业务逻辑处理的代码也越来越多,维护成本也越来越高

举个例子:你负责了一个 MVC 项目从 0 到 1 的搭建,后面业务越来越好,招了新的研发一起迭代需求。在版本迭代的过程中,Service 层逻辑方法类似又不完全相同。A 同学拷贝了你的代码,改了一小段逻辑,开了一个新的方法。B 同学看到两个类似的逻辑,偷偷在里面加了一个 if/else 的判断。C 同学再按照自己的理解去写了一个已经存在的逻辑……

那么,随着时间的推移,你会遇到什么样的工程问题呢?

  • 工程体积庞大;

  • 需求迭代会出现明明是类似的功能,却无法复用代码逻辑的情况;

  • 重复的代码很多;

  • 大类(几千上万行)随处可见;

  • 出了 Bug 不敢直接修复,只能继续往上贴逻辑;

  • 代码可读性很差,不加注释你都难以理解业务逻辑;

  • 功能之间耦合严重,改了一个 Bug,莫名其妙又出现了好几个其他的 Bug;

  • 外部系统 RPC 接口修改,大范围影响到本系统的逻辑;

  • ……

归根到底的原因是什么?

控制层就像个万能容器,什么代码都往里面写,承载了它这个年纪不该承受的业务逻辑。反观模型层与视图层,空空如也。业务逻辑不是跟着业务模型走的,而是在现有数据模型的情况下,或者先设计数据模型的情况下去迭代了业务需求。业务模块之间的边界被淡化,控制层内逻辑只要能实现需求,想怎么写就怎么写,没有一个规范与规约。

为了解决常规中大型 Web 系统 MVC 架构下存在的弊端问题,这就引出了 DDD。这也是本小册的大主题。在本小册中,我会带你看 DDD 如何从业务模型与业务边界出发去设计代码层级结构,将散落的、重复的逻辑内聚到业务模型中。从基础概念出发到落地实战演示,带你进入 DDD 的世界。

今天这一讲的内容相较而言偏原理些,都是讲解 DDD 相关的基础信息。虽然基础,但很关键,毕竟只有夯实好基础,而后才能更好地实战。

二、DDD如何解决MVC痛点

业务的交互方式要分为两种:系统内部交互,系统与外部交互。

MVC 分层下,不论是系统内交互还是系统与外部交互,逻辑都是按照功能点被杂糅在一起。Service 层利用 DO、DTO、VO 等业务 POJO 作为数据载体,完成了所有模型之间的逻辑处理、数据转换等跟业务有关或者无关的事情。Service 层臃肿且条理不清晰。就像是吃大乱炖,什么都往里面加,反正最后能吃就行。而这些 POJO 除了字段属性,内部没有任何的业务逻辑,这就是典型的贫血模型

DDD 核心**是什么呢?解耦与内聚!建立领域模型形成聚合根,将原先散落在 Service 层的业务逻辑收拢到领域模型内部,变成充血模型,聚合即为业务。业务不是像炒大锅饭一样混在一起,而是一道道工序复杂的美食,都有它们自己独立的做法。

下面来看看 DDD 是如何处理这两种交互方式的。

1.系统内部交互

DDD 的价值观里面,任何业务都是某个业务领域模型的职责体现。为了完成某一个需求功能,将核心的业务逻辑定义在领域内部,应用服务层编排调用领域中的业务方法来实现功能点的需求。也就是说,业务功能是领域所供的能力的组合。

这样,每个领域只会做自己业务边界内的事情,最小细粒度地去定义需求的实现。原先模型层空空的贫血模型摇身一变,变成了充血模型。进到应用服务层,你的代码就是你的业务逻辑。逻辑清晰,可维护性高!

2.系统与外部交互

假如微服务体系下,有一个下订单的需求。在通过订单服务下订单前,需要先请求用户服务获取下单用户的个人信息,如下图,用户服务在版本 A 时获取用户详情的接口是 interfaceA,版本 B 时换成 interfaceB。那么就会出现,需要修改订单服务中获取用户信息的逻辑。如果类似的逻辑散落在系统的很多地方,就会出现外部系统的业务逻辑变更,造成了本系统的大量依赖变更。

从上面的例子可知,系统内部完成业务逻辑可能会与外部系统进行交互,而此时外部系统一旦发生逻辑变更,将会影响到任意一个系统内依赖外部系统的逻辑。

为了解决这个痛点问题,DDD 通过定义适配器包装对外部系统的依赖。系统内部直接依赖适配器,由适配器去调用外部接口,减小外部系统的变动对本系统业务逻辑的影响。

三、DDD的优势是什么

通过上面的介绍和剖析,不难总结出 DDD 主要有以下优势。

  • 从业务出发,自顶向下设计系统,优先考虑领域模型,而不是切割数据和行为,告别贫血模型;
  • 领域设计简化复杂业务,内聚逻辑实现,准确传达业务规则,分而治之;
  • 应用服务层的编排即展示了业务逻辑,增强了代码的可读性与可维护性;
  • 消除业务参与人员的信息不对称,提升协助效率;
  • 将外部系统等不可控因素转化为可控因素,减小系统间依赖;
  • 适合于业务复杂的中台化的系统设计。

四、到底什么样的系统适配DDD

看完上文对于 DDD 的分析之后,你是不是觉得一对比,MVC 简直就是“弟弟”?但是你回过头来想想,DDD 其实在十几年前就已经被提出来了,但为什么是近几年才开始逐渐进入大众的视野呢?总结起来,主要有以下几条原因。

  • DDD 的结构不像 MVC 结构那么简单,分层更加复杂。

  • 消除信息不对称的成本比较大,需要多方人员协作讨论业务模型。

  • 迭代快的小系统不如直接使用 MVC 做好代码规范能够更快地上线。

因此,不适配 DDD 的系统是什么呢?

  • 中小规模的系统,本身业务体量小,功能单一,选择 MVC 架构无疑是最好的。

  • 项目化交付的系统,研发周期短,一天到晚按照甲方的需求定制功能(这种本身业务需求边界就不清晰,功能的可持续迭代性就很差,而且这种系统一般就是一口价买卖),这种也最好选择 MVC。

那相反地,适配 DDD 的系统是什么呢?中大规模系统,产品化模式,业务可持续迭代,可预见的业务逻辑复杂性的系统。

总而言之就是:

  • 你还不了解 DDD 或者你们系统功能简单,就选择 MVC;
  • 你不知道选用什么技术架构做开发,处于业务探索阶段,选用 MVC;
  • 其他时候就酌情考虑 DDD。

五、分支说明

Demo代码包含了灰度分层架构 (分支:v0.0.2-SNAPSHOT) 与能力分层架构 (分支:mater)

两种分层架构的特点与优势已经在小册章节《DDD的分层详解》中详细叙述。由于小册的学习者大多初次接触DDD或者对DDD的了解不是特别深刻,因此为了让大家更好的熟悉DDD的编码风格与规范,Demo讲解部分的文章将以能力分层架构 (分支:mater) 做解析。

阅读完能力分层架构后,你也可以自行将分支切换至v0.0.2-SNAPSHOT学习灰度分层架构,相信你会有不一样的体会。