简介
领域驱动设计(Domain-Driven Design,简称DDD)是一种软件开发方法论,它强调将业务逻辑和核心业务过程放在应用程序的中心。
DDD强调通过理解业务领域,将其映射到软件代码中,以便更好地满足业务需求。它主要包含以下几个核心概念:
- 领域模型(Domain Model): 领域模型是对业务领域的抽象和建模。它由一组实体(Entity)、值对象(Value Object)、聚合(Aggregate)、领域服务(Domain Service)等组成。
- 聚合(Aggregate): 聚合是一组相关对象的集合,它们共同形成了一个有意义的整体。聚合根(Aggregate Root)是聚合的入口点,通过它可以访问聚合内部的其他对象。
- 事件(Event): 事件是领域中发生的重要事实或变化。事件是不可变的,可以用于记录系统中已经发生的事情。
- 命令(Command): 命令是对系统执行某种请求或操作的指令,它会触发相应的业务逻辑。
- 事件溯源(Event Sourcing): 事件溯源是一种通过保存和恢复事件来重构对象状态的方法。在DDD中,事件溯源被用于存储和还原领域模型的状态。
微服务VS DDD架构
微服务是应对系统架构、技术架构上的挑战(主要是可用性/性能),通过注册中心、负载均衡、限流、熔断等方案来应对。
DDD是应对业务架构、工程架构上的挑战:通过战略建模和战术建模的方式将业务架构映射到系统架构上,在响应业务变化调整业务架构时,也随之变化系统架构。
领域
业务范围以及在其中所进行的活动称为领域。
领域可以划分为子域,在领域划分过程中,会不断划分子域,子域按重要程度会被划分成三类:
- 核心域:核心竞争力,业务成功的主要促成因素。
- 通用域:不是核心,被整个业务系统使用。
- 支撑域:不是核心,可以通过购买满足需求。
DDD实现
1、战略建模
(1)内容:从高层次、宏观上去划分和集成限界上下文。
(2)划分界限上下文
- 每个限界上下文专注于解决某个特定的子域的问题,每个子域都对应一个明确的问题,提供独立的价值,每个子域都相对独立,故各个限界上下文之间也是相对独立的。
- 同一个概念,不必总是对应于一个单一模型。
- 子域及其对应的限界上下文中的模型会因为其要解决的问题变化而变化,不会因为其他子域的变化而变化,即低耦合;
- 当一个子域发生变化时,只需要修改其对应限界上下文中的模型,不需要变动其他子域的模型,即高内聚。
(3)限界上下文之间映射关系
合作关系(Partnership):两个上下文紧密合作的关系,一荣俱荣,一损俱损。
共享内核(Shared Kernel):两个上下文依赖部分共享的模型。
客户方-供应方开发(Customer-Supplier Development):上下文之间有组织的上下游依赖。
遵奉者(Conformist):下游上下文只能盲目依赖上游上下文。
防腐层(Anticorruption Layer):一个上下文通过一些适配和转换与另一个上下文交互。
开放主机服务(Open Host Service):定义一种协议来让其他上下文来对本上下文进行访问。
发布语言(Published Language):通常与OHS一起使用,用于定义开放主机的协议。
大泥球(Big Ball of Mud):混杂在一起的上下文关系,边界不清晰。
另谋他路(SeparateWay):两个完全没有任何联系的上下文。
2、战术建模
(1)内容
通过模块、聚合、实体、值对象、领域服务、领域事件等对象来细化限界上下文。
(2)模块
是一种控制限界上下文的手段,在工程中我们一般使用一个模块来表示一个领域的限界上下文。
一般的工程中包的组织方式为{com.公司名.组织架构.业务.上下文.*},这样的组织结构能够明确的将一个上下文限定在包的内部。
(3)实体
当一个对象由其标识(而不是属性)区分时,这种对象称为实体。
(4)值对象
- 当一个对象用于对事务进行描述而没有唯一标识时,它被称作值对象。
- 在实践中,需要保证值对象创建后就不能被修改,即不允许外部再修改其属性
(5)聚合
- 聚合是一组相关对象(实体、值对象)的集合,作为一个整体被外界访问,聚合是为了保证领域内对象之间的一致性问题。
- 聚合边界内必须明确有哪些信息,如果没有这些信息就不能称为一个有效的聚合。
(6)领域服务
- 业务逻辑优先在聚合根边界内完成;聚合根中不合适放置的业务逻辑才考虑放到 DomainService 中。
(7)领域事件
- 领域事件是对领域内发生的活动进行的建模。
DDD代码分层架构
传统三层架构:表示层、业务逻辑层、数据访问层
存在的问题:
- 类的职责不易划分
- 类的依赖关系混乱
- 业务处理逻辑和技术处理逻辑混合在一起
- 一个业务领域的动作分散在多处,不易复用,迭代成本高。
- 事务控制范围太大
DDD分层架构的目的:
- 将技术复杂度和业务复杂度分离,让领域层负责业务复杂度,让应用层和基础设施层负责技术复杂度。
DDD传统四层架构:
- 用户接口层(User Interface)
负责向用户显示信息和解释用户指令,这里的用户可能是用户,也可能是外界其它系统。
- 应用层(Application)
应用层是很薄的一层,理论上不应该有业务规则或逻辑,主要面向用例,通过对领域层对象的编排,实现了用例。
应用层除了负责调用领域层和基础设施层,还需要协调并发控制、事务控制、权限控制等技术事项。
- 领域层(Domain)
领域层的作用是实现企业核心业务逻辑,领域层主要体现领域模型的业务能力,它用来表达业务概念、业务状态和业务规则,与具体的实现技术无关。
- 基础设施层(Infrastructure)
基础层是贯穿所有层的,它的作用就是为其它各层提供通用的技术和基础服务,包括第三方工具、消息中间件、缓存以及数据库持久化等。
四层架构的优化:
依赖倒置原则:
- 高层模块不应该依赖于底层模块,两者都应该依赖于抽象。
- 抽象不应该依赖于实现细节,实现细节应该依赖于接口。
- 按照DIP的原则,领域层和基础设施层都只依赖于由领域模型所定义的抽象接口,这样领域层就可以不再依赖于基础设施层,而基础设施层通过注入抽象接口的实现就完成了对领域层的解耦。
按照依赖倒置原则(DIP),各层的内容如下:
- 用户接口层(User Interface)内容:
- 接口实现类(controller、rpcServiceImpl)
- 消息监听类(listener)
- 应用层(Application)
流程控制是应用层最核心的功能,通常我们会使用责任链的设计模式来对请求进行处理。
责任链模式的优点:可以非常灵活地对处理过程进行拆分,比如可以将参数校验、幂等处理、事务处理、并发控制等拆分到独立的模块里,这些模块很容易被抽取为通用的模块进行复用,大大降低了代码的复杂度。
内容:
- 调用领域层的实体对象完成一个业务。
- 调用repository保存数据。
- 领域层(Domain)内容:
entity
value object
domain service
event publiser/subscriber
repository interface
adapter interface
- 基础设施层(Infrastructure)内容:
repository
DDD分层架构的数据库等基础资源访问,采用了仓储(Repository)设计模式,通过依赖倒置实现各层对基础资源的解耦。仓储又分为两部分:仓储接口和仓储实现。仓储接口放在领域层中,仓储实现放在基础层。Repository的实现一般需要具备以下特性,以便解放领域层和应用层的复杂性。
- 保证entity的唯一性
- 支持延迟更新
- 支持读缓存
mq(发消息)
cache
中间件
adapter
唯有热爱方能抵御岁月漫长。