我们的业务逻辑实际上与程序架构、数据库、缓存等严重耦合在一起,我一直觉得这是一种糟糕的设计。在我的想象中,最完美的情况是程序的核心应该用一种接近自然语言的 DSL(领域特定语言)来完整描述业务逻辑。
开发业务的人,谁关心我们的对象是在堆上还是在栈上,谁关心物理服务器是什么型号,甚至我们连数据库提供的功能都不愿多用,想尽可能减少对指定数据库的依赖。现在的服务网格也在剥离程序自身的一些架构方面的控制逻辑。
随着程序设计不断地剥离非业务逻辑的趋势,在不远的未来,早晚会实现我所想象的这种开发模式。
可能说的有点远,有点大。至少,就程序员熟悉的分层这种拆分复杂逻辑的思想来说,我们应该尽可能只采用程序设计语言的基础语法,没有任何外部依赖地,描述业务逻辑。这样的代码不是更容易理解,更方便审计,更便于维护吗?同时还更健壮。
我觉得我的这些想法和 DDD 不谋而合。
注意:DDD 适用于领域复杂度高、需要长期维护和扩展的业务系统。但对于简单的 CRUD 应用,引入了更多抽象,带来不必要的复杂性,还是应该短平快一些。
注意:重点是灵活应用设计思想,让架构为业务服务,而不是为了架构而架构。
核心思想
- 程序的核心的是业务逻辑的描述,贴近业务,而不是整个系统围绕着数据库,然后业务逻辑和技术实现混杂在一起,分散在程序的各处,可能叫做 XXXController、XXXService、XXXUtil。
- 划分子模块(),不同子模块之间通过 API、消息队列、事件驱动等方式进行通信,而不是共享数据。
- 强调统一描述,对问题的描述中不要增加业务实现的部分,增加团队沟通成本。
比如:订单状态包括用户未支付、用户已支付、商家确认、商家备货、商家发货、物流发货、物流配送、用户签收、订单完成、订单取消、订单退款等等状态,存储在数据库里面可能是一个 int 类型字段,
如果在代码中订单状态变更写的是 setStatus(1)、setStatus(2)、setStatus(3),这样做是非常糟糕的。
如果这样的表述出现在内部沟通中,除了做这个开发的人,其他人看到也是一脸懵。
所以状态的状态
领域驱动设计
DDD(Domain-Driven Design,领域驱动设计)是一种软件设计方法,强调软件设计应该以业务领域为中心,而不是以技术为中心。DDD 由 Eric Evans 在 2003 年提出。
领域驱动设计(Domain-Driven Design,简称 DDD)是一种以 业务需求 为核心的软件设计方法,强调 构建符合业务逻辑的领域模型,并通过 明确的边界 和 良好的架构设计 来提升软件的可维护性和可扩展性。DDD 主要包括两个核心部分:战略设计 和 战术设计。
战略设计(Strategic Design)
战略设计关注如何合理划分业务领域,确定子系统的边界,并定义它们之间的关系。核心概念包括:
界限上下文(Bounded Context)
界限上下文是业务系统中的一个独立单元,包含特定的业务逻辑、数据模型和规则。不同上下文之间通过清晰的接口 进行交互,以避免模型混乱。
领域(Domain)与子域(Subdomain)
- 领域(Domain):指整个业务范畴,例如“电子商务”“订单管理”等。
- 子域(Subdomain):将复杂业务拆分为多个独立子域,例如在电子商务系统中,可能包含“商品管理”“订单管理”“支付处理”等子域。
- 核心子域(Core Subdomain):业务的核心竞争力,最需要投入设计和优化的部分。
- 支撑子域(Supporting Subdomain):为核心业务提供支持的部分,例如 CRM、客服管理等。
- 通用子域(Generic Subdomain):可以复用的领域,如用户认证、日志管理等。
战术设计(Tactical Design)
战术设计关注如何在代码层面 实现领域模型,确保业务逻辑的清晰表达和长期可维护性。核心概念包括:
领域模型(Domain Model)
领域模型是对业务逻辑的抽象,主要由以下组成部分:
- 实体(Entity):具有唯一标识(ID)的对象,状态可能随时间变化,例如“订单”“用户”等。
- 值对象(Value Object):无唯一标识,通常用于描述属性,如“地址”“货币金额”。
- 聚合(Aggregate):由多个实体和值对象组成的业务单元,具有一致性约束。
- 聚合根(Aggregate Root):聚合的核心实体,负责维护聚合内部的一致性,对外提供访问接口。
仓储(Repository)
仓储模式用于 管理实体的持久化,屏蔽底层数据库操作,提供对象级的访问方式。例如 OrderRepository
负责管理订单的存取。
领域服务(Domain Service)
当某些业务逻辑无法归属于单个实体时,使用 领域服务(无状态)。例如“计算订单折扣”可能涉及多个对象,可放在 OrderDiscountService
中。
领域事件(Domain Event)
领域事件用于 表示业务中的重要事件,支持异步处理和系统解耦。例如“订单已支付”可以触发多个后续操作(发货、通知用户等)。
其他类似名词
- BDD(Business-Driven Development,业务驱动开发):强调通过业务需求来驱动开发过程,通常使用自然语言编写测试用例。
- BDD(Behavior-Driven Development,行为驱动开发):强调通过行为来定义软件功能,通常使用 Gherkin 语言编写测试用例。
- FDD(Feature-Driven Development,特性驱动开发):强调通过特性来驱动开发过程,每个特性都是一个可交付的增量。
- TDD(Test-Driven Development,测试驱动开发):强调在编写代码之前先编写测试用例,通过测试驱动代码的实现。
参考资料与拓展阅读
- 知乎专栏, 阿里技术, 领域驱动设计详解:是什么、为什么、怎么做?
- 知乎, 什么是领域驱动开发(domain driven development)?
- 领域驱动设计(DDD)的几种典型架构介绍,微信公众号文章链接