专栏首页微观技术DDD是如何解决复杂业务扩展问题?

DDD是如何解决复杂业务扩展问题?

业务初期,功能比较简单,CRUD基本可以满足。但随着系统的不断演化,业务系统越来越复杂,各模块间有着千丝万缕的关系,如何提升其扩展性,避免牵一发而动全身,是我们非常关心的。

我们会想到重构,重构伴随在业务迭代的整个生命周期里。保持行为不变的代码改善清除了不协调的局部设计。克服演进式设计中大杂烩问题的主力,通过在单独的类及方法级别上做一系列小步重构来完成。

早年的J2EE开发模式,讲究 Web/Service/Dao 三层结构。面向过程编程,对象只是数据的载体,没有行为。以数据为中心,以数据库ER设计作驱动。分层架构在这种开发模式下,可以理解为是对数据移动、处理和实现的过程。该对象我们称之为贫血领域对象。

贫血领域对象(Anemic Domain Object):是指仅用作数据载体,而没有行为和动作的领域对象。大量的业务逻辑写在了Service层中,随着业务逻辑复杂,业务逻辑、状态会散落在Service层中的很多处理类或方法中。将数据和行为割裂,原来的代码意图会越来越模糊,代码的理解和维护成本会越来越高。

如何设计复杂的业务系统

概要来讲分为三块:拆分、抽象、DDD

1、拆分。分为业务维度、技术维度。

业务维度把大的问题域拆分成若干小的业务子域。这样容易实现人员、资源的聚焦。但要考虑如何拆分的合理性,注重高内聚低耦合。

技术维度主要是软件分层,如MVC,讲究的模块化、组件化,预留接口,支持扩展。

2、抽象。物质决定意识,万事万物都有原型。根据达尔文的进化论,万物都有自己的特征,相似的物种可以归类到一个属、科等。软件也是一样的道理,将相似的业务聚拢,底层模型统一化设计,并要支持好扩展性。

3、DDD。至少30年以前,一些软件设计人员就已经意识到领域建模和设计的重要性,并形成一种思潮,Eric Evans将其定义为领域驱动设计(Domain-Driven Design,简称DDD),将数据和行为封装在一起,并与现实世界中的业务对象相映射。各类具备明确的职责划分,将领域逻辑分散到领域对象中。

DDD核心组成

1、实体

当一个对象由其标识(而不是属性)区分时,这种对象称为实体(Entity)。

例:最简单的,公安系统的身份信息录入,对于人的模拟,即认为是实体,因为每个人是独一无二的,且其具有唯一标识(如公安系统分发的身份证号码)

2、值对象

当一个对象用于对事物进行描述而没有唯一标识时,它被称作值对象(Value Object)。

