DDD领域驱动开发——概述

大家好,欢迎来到小蒋的技术圈。上次跟大家聊了IaaS、PaaS、SaaS都是什么,估计大家还有印象。Iaas、Paas、SaaS实际上也是软件行业进化的不同阶段的产物。在这个进化的过程中,有一种设计思想在2004年的时候就被提出来了,也被作者写成书出版。到现在为止经过了18年了,但依然炙手可热。而且逐渐变成了行业内微服务设计的标准实践。他就是“领域驱动设计”,今天小蒋就准备和大家一起聊聊看。下面我们开始吧。

 

领域驱动设计并不是一个什么新鲜玩意,它早在2004年的时候就被作者埃里克·埃文斯发表了。书名为Domain-Driven Design – Tacking Complexity in the Heart of Software。中文译名为领域驱动设计,清华出版社在2006年3月发布译本。大家也亲切的称呼它为DDD,也就是它的英文单词首字母。

 

我们来看一下Wikipedia中对领域驱动设计的定义:

领域驱动设计(英语:Domain-driven design,缩写DDD)是一种通过将实现连接到持续进化的模型来满足复杂需求的软件开发方法。紧接着Wikipedia中写到,领域驱动设计的前提是:

  • 把项目的主要重点放在核心领域(core domain)和域逻辑
  • 把复杂的设计放在有界域(bounded context)的模型上
  • 发起一个创造性的合作之间的技术和领域专家以迭代地完善的概念模式,解决特定领域的问题

 

这就是Wiki中对DDD的定义,想详细了解的伙伴,可以买这本书看看。

 

既然我们知道了DDD是一种软件开发方法,或者说是软件设计方法。那小蒋插一句说下我对设计的理解,欢迎大家拍砖。设计是把双刃剑,没有最好的,也没有更好的,而是条条大路通罗马。不设计和过度设计都是有问题的,恰到好处的设计才是我们要追求的。不过要做到恰到好处非常困难。

 

所以埃里克·埃文斯就推出了他的著作DDD。有一段时间DDD非常火爆,好多软件公司都在搞。不过我要先说明DDD只是一种方法,谈不上压倒性的优势,更不是完美无缺。切忌不可盲目崇拜,每种设计方法都有它特有的局限性。Microsoft就曾经建议仅将它用于复杂领域中。虽然领域驱动设计提供了许多技术优势,如可维护性。但领域驱动设计的系统可能会花费较高的成本。对于小型的系统,使用领域驱动设计可能会得不偿失。

 

基础

在“领域驱动设计”书中作者阐述了一些高层级的概念和实践,比如通用语言,这意味着领域模型应该形成领域专家为描述系统需求而提供的共同语言。我先给大家说一下,大家先有个概念就好。后面我再给家大家具体的分享一些真实的项目。

 

Entity-实体

一个不由自身属性定义而是由标识线和它身份定义的对象。

 

Value Object-值对象

只包含元素属性的不可变对象。

 

Service-领域服务

强调与其他对象的关系,只定义了可以为客户做什么,不应该替代Entity和Value Object的所有行为。

 

Module-模块

一种表达机制,划分代码和概念。

 

Factory-工厂

对于那些需要创建特定域对象的方法应该委派给工厂对象,这样可以更容易的替换实现。

 

Repository-资源库

对于检索特定域对象的方法应该委派给Repository对象,因为这样可以很容易地互换替代存储的实现

 

Aggregate-聚合

由Root Entity 绑定在一起的对象的集合,也成为聚合根。聚合根通过禁止外部对象保持其成员的引用来保证在聚合内进行的更改的 一致性。

 

Domain Event-领域事件

一个域对象定义了一个事件。域事件是域专家所关心的事件。

 

 

 

问题

 

说了一大堆虚的概念,到底DDD是个什么玩意,究竟人们用DDD解决什么具体问题?能不能更直观一点?

 

我给大家找了一个“美团点评业务系统”的开发案例,他们在DDD实践过程中的一个真实案例。那我们来看一下他们究竟要用DDD解决什么样的真实问题:

 

过度耦合

据美团工程师文彬和子维介绍,业务初期,我们的功能大都非常简单,普通的CRUD就能满足,此时系统是清晰的。随着迭代的不断演化,业务逻辑变得越来越复杂,我们的系统也越来越冗余。模块彼此关联,谁都很难说清楚模块具体功能意图是啥。修改一个功能时,往往光回溯该功能需要的修改点就需要很长时间,更别提修改带来的不可预知的负影响面。

 

 

 

