DDD领域驱动分层架构设计及业务场景实践.docx
1.DDD领域驱动设计基本概念理解1.1 什么是DDD领域驱动设计?领域驱动设计DDD(DomainDrivenDesign)是一种从系统分析到软件建模的设计思想和方法论,最早在2004年由EricEvans在著作Domain-DrivenDesign-TacklingComplexityintheHeartofSoftware>中提出相应的概念.其核心思想是以领域为核心驱动力构建软件设计体系,并围绕业务概念抽象出领域模型,通过领域和边界划分将豆杂的业务模型抽象化、简单化,最终实现巨杂软件应用系统的拆解和封装.DDD不仅可以用于微服务设计,还可以很好地应用于企业中台的设计,也适用于传统的单体应用。在DDD中分为战略设计和战术设计:战略设计:主要从业务视角出发,建立业务领域模型、划分领域边界、建立通用语言的限界上下文。在战略设计中,需要对领域进行全面的了解和分析,探究业务的规则和本质,以及领域未来的趋势发展和可能变化.战术设计:从技术视角出发,侧重于领域模型的技术实现完成软件开发和落地,包括:实体、值对象、聚合、工厂、仓而、领域服务、领域事件等代码逻娼的设计和实现。战术设计关注的是领域中的具体情境和场景,需要针对具体的问题进行具体的分析和设计,以满足业务需求.1.2 DDD领域驱动设计和微服务关系这些年随着软硬件技术的发展,软件设计架构也从最早的单机架构(BS/CS)、到集中式架构,再到如今微服务(MicroServices)分布式架构.单机架构:采用面向过程的设计方法,系统包括应用客户端层和数据库层,整个系统围绕数据库驱动设计和开发.集中式架构:采用面向对象的设计方法,经典的三层架构包括业务接入层、业务逻辑层和数据访问层.这种集中式架构容易在某一层变得腌肿,另外受限于集中式的限制,可扩展性差,架构上不熨活。分布式微服务架构:微服务架构将应用程序拆分为一系列相互协作的微服务,每个微服务负责一个特定的业务领域或业务功能,从而实现应用之间的解耦,同时解决单体应用扩展性和灵活性问题.在微服务架构中,每个微服务都是独立的,可以使用不同的编程语言、数据存储和框架,这使得每个微服务可以独立地演进和部署.DDD的概念最早在2004年就提出来之后一直不温不火直到微服务的概念出现.也可以说DDD的流行和微服务的发展有一定的关系.微服务的兴起使得越来越多的开发人员开始关注如何将应用程序划分成一系列相互协作的循服务,并且每个微服务需要实现自己的业务逻辑.在这个过程中,开发人员发现他们需要一种更好的方式来划分业务领域边界,建立正确的业务模型和通用语言,以便更好地实现微服务的独立性和可维护性.DDD正是一种能够帮助开发人员更好地设计和实现业务领域的模式,通过将业务领域作为核心,DDD可以帮助开发人员更好地理解业务需求,建立正确的业务模型和通用语言,从而更好地划分微服务边界,从而更好地实现微服务的独立性和可维护性.1.3 DDD领域驱动设计中核心概念领域驱动设计DDD中的核心概念如下图所示,包括:战略设计:包括领域/子域、统一语言、限界上下文等概念战术设计:使用这些模式来捕获和传递领域中的概念、关系、规则代表领域的概念:如实体、值对敏、领域服务、领域事件等用于管理对演的生命周期:如聚合、工厂、费源库等用于集成或跟踪:领域事件、事件溯源等1.3.1 统一语言DDD中的统一语言(Ubiquitous1.anguage)是一种用于描述业务领域中的概念、规则和流程的语言,它是一种由领域专家、开发人员和用户共同理解的通用语言,而不是一种特定的编程语言或框架.统一语言贯穿于整个开发过程,从需求分析到设计再到编码.它应该用于描述业务领域中的实体、值对象、聚合、领域服务等概念,同时也要用于描述业务规则、流程和交互.在统一语言中,应该使用业务领域的术语和词汇,而不是技术术语和词汇。例如,应该使用“客户"而不是"用户”来描述应用程序的客户端,因为"客户”是业务领域中的术语,而“用户”则是技术领域的术语.统一语言的重要性在于,它能第帮助开发人员更好地理解业务需求和领域模型,同时也能帮助业务专家更好地理解技术和系统实现.它还能够提高代码的可读性和可维护性,因为代码中的术语和词汇能够技业务专家和开发人员共同理解.1.3.2 领域和子域在研究和解决业务问题时,DDD会按照一定的业务规则将业务领域进行细分,当领域细分到一定的程度后,DDD会将问题范围限定在特定的边界内,在这个边界内建立领域模型.简言之,领域是业务领域的范围和边界.领域可以进一步划分为不同的子领域称为子域,每个子域都包含了一些特定的业务规则和功能,它们共同构成了整个业务领域.领域和子域是基于业务需求和功能进行划分,在划分领域和子域时,需要考虑到业务需求的变化和扩展,使得系统能够更好地适应业务需求的变化.领域和子域的划分也是一种设计上的分屉,它能够帮助开发人员更好地理解和组织系统中的代码和模型。在实现时,每个子域都可以被划分成一个或多个限界上下文(BoundaryContext),每个限界上下文都包含了一些特定的业务规则和功能,它们共同实现了该子域的功能.1.3.3 核心域、通用域和支撑域领域在不断划分的过程中,细分为不同的子域,根据电要性的不同,子域又划分为核心域、通用域和支厚域.1)核心域是指业务领域中的核心业务遗帽和模型,它们是领域驱动设计的核心部分.核心域包含领域模型、领域服务、业务规则等,它们是业务领域中最重要,最稳定的部分.2)通用域是指业务领域中通用的、可复用的业务逻辑和模型,它们不是核心业务逻辑,但可能在多个核心域中被使用。通用域包含一些通用的业务逻担、数据校验、安全控制等.3)充理域是指用于支持业务领域中的核心域和通用域的感础设施和工具,包括数据存玮、消息传递、分布式系统、自动化测试等。支撑域中不包含业务逻期和模型,而是聚焦于技术实现和系统监控、管理等方面.1.3.4 限界上下文限界上下文是指一个业务领域的边界,它包含了多个子域(Subdomain),每个子域都包含了一些特定的业务规则和功能.限界上下文用来封装通用语言和领域对象,提供上下文环境,保证在领域之内的一些术语、业务相关对象等(通用语三)有一个确切的含义,没有二义性。限界上下文是一个显式的边界,领域模型便存在于这个边界之内;在边界内,通用语言中的所有术语和词组都有特定的含义,而模型需要准确地反映通用语言.通过限界上下文,开发人员可以更好的组织和管理领域中的不同部分,更好的理解业务需求和领域模型.领域、子域和限界上下文的关系如下图所示:1.3.5 实体Entity实体是拥有唯一标识和状态,且具有生命周期的业务对象.在实体的生命周期内,无论其如何变化,其仍旧是同一个实体.实体的标识和状态可以在业务过程中发生变化,而实体之间的关系也可以随若业务过程的变化而发生变化.实体的业名形态:在领域模型中实体是多个届性、操作或行为的戮体,实体的属性描述实体的状态比如客户的姓名、年龄等;实体的行为指实体可以执行的操作,比如创建、更新等.实体和值对象是组成领域模型的基础单元。实体的代码形态:在代码模型中,实体表现为实体类,这个类包含了实体的属性和方法,通过这些方法实现实体自身的业务遗掘。实体的运行形态:实体是以DO(领域对象)的形式存在,每个实体对象都有一个唯一的ID,这个ID在实体的整个生命周期内无论实体对象怎么变化都具有唯一性.实体的数据库形态QDD先构建领域模型,再针对业务场景构建实体对象和行为,最后将实体对象映射到数据持久化对象.在领域模型映射到数据模型时,一个实体可能对应。个、1个或者多个数据库持久化对象.1.3.6 值对象实现领域驱动设计一书中对值对象的定义如下:通过对象,属性值来识别的对象,它将多个相关属性组合为一个概念整体.在DDD中用来描述领域的特定方面,并且是一个没有标识符的对象,叫作值对象。值对象没有唯一标识,没有生命周期,不可修改,当值对象发生改变时只能替换.值对余将不同的相关属性组成一个整体概念,是一堆不可变质性的集合。值对象的业务形态:值对象是若干不可修改的属性集合,只有数据初始化操作和有限不涉及数据修改的行为,基本不包含业务逻娼。值对象在逻辑上依然是实体属性的一部分.值对象的代码形态:如果值对象是单一属性例如字符串、整型、枚举等,则亘接定义为实体类的属性;如果值对源是属性集合,则把它设计为Class类,包含多个属性.值对象的运行形态:值对象嵌入到实体中,有两种不同的数据格式分别是属性嵌入的方式和序列化大对象的方式,比如JSON字段1.3.7 聚合和聚合根1)聚合和聚合根DDD中实体和值对象是很基础的领域对象,实体一般对应业务对象、值对象一般是属性集合,但是实体和值对象都是个体化的表现.而聚合是把一些关联性极强、生命周期一致的实体、值对象放到一个聚合里,聚合根(AggregateRoot)是指聚合中的实体,它负责聚合的内聚性和一致性,同时也是聚合的入口点.聚合有一个聚合根和上下文边界,这个边界根据业务单一职责和高内聚原则,定义了聚合内部应该包含哪些实体和值对象,而聚合之间的边界是松融合的.按照这种方式设计出来的微服务很自然就是"高内聚、低楷合"的。2)聚合设计原则在领域驱动设计中,聚合设计是非常生要的一部分.以下是聚合设计的一些原则:保持完整性:聚合中包含一组相互关联的实体和值对象,它们被视为一个整体.聚合的边界应该明确,以保持其完整性。保持一致性:聚合内数据强一致性,而聚合之间数据最终一致性.聚合中的实体和值对象在业务规则和均束下保持一致,如果一个聚合违反了业务规则或约束,那么它就不是一个一致的聚合.保持小型聚合:鬃合尽可能小,只包含必要的实体和值对象.小型聚合可以降低由于业务过大导致聚合里构,有助于提高系统的可维护性和可扩展性.避免聚合根以外的实体:聚合根以外的实体应该尽可能避免,因为它们增加了系统的豆杂性和藕合度.如果必须使用这些实体,应该确保它们与聚合根之间的关系是一致的.避免跨聚合引用:如果一个实体或值对象需要引用另一个聚合中的实体或值对源.通过关联外部聚合根ID的方式引用,而不是直接对象引用的方式。这样可以保持聚合的独立性和可维护性.1.3.8 工厂如果实体或值对象的创建过程非常豆杂,可以将其委托给工厂.DDD中工厂是指用于创建领域模型对象的工厂方法,它用于将业名逻辑和实现技术解褐.它是一种设计模式,允许开发人员通过调用工厂方法来创建对象,而不需要直接在代码中实例化对象,将创建复杂对象和聚合的职责分配给一个单独的对象,该对象本身并不承担领域模型中的职责,但是依然是领域设计的一部分.工厂应该提供一个创建对彖的接口,该接口封装了所有创建对象的复杂操作过程,同时,它并不需要客户去引用那个实际被创建的对象.对于聚合来说,我们应该一次性地创建整个聚合,并且确保它的不变条件得到满足.1.3.9 仓库仓库(Repository)是一种模式,用于封装数据访问逻辑,提供对数据的持久化和查询.仓库操作的最小单元就是聚合,每个聚合会对IS一个仓库.它旨在将数据访问细节与领域模型分曲,使领域模型更加独立和可测试.仓库提供了一种统一的接口,使得领域模型可以与不同的数据存储方式进行交互.同时也提供了一些直询操作.例如,一个客户仓库可以具有存储客户、检索客户等方法.通过调用仓库方法,开发人员可以存储和检索客户对象,同时也可以在存储和检索对象时执行其他的业务逻箱和验证.1.3.10 领域服务领域服务(DomainService)是指跨越多个聚合或子域的逻辑服务,用于处理业务需求和行为.领域服务通常用于执行跆聚合的操作,例如处理业务规则、集成外部系统、执行弟杂业务流程等.需要注意的是,实体和领域服务在实现业务逻辑上不是同级的,当领域中的某些功能,单一实体或者值对象不能实现时,领域服务就会出马,它可以组合聚合内的多个实体或者值对凝,实现复杂的业务逻辑.1.3.11 领域事件领域事件(DomainEvent)是指业务领域中发生的一些事件,通常意味着领域对象状态的改变.领域事件在系统中起到了传递消息、触发其他动作的作用,是解耦领域模型的审要手段之一.在实现时,领域事件可以由领域模型中的实体或服务触发,例如当用户注册成功时,可以触发一个“用户注册成功"的领域事件.领域事件也可以由外部系统或服务触发,例如当第三方支付系统支付成功时,可以触发一个”订单支付成功”的领域事件.领域事件处理包括:事件构建和发布、事件数据持久化、事件总线、消息中间件、事件接收和处理等.事件构建和发布:事件基本属性通常包含事件的标识符、时间戳和事件数据等信息;业务属性用于记录事件发生时刻的业务数据。事件的基本属性和业务属性一起构成事件实体.(事件发布方式很多,可以通过应用服务或领域服务发布到事件总线或消息中间件;也可以从事件列表中定时获取增量事件数据发布到消息中间件.事件数据持久化:用于系统之间的数据对账,或者实现发布方和订阅方数据的审计.持久化到本地业务数据库的事件表中,利用本地事务保证业务和事件数据的一致性;OO持久化到共享的事件数据库中,事件数据库和业务库是独立的.O事件总线:实现微服务内聚合之间领域事务,提供事务分发和接收等服务.消息中间件:跨微服务的领域事件大多会用到消息中间件,实现跨微服务的事件发布和订阅.事件接收和处理:订阅方采用监听机制接收消息队列中的事件数据,当完成事件数据的持久化处理后,就可以进行下一步的业务处理。1.3.12 贫血模型和充血模型1)苣血模型(AnemicDomainModel)所谓的益血模型指的是定义领域对象的时候,只有对象的属性信息,没有对象的行为信息.贫血模型强调了业务逻辑和数据之间的分商,将业务逻辑和数据分别放在不同的对象中,导致领域模型变得贫血、不灵活和难以维护.2)充血模型(RichDomainModel)充血模型指的是领域对软既包括属性信息,又包含行为信息,在充血模型中,领域对象包含了业务逐矩、数据和规则,它们被组织在一个聚合根中.相对而言贫血模型相对简单,模型上只有数据没有行为,针对简单的模型可以快速的完成交付,但是后期的成本较高,并且难以保证事务的一致性和完整性。充血模型则是领域模型模式,实现逻辑上由各自的对缺负责,具备事务的一致性和完整性,事务边界也被包含在聚合根中。1.4 DDD领域驱动设计中架构模型在DDD领域中,常见的领域驱动设计架构有以下几种:D分层架构:格领域模型和业务逻辑分离出来,并减少对基批设施和应用层的依赖,从最早三屏架构,演化到四屈架构、五层架构,包括用户接口层、应用屣、领域层和基础设施层。分层架构中,每层只能与位于其下方的层IS合。2)六边形架构:也称为端口与适配器.对于每种外界类型,都有一个适配器与之相对应.该架构的定义涉及六个部分,分别是应用程序、域、基础设施、适配器、API和外部系统.六边形架构的核心理念是应用通过端口与外部进行交互。3)CQRS架构:CQRS架构是一种读写分离的架构设计,系统被分为两个部分:命令端(Command)和有询端(Query).命令端负责处理写操作(例如修改数据),而查询端则负责处理读操作(例如查询数据)4)事件驱动架构:用于处理事件的生成、发现和处理等任务的软件架构.在这种架构中,领域对象之间通过发布和订阅事件来通信,一个事件通常会引发多个服务的同步,从而引起事件的扩散.1.5DDD领域驱动设计的优缺点实现DDD的最大好处是第一时间将业务需求构建成领域模型,而不是将其切割为数据和行为,然后再用数据库去实现.DDD具有以下优点:统一业务语言:通过使用统一的领域语言,准确传达业务规则,提升团队间的沟通效率.清晰业务边界:统一各个子域的认识,通过领域模型界定需求实现边界。提升变化应对:通过领域模型与数据模型的分离,格核心业务的不变与需求的变进行有效隔潮,提升架构应变化的能力。当然使用DDD需要一定的学习和使用成本,开发人员需要掌握一定的领域建模知识和技能.同时在高并发场景下,由于DDD使用的模型可能会增加一些额外的开销,2、DDD领域驱动设计主要架构模型介绍2.1分展架构DDD的分层架构在不断的优化,在最早的四层架构中(如左上图所示),基础设施层被其它层依赖,处于最核心的位置,但实际上领域层才是依赖的核心.后来采用依赖倒置的设计,优化了传统的四层架构,实现了各层对基础设施层的解螭.四层架构包括:用户接口层、应用层、领域层和基础设施层.1)用户按口层:负责展示用户界面和处理用户输入,它通常包含一些前端框架和Ul组件;2底用层次责处理业务逻辑和应用程序的流程,它通常包含一些应用程序服务,例如事务处理、规则验证和流程管理等;3)领域层:领域层是领域模型的核心,它负责表示业务领域中的实体、值对象、聚合、领域服务等.领域层通常包含一些领域模型类、领域服务类和领域事件等;4)基础设施层:负责提供一些通用服务和功能,例如数据存储、消息传递、日志记录等.基础设施层通常包含一些工具类、庵和框架等.在传统架构中,由于上层应用对数据库的强森合,很多业务在架构演进中担心更换数据库对应用的影响,因为一旦更换数据库,就可能需要再写大部分代码,这对开发来说是致命的.采用依赖倒置的设计后,应用层通过解偶保持独立的核心业务逻辑,当数据库变更后,只需要更换数据库基础服务即可,对应用的影响降到最低.2.2 六边形架构六边形架构又称为“端口-适配器"架构,对于每种外界类型,都有一个适配器与之相对应,其核心设计思想是外界通过应用API与内部进行交互.如图所示的六边形架构中,核心业务逻担(应用程序和领域模型)与外部资源(包括Web应用、基础设施等)完全隔商,仅通过适配甥进行交互.除了核心的领域模型,还包括如下三个层次:应用程序层(Application1.ayer):负责处理业务逻辑和应用程序的流程,它通常包含一些应用程序服务,例如事务处理、规则验证和流程管理等。适配器层(Adapter1.ayer):负责与外部系统进行交互,例如与数据麻、消息队列、Web服务等进行通信和交互.它通常包含一些适配器类和工具类等。基础设施层(Infrastructure1.ayer):负责提供一些通用服务和功能,例如数据存储、消息传递、日志记录等.基础设施层通常包含一些工具类、库和框架等.在六边形架构中,一个端口可能对应多个外部系统,不同的外部系统也可能会使用不同的适配器,由适配器负责协议转换.因此在使用时,根据用例来设计应用程序,所有的适配器使用相同的API满足不同的端口访问需求.2.3 CQRS架构CQRS是一种读写分离的架构设计,系统分为两个部分:命令端和直询端,命令端负责处理写麋作,而直询端负责处理读搽作.在CQRS架构中,写操作和读操作技分离并分布在两个独立的系统中.命令端和查询端之间的数据一致性采用最终一致性的方式处理.这意味着查询端可能会返回不是最新的数据,但系统会确保在一定时间内数据达到一致状态.除了分离写操作和读操作CQRS架构还涉及到事件的概念,事件表示命令操作领域中的聚合根状态发生变化后产生的事件1这些事件可以被其他服务订阅,以实现系统的联动.3、DDD领域驱动设计落地实践前面介绍了DDD的基本概念以及主要架构,这些概念很抽象,读起来是一头更水.下面以"在线书店”为例,探讨下DDD具体落地实践.3.1 战略设计在战略设计阶段,主要过程分为业务场景分析、领域建模、划分边界上下文,战路设计是DDD中的核心步骤.3.1.1 业务场景分析业务场景分析阶段是对业务的全面饯理,将业务中涉及到的细节展现出来,为后续的领域建模提供足够的业务输入.常见的业务场景分析方法有事件风暴法、四色建模法、用例分析法等,四色建模法相对比较豆杂,本文以事件风嶷法对"在线书店"业务场景进行分析梭理.1)场景分析参与方事件风基类似于头脑风后的方法,业务场景的分析讨论参与角色包括领域专家、产品经理、需求分析人员、架构师、项目经理、开发经理和测试经理.参与者要尽可能遍历所有业务细节,列出具体的业务场景、业务规则、业务之间的关联以及热点问题等.2)业务讨论与傀理经过事件风品讨论后,总结出"在线书店"下的典型的业务场空,包括:书籍管理:核心业务场景是书籍销售,书籍管理包括书籍信息的录入、编辑、查询、分类、推荐等.用户管理:为用户提供注册、登录、个人信息管理、购物车管理等功能.订单管理:为用户提供订单的创建、支付、发货、跟踪等功能.支付管理:为用户提供安全的支付方式分析业务中的事件,寻找业务逻辑和业务规则,比如用户购书流程,从用户注册登录、书籍直询推荐、订单创建和支付等流程进行梳理.下面是按照业务操作对象、事件和命令(动作)形成事件风爆小黑板对整个业务流程进行梳理。3.1.2 颖域建模在领域建模时,根据场史分析过程中产生的领域对蕊,找出产生命令的实体,分析实体之间的依赖关系产生聚合,为聚合划定界限上下文,建立领域模型以及模型之间的依赖.根据上图业务场景分析,提取产生这些行为的实体,包括用户、订单、书籍、购物车和系统管理.完成领域对象分析后,需要构建业务聚合,首先在实体中找到聚合根,根据聚合根的特点,聚合根是一个实体具有全局唯一标识,并且具备生命周期的同时需要专门的模块进行管理.根据这些特征就有了书籍、用户、订单和系统管理这些聚合.3.1.3 划分边界上下文获得了整个业务流程中的所有聚合后,需要更具业务语义上下文将具体的聚合划分到对应的上下文中.根据订单和购物车的业务操作行为,订单和购物车共同构成订单域分别管理订单和购物车,用户管理和系统管理共同构成系统和用户常理,因此分为订单域、商品域及系统和用户管理域三个限界上下文。3.2 战术设计3.2.1 岗服务拆分通常一个眼界上下文拆分为一个微服务,微服务的设计还需要考虑服务的粒度、分层、边界划分、依赖关系和集成关系.因此,在“在线书店”业务场景中,拆分为订单服务、商品服务和系统用户管理服务三个微服务.3.2.2 领域分层根据前面介绍的四层分层架构,对领域进行分层设计,包括:用户接口层、应用层、领域度和基础设施层.33代码实现1)定义书籍实体,它具有书名、作者、出版日期和价格等属性2)定义一个值对象类ISBN,它代表书的唯一标识符:3)定义一个领域服务类BookstoreService,它包含一些业务逻娼,例如添加书籍、百找书籍和下架书籍等:4)定义一个领域事件类BookEvent,它代表书籍状态的变化,例如书籍上架、书籍下架等:5)定义一个应用服务类BookstoreAppIicationService,它与用户交互,并调用领域服务类中的方法来实现业务逻辑:以上是Python实现一个简单的Dl)D项目示例,它包含了实体、值对象、领域版务、领域事件和应用服务等多个组件。4、总结本文简要介绍了DDD领域驱动设计的核心概念、常用的分层架构,并结合实际业务场景落地DDD设计实现.总而言之,DDD是一种软件设计的思想和方法论,其核心是将业务抽象成领域模型,并以领域为核心驱动力构建软件设计体系,将复杂的业务模型抽象化,非常适合微服务架构下的开发设计.