例:比如颜色信息,我们只需要知道{“name”:“黑色”,”css”:“#000000”}这样的值信息就能够满足要求了,这避免了我们对标识追踪带来的系统复杂性。

3、聚合根

Aggregate(聚合)是一组相关对象的集合,作为一个整体被外界访问,聚合根(Aggregate Root)是这个聚合的根节点。

聚合由根实体,值对象和实体组成。如:一个电脑包含硬盘、CPU、内存条等,这一个组合就是一个聚合,而电脑就是这个组合的聚合根。在聚合中,根是唯一允许外部对象保持对它的引用的元素,而边界内部的对象之间则可以互相引用。

4、领域服务

当我们在分析某一领域时,一直在尝试如何将信息转化为领域模型,但并非所有的点我们都能用Model来涵盖。对象应当有属性,状态和行为,但有时领域中有一些行为是无法映射到具体的对象中的,我们也不能强行将其放入在某一个模型对象中,而将其单独作为一个方法又没有地方,此时就需要服务。

服务是无状态的,对象是有状态的。所谓状态,就是对象的基本属性:高矮胖瘦。服务本身也是对象,但它却没有属性(只有行为),因此说是无状态的。

5、领域事件

领域事件是对领域内发生的活动进行的建模。

6、限界上下文

一个由显示边界限定的特定职责。领域模型便存在于这个边界之内。在边界内,每一个模型概念,包括它的属性和操作,都具有特殊的含义。

一个给定的业务领域会包含多个限界上下文,想与一个限界上下文沟通,则需要通过显示边界进行通信。系统通过确定的限界上下文来进行解耦,而每一个上下文内部紧密组织,职责明确,具有较高的内聚性。

一个很形象的隐喻:细胞质所以能够存在,是因为细胞膜限定了什么在细胞内,什么在细胞外,并且确定了什么物质可以通过细胞膜。

如何划分限界上下文?

我们的实践是,考虑产品所讲的通用语言,从中提取一些术语称之为概念对象,寻找对象之间的联系;或者从需求里提取一些动词,观察动词和对象之间的关系;我们将紧耦合的各自圈在一起,观察他们内在的联系,从而形成对应的界限上下文。形成之后,我们可以尝试用语言来描述下界限上下文的职责,看它是否清晰、准确、简洁和完整。简言之,限界上下文应该从需求出发,按领域划分。

7、资源库(Repositories)

资源库的是封装所有获取对象引用所需的逻辑。领域对象不需处理基础设施,以得到领域中对其他对象的所需的引用。只需从资源库中获取它们,于是模型重获它应有的清晰和焦点。

资源库会保存对某些对象的引用。当一个对象被创建出来时,它可以被保存到资源库中,然后以后使用时可从资源库中检索到。如果客户程序从资源库中请求一个对象,而资源库中并没有它,就会从存储介质中获取它。换种说法是,资源库作为一个全局的可访问对象的存储点而存在。

Repository的接口应当采用领域通用语言。作为客户端,不应当知道数据库实现的细节。

Repository和DAO的作用类似,二者的主要区别:

DAO是比Repository更低的一层,包含了如何从数据库中提取数据的代码。

Repository以“领域”为中心,所描述的是“领域语言”。Repository把ORM框架与领域模型隔离,对外隐藏封装了数据访问机制。

DDD四层架构

1、User Interface 接口层,主要用于处理用户发送的Restful请求、解析用户输入信息、校验等,并将信息传递给Application层的接口。

2、Application为应用层,定义软件要完成的任务,并且指挥表达领域概念的对象来解决问题。这一层所负责的工作对业务来说意义重大,也是与其它系统的应用层进行交互的必要渠道。应用层要尽量简单,不包含业务规则或者知识,而只为下一层中的领域对象协调任务,分配工作,使它们互相协作。它没有反映业务情况的状态,但是却可以具有另外一种状态,为用户或程序显示某个任务的进度。

3、Domain为领域层(或模型层),负责表达业务概念,业务状态信息以及业务规则。尽管保存业务状态的技术细节是由基础设施层实现的,但是反映业务情况的状态是由本层控制并且使用的。领域层是业务软件的核心,领域模型位于这一层。

4、Infrastructure为基础设施层,为其他层提供通用的技术能力:业务平台,编程框架,持久化机制,消息机制,第三方库的封装,通用算法,等等

DDD六边形架构

DDD分层架构中的低层组件应该依赖于高层组件提供的接口,即无论高层还是低层都依赖于抽象,整个分层架构好像被推平了。如果我们把分层架构推平,再向其中加入一些对称性,就会出现一种具有对称性特征的架构风格,即六边形架构。六边形架构是Alistair Cockburn在2005年提出的,在这种架构中,不同的客户通过“平等”的方式与系统交互。需要新的客户吗?不是问题。只需要添加一个新的适配器将客户输入转化成能被系统API所理解的参数就行。同时,对于每种特定的输出,都有一个新建的适配器负责完成相应的转换功能。

DDD和微服务什么关系

随着微服务架构的不断流行,很多企业开始在自己的业务中推行微服务。但在实际落地过程中,总是遇到各种边界问题。DDD中的限界上下文则完美匹配微服务要求,可以将该限界上下文理解为一个微服务进程。DDD在面向高度复杂的软件系统,如何去建模,它的核心点是根据系统的复杂度建立合适的模型。

DDD 的一个生命周期是这样的:在设计和实现一个系统的时候,业务领域专家和开发人员以一套统一语言进行协作,共同完成领域模型的构建,在这个过程中,业务架构和系统架构等问题都得到了解决,之后将领域模型中关于系统架构的主体映射为实现代码,完成系统的实现落地。

DDD的本质是一种软件设计方法,而微服务是具体的落地实现。

在微服务架构实践中,人们大量地使用了DDD中的概念和技术,注意事项

1、微服务中应该首先建立UL(Ubiquitous Language,通用语言),然后再讨论领域模型。

2、一个微服务最大不要超过一个BC(Bounded Context,限界上下文),否则微服务内会存在有歧义的领域概念。

3、一个微服务最小不要小于一个聚合,否则会引入分布式事务的复杂度。

4、微服务的划分过程类似于BC的划分过程,每个微服务都有一个领域模型。

5、微服务间的集成可以通过Context Map来完成,比如ACL(Anticorruption Layer,防腐层)。

6、微服务间最好采用Domain Event(领域事件)来进行交互,使得微服务可以保持松耦合。

本文分享自微信公众号 - 微观技术(weiguanjishu),作者:TomGE

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-06-07

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 如何设计一个高性能的秒杀系统

    秒杀系统要如何架构,在做技术方案时要注意哪些问题,搞了个秒杀专辑,专门收集秒杀系列文章。

    用户7676729
  • 如何通过Binlog来实现不同系统间数据同步

    互联网时代除了业务迭代速度快,还有就是数据增速也比较快。单应用、单实例、单数据库的时代早已不复返。现在,作为技术研发,如果参与的项目没有用到分库分表,都不好意说...

    用户7676729
  • Spring框架的设计模式

    Spring 的核心功能是 IOC 容器以及 AOP 面向切面编程,同样也引入很多设计模式,提高代码的扩展性和灵活性。

    用户7676729
  • JS基础测试: 下列哪种不是创建对象的方法?​

    可以看出对象都是OBJECT类型,可以使用TYPEOF来进行判断,如果得出类型是OBJECT类型,可以做为对象类型来创建。

    舒克
  • JVM GC 那些事(二)- 堆上的内存分配机制

    前一篇文章JVM GC 那些事(一)- JVM 运行时内存划分介绍了 JVM 运行时的内存划分情况。本文将介绍 JVM GC “主战场” 堆上的内存分配机制。

    codingforfun
  • 2020年最全面的78道JVM面试题总结(含答案解析和思维导图)

    Java 中,int 类型变量的长度是一个固定值,与平台无关,都是 32 位。意思就是说,在 32 位 和 64 位 的 Java 虚拟机中,int 类型的长度...

    程序员追风
  • 2019年JVM最新面试题,必须收藏它

    方法区和对是所有线程共享的内存区域;而java栈、本地方法栈和程序员计数器是运行是线程私有的内存区域。

    李红
  • OOAD与UML笔记

    UML基础介绍 1.UML的定义 统一建模语言(UML)是一种图形化的语言,它可以帮助我们在OOAD过程中标识元素、构建模块、分析过程并可通过文档说明系统中的重...

    用户1221057
  • java面试大总结(3)

    会。如:int i,i2; return (i-i2); //when i为足够大的正数,i2为足够大的负数。结果会造成溢位,导致错误。

    py3study
  • 《Spring设计思想》AOP实现原理(基于JDK和基于CGLIB)

    版权声明:本文为博主原创文章,未经博主允许不得转载。 https://louluan.blog.c...

    亦山

扫码关注云+社区

领取腾讯云代金券