订单服务接口中提供了查询、创建订单相关的接口。也提供了订单评价、支付、保险的接口。同时我们的表也是一个订单大表,包含了非常多字段。在我们维护代码时,牵一发而动全身,很可能只是想改一下评价相关的功能,却影响到了创建订单的核心路径。虽然我们可以通过测试保证功能完备性,但当我们在订单领域有大量需求同时并行开发时,改动重叠、恶性循环、疲于奔命修改各种问题。

 

后来我们总结,上述问题,归根到底在于系统架构不清晰,划分出来的模块内聚度低、高耦合。

 

我们尝试过一种解决方案,就是按照演进式设计的理论,让系统的设计随着系统实现的增长而增长。我们不需要提前做设计,就让系统伴随业务成长而演进,这其实是一种敏捷实践。后来发现效果很差,当然也可能是我们内部的问题。我们承认敏捷实践中的重构、测试驱动设计及持续集成可以对付各种混乱问题。重构,保持行为不变的代码改善清除了不协调的局部设计。测试驱动设计,确保对系统的更改不会导致系统丢失或破坏现有的功能。持续集成,则为团队提供了同一个代码库。

 

在这三种实践中,重构是克服演进式设计中大杂烩问题的主力,通过在单独的类及方法级别上做一系列小步重构来完成。我们可以很容易重构出一个独立的类来放某些通用的逻辑,但是你会发现你很难给它一个业务上的含义,只能给予一个技术维度的含义。这会带来什么问题呢?新同学并不总是知道对通用逻辑的改动或获取来之该类。显然,制定项目规范并不是好的idea。我们又闻到了代码即将腐败的味道。

 

事实上,你可能意识到问题之所在。在解决现实问题时,我们会将问题映射到脑海中的概念模型,在模型中解决问题,再将解决方案转为实际的代码。上述问题在于我们解决了设计到代码之间的重构,但提炼出来的设计模型,并不具有实际的业务含义,这就导致在开发新需求时,其他同学并不能很自然地将业务问题映射到该设计模型。设计似乎变成了重构者的自娱自乐,代码继续腐败,重构重构……无休止的循环。

 

我们要解决的实际问题就是代码的设计模型与业务模型不匹配这个问题,导致的重构工作无休止的循环。后来我们选择了DDD。

 

为什么选择DDD

解决复杂的大规模软件的武器可以被粗略地归为三类:分治、抽象和知识。

 

分治,把问题空间分割为规模更小且易于处理的若干子问题。分割后的问题需要足够小,以便一个人单枪匹马就能解决他们;其次,必须考虑如何将分割的各个部分装配为整体。分割得越合理越容易理解,在装配成整体时,所需跟踪的细节也就越少。即更容易设计各个部分的协作方式。评判什么是分治得好,即高内聚低耦合。

 

抽象,使用抽象能够精简问题空间,而且问题越小越容易理解。举个例子,从北京到上海出差,可以先理解为使用交通工具前往,但不需要一开始就想清楚到底是高铁还是飞机,以及乘坐他们需要注意什么。

 

DDD提供了这样的知识手段,让我们知道如何抽象出限界上下文以及如何去分治。DDD的核心诉求就是将业务架构映射到系统架构上,在响应业务变化调整业务架构时,也随之变化系统架构。

 

这就是“美团点评业务系统”他们用DDD来解决的真实问题。

 

总结

所以说,DDD实际上就是一整套的设计方法论,和复杂场景下如何开展软件开发的一种工具。为的就是帮助我们建立一整套的逻辑思维方式,指导我们解决复杂场景下的的软件建设。

 

小蒋想跟大家分享的是我们是否关注设计本身,不管是什么流派的设计,有设计就是好的。经历了这么多年的开发,领悟到设计真的很重要啊,不设计的代码今天不死也是拖到明天去死。不管我们在团队里待多久,不能给未来的兄弟挖坑啊!

 

好的,以上就是今天的分享。下次我们来探讨一下DDD中提到的领域模型具体是个什么东西。好,我们下次见。

 

 

音频地址:https://download.csdn.net/download/wei_wei10/12091662

(审核通过,就能下载了)

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小蒋聊技术

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值