谈一谈 DDD
一、前言
最近 10 年的互联网发展,从电子商务到移动互联,再到“互联网+”与传统行业的互联网转型,是一个非常痛苦的转型过程。在这个过程中,一方面会给我们带来诸多的挑战,另一方面又会给我们带来无尽的机会,它会带来更多的新兴市场、新兴产业与全新业务,给我们带来全新的发展机遇。然而,在面对全新业务、全新增长点的时候,我们能不能把握住这样的机遇呢?
随着业务的不断迭代和发展,用户数量也在高速增长。市场和业务环境的多变,需求往往是复杂多变的,如何快速响应变更。需求变更一次,版本迭代一次,软件就修改一次,软件修改一次,质量就下降一次。不论第一次的设计质量有多高,软件经历不了几次变更,就进入一种低质量、难以维护的状态。需要设计一套易于扩展和维护的系统。
二、概述
2.1 什么是DDD
DDD,全称Domain-Driven Design, 是一种处理复杂领域的设计思想,它试图分离技术实现的复杂性,并围绕业务概念构建领域模型来控制业务的复杂性,以解决软件难以理解,难以演进的问题。它通过边界划分将复杂业务领域简单化,帮我们设计出清晰的领域和应用边界,可以很容易地实现架构演进。
DDD将项目的主要焦点放在核心领域和领域逻辑上。基于一个模型进行复杂的设计,在技术和领域专家之间发起创造性的协作,迭代地切割问题的概念性核心。
DDD是面向对象的设计思想,是面向对象设计的一种升华。
2.2 背景
2003 年埃里克·埃文斯(Eric Evans)发表了《领域驱动设计》(Domain-Driven Design –Tackling Complexity in the Heart of Software)这本书,从此领域驱动设计(Domain Driven Design,简称 DDD)诞生。DDD 核心思想是通过领域驱动设计方法定义领域模型,从而确定业务和应用边界,保证业务模型与代码模型的一致性。
但 DDD 提出后在软件开发领域一直都是“雷声大,雨点小”!直到 Martin Fowler 提出微服务架构,DDD 才真正迎来了自己的时代。
大部分的Java程序员开发生涯是从学习J2EE经典的分层理论开始的(Action、Service、Dao),在这种分层理论中,我们基本没有啥机会使用那些所谓的“行为型”的设计模式,这里的核心原因,就是J2EE经典分层的开发方式是“贫血模型”。
2.3 使用DDD的价值
- 获得了一个非常有用的领域模型
- 业务得到了更准确的定义和理解
- 领域专家可以为软件设计做出贡献
- 更好的用户体验
- 清晰的模型边界
- 更好的企业架构
领域专家、设计人员、开发人员通过领域模型进行交流,彼此共享知识和信息,有利于沟通概念的统一,减少交流的障碍,防止需求变样。
设计足够精良且符合业务需求的领域模型能够快速的响应需求变化。
软件的本质就是对真实世界的模拟。因此,我们会有一种想法,能不能将软件设计与真实世界对应起来,真实世界是什么样子,那么软件世界就怎么设计。如果是这样的话,那么在每次需求变更时,将变更还原到真实世界中,看看真实世界是什么样子的,根据真实世界进行变更。这样,日后不论怎么变更,经过多少轮变更,都按照这样的方法进行设计,就不会迷失方向,设计质量就可以得到保证,这就是“领域驱动设计”的思想。
2.4 实施DDD所面临的挑战
- 为创建通用语言腾出时间和精力
- 持续地将领域专家引入项目
- 改变开发者对领域的思考方式
三、如何DDD
3.1 战略设计与战术设计
DDD的实施分战略设计与战术设计。
战略建模:战略设计主要从业务视角出发,建立业务领域模型,划分领域边界,建立通用语言的限界上下文,限界上下文可以作为微服务设计的参考边界。战略建模是以一种最宏观的角度去审视整个项目对它进行拆分,来划分“界限上下文”,最终形成一个具有俯瞰视角的“上下文映射图”。
战术建模:战术设计则从技术视角出发,侧重于领域模型的技术实现,完成软件开发和落地,包括:聚合根、实体、值对象、领域服务、应用服务和资源库等代码逻辑的设计和实现。
在实施过程中,我们需要明确“通用语言”,通用语言和限界上下文(Bounded Context)同时构成了DDD的两大支柱,并且它们是相辅相成的。
通用语言是以一种领域专家、设计人员、开发人员都能理解的语言作为相互交流的工具,在不断交流的过程中发现和挖出一些主要的领域概念,然后将这些概念设计成一个领域模型。由领域模型驱动软件设计,用代码来表现该领域模型。领域需求的最初细节,在功能层面通过领域专家的讨论得出。
领域模型贯穿软件分析、设计,以及开发的整个过程;领域专家、设计人员、开发人员通过领域模型进行交流,彼此共享知识与信息;因为大家面向的都是同一个模型,所以可以防止需求走样,可以让软件设计开发人员做出来的软件真正满足需求。
在这个过程中,需要业务专家的参与,他们才是最懂业务的人,他们能用他们已有的对这个领域的知识,与我们一起设计开发系统,让我们的系统更符合实际需要。典型的例子,在设计一个系统时,如果两个对象或模型有关联关系,那么这个关联关系是一对多、一对一还是多对多,有时我们并不能很好的判断出来,这个时候就需要领域专家应用他们的知识来帮助我们一起处理这部分的设计。
领域专家并不是一个职位,他可以是精通业务的任何人。可能是软件产品的设计者,甚至可能是销售员。
在设计的过程中,我们书面设计的文档不可能面面俱到,应尽量将文档减至最少,并且主要用它来补充代码和口头交流,解释我们模型中的概念,避免文档与项目脱节,设计的重要细节应该在代码中体现出来。
3.2 DDD领域模型
DDD的研究方式与自然科学的研究方法类似,下面以研究一棵树的过程举例
- 第一步:确定研究对象,即研究领域,这里是一棵桃树。
- 第二步:对研究对象进行细分,将桃树细分为器官,器官又分为营养器官和生殖器官两种。其中营养器官包括根、茎和叶,生殖器官包括花、果实和种子。桃树的知识体系是我们已经确定要研究的问题域,对应 DDD 的领域。根、茎、叶、花、果实和种子等器官则是细分后的问题子域。这个过程就是 DDD 将领域细分为多个子域的过程。
- 第三步:对器官进行细分,将器官细分为组织。比如,叶子器官可细分为保护组织、营养组织和输导组织等。这个过程就是 DDD 将子域进一步细分为多个子域的过程。
- 第四步:对组织进行细分,将组织细分为细胞,细胞成为我们研究的最小单元。细胞之间的细胞壁确定了单元的边界,也确定了研究的最小边界。
在领域不断划分的过程中,领域会细分为不同的子域,子域可以根据自身重要性和功能属性划分为三类子域,它们分别是:核心域、通用域和支撑域。决定产品和公司核心竞争力的子域是核心域,它是业务成功的主要因素和公司的核心竞争力。没有太多个性化的诉求,同时被多个子域使用的通用功能子域是通用域。还有一种功能子域是必需的,但既不包含决定产品和公司核心竞争力的功能,也不包含通用功能的子域,它就是支撑域。核心域、支撑域和通用域的主要目标是:通过领域划分,区分不同子域在公司内的不同功能属性和重要性,从而公司可对不同子域采取不同的资源投入和建设策略,其关注度也会不一样。
3.3 限界上下文
限界上下文用来确定语义所在的领域边界,限界就是领域的边界,而上下文则是语义环境。通过领域的限界上下文,我们就可以在统一的领域边界内用同意的语言进行交流。
比如电商领域的商品一样,商品在不同的阶段有不同术语,在销售阶段是商品,而在运输阶段则变成了货物。同样的一个东西,由于业务领域的不同,赋予了这些术语不同的涵义和职责边界,这个边界就可能会成为未来微服务设计的边界,所以领域边界就是通过限界上下文来定义的。
所以,在微服务中,每个领域模型都有它对于的限界上下文,团队在限界上下文内通用语言交流。领域内所有限界上下文的领域模型构成整个领域的领域模型。
3.4 运用单一职责原则
单一职责原则:软件系统中的每个元素只完成自己职责范围内的事,而将其他的事交给别人去做,我只是去调用。
“一个职责就是软件变化的一个原因”是著名的软件大师 Bob 大叔在他的《敏捷软件开发:原则、模式与实践》中的表述。
单一职责原则要求我们在维护软件的过程中需要不断地进行整理,将软件变化同一个原因的代码放在一起,将软件变化不同原因的代码分开放。
四、总结
深刻理解 DDD 的设计思想和内涵,把握好边界和分层这个大原则,结合企业文化和技术特点,灵活运用战术设计方法,选择最适合的技术和方法解决实际问题。
在每次变更的时候,先回到领域模型,基于业务进行领域模型的变更。然后,再基于领域模型的变更,指导程序的变更。这样,不论经历多少次需求变更,始终能够保持设计质量不退化。这样的设计,才能保障系统始终在低成本的状态下,可持续地不断维护下去。
参考资料
- 《领域驱动设计——软件核心复杂性应对之道》
- 《实现领域驱动设计》
- DDD实战课(欧创新)
谈一谈 DDD的更多相关文章
- 谈一谈Java8的函数式编程(二) --Java8中的流
流与集合 众所周知,日常开发与操作中涉及到集合的操作相当频繁,而java中对于集合的操作又是相当麻烦.这里你可能就有疑问了,我感觉平常开发的时候操作集合时不麻烦呀?那下面我们从一个例子说起. 计 ...
- 谈一谈泛型(Generic)
谈一谈泛型 首先,泛型是C#2出现的.这也是C#2一个重要的新特性.泛型的好处之一就是在编译时执行更多的检查. 泛型类型和类型参数 泛型的两种形式:泛型类型( 包括类.接口.委托和结构 没有泛型枚 ...
- 从一张图开始,谈一谈.NET Core和前后端技术的演进之路
从一张图开始,谈一谈.NET Core和前后端技术的演进之路 邹溪源,李文强,来自长沙.NET技术社区 一张图 2019年3月10日,在长沙.NET 技术社区组织的技术沙龙<.NET Core和 ...
- 谈一谈Elasticsearch的集群部署
Elasticsearch天生就支持分布式部署,通过集群部署可以提高系统的可用性.本文重点谈一谈Elasticsearch的集群节点相关问题,搞清楚这些是进行Elasticsearch集群部署和拓 ...
- 谈一谈iOS事件的产生和传递
谈一谈iOS事件的产生和传递 1.事件的产生 发生触摸事件后,系统会将该事件加入到一个由UIApplication管理的事件队列中. UIApplication会从事件队列中取出最前面的事件,并将事件 ...
- 谈一谈对MySQL InnoDB的认识及数据库事物处理的隔离级别
介绍: InnoDB引擎是MySQL数据库的一个重要的存储引擎,和其他存储引擎相比,InnoDB引擎的优点是支持兼容ACID的事务(类似于PostgreSQL),以及参数完整性(有外键)等.现在Inn ...
- 谈一谈APP版本号问题
如题:谈一谈APP版本号问题 为什么要谈这个问题,周五晚上11~12点,被微信点名,说APP有错,无效的版本号,商城无法下单.我正在准备收拾东西,周末回老家,结果看到这样问题,菊花一紧.我擦,我刚加的 ...
- 谈一谈深度学习之semantic Segmentation
上一次发博客已经是9月份的事了....这段时间公司的事实在是多,有写博客的时间都拿去看paper了..正好春节回来写点东西,也正好对这段时间做一个总结. 首先当然还是好好说点这段时间的主要工作:语义分 ...
- 蓝的成长记——追逐DBA(5):不谈技术谈业务,恼人的应用系统
***************************************声明*************************************** 个人在oracle路上的成长记录,当中 ...
- 谈一谈并查集QAQ(上)
最近几日理了理学过的很多oi知识...发现不知不觉就有很多的知识忘记了... 在聊聊并查集的时候顺便当作巩固吧.... 什么是并查集呢? ( Union Find Set ) 是一种用于处理分离集合的 ...
随机推荐
- IOC和DI之刨根问底之第一节
很多freshman上来就想搞清楚什么是IOC和DI,其实很多先进的理论和技术都在老的基础上升华出来的,最终目的是为了解放生产力. 所以先来说说下面两点基础知识: Direct Dependency( ...
- 前端大牛带你了解JavaScript 函数式编程
前言 函数式编程在前端已经成为了一个非常热门的话题.在最近几年里,我们看到非常多的应用程序代码库里大量使用着函数式编程思想. 本文将略去那些晦涩难懂的概念介绍,重点展示在 JavaScript 中到底 ...
- 常用JAVA API :String 、StringBuilder、StringBuffer的常用方法和区别
摘要 本文将介绍String.StringBuilder类的常用方法. 在java中String类不可变的,创建一个String对象后不能更改它的值.所以如果需要对原字符串进行一些改动操作,就需要用S ...
- 访问所有HTTPS网站显示连接不安全 (火狐浏览器)
当 Firefox 连接到一个安全的网站时(网址最开始为"https://"),它必须确认该网站出具的证书有效且使用足够高的加密强度.如果证书无法通过验证,或加密强度过低,Fire ...
- shell 中单引号和双引号的区别
用以下代码来说明: #!/bin/bash url="http://c.biancheng.net" website1='C语言中文网:${url}' website2=" ...
- Spring源码学习之容器的基本实现(一)
前言 最近学习了<<Spring源码深度解析>>受益匪浅,本博客是对学习内容的一个总结.分享,方便日后自己复习或与一同学习的小伙伴一起探讨之用. 建议与源码配合使用,效果更嘉, ...
- storm在windows下本地调试报错java.lang.UnsatisfiedLinkError cannot find rocksdbjnixxxxxxxxxx.dll
storm启动本地集群调试时,有时会找不到rocksdbjni.dll,storm加载该库的时候会先从jkd的bin下找rocksdbjni.dll,如果找不到就从pom文件的依赖包里找,再找不到就会 ...
- celery config
/* Useful celery config. app = Celery('tasks', broker='redis://localhost:6379', backend='redis://loc ...
- git修改用户和邮箱
GIT查看当前用户以及邮箱 $ git config user.name $ git config user.email GIT修改用户以及邮箱 $ git config --global user. ...
- php多域名跳转nginx
当web服务器nginx已经配置好了网站根目录时,需要增加另外的域名.但是由于限制必须在原来的网站根目录下面,nginx已经无法实现.只能通过php index页面进行调试.如下面: define(' ...