一篇带你了解大厂都在用的DDD领域驱动设计
一、DDD到底是什么
DDD全称Domain Driven Design,领域驱动设计。
为了解决快速变化、复杂系统的设计问题的
领域驱动设计是Eric Evans在2004年发表的Domain Driven Design(领域驱动设计,DDD)著作中提出的一种从系统分析到软件建模的一套方法论。以领域为核心驱动力的设计体系。
从领域驱动定义来看,领域驱动设计-软件核心复杂性应对之道,从Eric 定义中可以看出,领域驱动设计是为了解决复杂的软件设计,而且只是解决软件复杂性的一种方式,并不是唯一选择。另外不是所有的业务服务都合适做DDD架构,DDD适合产品化,可持续迭代,业务逻辑足够复杂的业务系统,对于系统初期业务逻辑相对比较简单的应用,传统MVC架构更具有优势,可以减少一部分认知成本与开发成本。而且领域驱动设计并不是万金油,只是解决复杂软件的一种方案,领域驱动设计本身只提供了理论思想,具体的落地方案一定是结合具体的业务场景实现的。
从领域驱动对应关系来看,一方面目前很多建设中台的时候大多采用DDD思想落地,DDD很多思想比如领域划分,领域事件,领域服务,边界上下文划分,充血模型,代码防腐,统一语义等等可以很好的帮助实现中台的落地,但是中台落地DDD并不是唯一选择。另一方面对于DDD的这些思想,与DDD的关系更多是聚合关系,而不是组合关系,也就是在具体应用开发中,即使采用传统的MVC架构,这些思想依然可以很好的发挥其作用。
二、DDD的由来
早期都是面向过程的代码,发现很难做到复用,结构体复用,对于结构体操作operatioin不能复用
于是就诞生了面向对象,属性和方法的统一。
需求不断变化,并且需要从产品客户不断平滑的传递到研发端,进入了第一次软件危机
第一次软件危机解决:敏捷开发-TDD-快速原型-MVC-Spring-ORM
2003年DDD诞生,201X年,微服务流行,业务的快速发展-团队快速膨胀,于是DDD就来了
三、MVC到DDD的演进
先来看看mvc三层结构的代码
包的组织:
- controller
- service
- dao
再来看看DDD架构下的代码
1、抽象CheckService,应对业务变化(防腐层)
业务检查也放到了checkService里面,因为这个选课的业务检查是可以换的,今年可以这样,明年可以那样,我们只需要那service抽出来,换具体实现就可以了,这样业务代码就不用变了。从而实现应对业务变化,这个也叫防腐层
2、抽象MQ基础设施层,防止第三方组件的变化(rocketMQ0->kafka)
另外我现在的MQ可能是kafka直接templete来set过去,如果我的mq不用kafka那么标准格式就得跟着变,我们的基础支撑层是不会确定用哪种mq,会在mq上面封装一个msgSender,在他的实现类里完全可以有各种mq来实现,假如我要换一直mq的话只要实现msgSender就可以了
3、贫血模型vs充血模型
充血模型和其他充血模型怎么做交互?
Student假如是贫血模型,会被各种sevice调用,选课,借书,体育等等,会分布到各个业务中,你还敢改student信息吗?除非你读懂每一个servcie的调用过程!
改成冲血模型,加入upgrade和run等和自身相关的业务逻辑
比如我现在有个学生领域和课程领域,我们可以单独有个服务交选课service(领域服务),来做他们两个的业务逻辑,将来我们假如他们有了新的业务,只需要在这里面加就行了,然后他们各自领域内自治,可以自我发展。
充血模型是怎么和数据库打交道的?
我们知道存储可以是各式各样的, 我们一般用仓库StudentRepository来管理Student的存储,他也是个抽象层防腐层,可以用MqSQL也好MongoDB也好具体落地
如果是复杂的对象,我们就可以用工厂模式,仓库中集成Factory/Builder应对复杂对象的组装。student里面除了有简单的属性id,name,age,可能还有address要存到另一个库里面,phone要看是不是骚扰号码存另一个库,这种复杂就需要用到组装器来组装
4、他们的对比
MVC保证了实现最差也不会差到那里去(但基本总是最差),DDD如果做的不好,可能比MVC还差,领域之间划分不清晰,可能会浪费很长一段时间也很难落地。如果做的好,确实可以做到应对业务变化(得非常了解业务,预测业务变化才能做到),也不会出现屎山代码。总的来说要让DDD真正落地,需要公司工程师有很高的素质。
5、DDD和微服务的关系
其实DDD和微服务没有什么关系,DDD是设计思想,微服务是具体实现。DDD可以用微服务实现也可以用单体来实现。如果是上面的例子,两个领域学生和课程。全部放一起就是单体,单体上学生域到课程域的领域服务单体也可以实现;如果是微服务,他们之间的领域服务可能用rpc来实现,也可用中间件来实现。