✅ DDD 的全称是 Domain-Driven Design,中文叫 领域驱动设计。
📖 一、DDD 的基本定义
DDD(Domain-Driven Design) 是一种以“业务领域”为核心的软件设计思想。
它的核心目标是:
👉 用代码表达业务逻辑本身,而不是技术细节。
🧠 二、关键词拆解理解
| 单词 | 含义 | 对应代码层 |
|---|---|---|
| Domain(领域) | 指业务范围或业务模型(如订单、用户、支付) | internal/domain/ |
| Driven(驱动) | 表示系统的设计、架构、代码组织,都围绕领域模型来驱动 | 整个架构理念 |
| Design(设计) | 指通过分层、抽象、建模,把复杂业务变得可维护、可扩展 | 系统分层结构 |
🧱 三、DDD 的核心思想
一句话总结:
让开发者与业务专家“说同一种语言”,用代码直接表达业务规则。
它提倡:
- 以业务逻辑为中心,而不是数据库或框架;
- 通过模型(Entity、Value Object、Aggregate)表达业务概念;
- 通过分层(Domain / Usecase / Data / Interface)实现解耦;
- 通过 Repository 把领域模型与基础设施隔离。
🧩 四、DDD 在代码层的典型结构(以 Kratos 为例)
internal/
├── domain/ # 领域层(实体、值对象、仓储接口)
│ └── order/
│ ├── entity.go
│ ├── value.go
│ └── repo.go
├── usecase/ # 应用层(用例逻辑)
│ └── order_usecase.go
├── data/ # 基础设施层(实现仓储、数据库、缓存等)
│ └── order_repo.go
└── service/ # 接口层(gRPC/HTTP 控制器)
└── order_service.go
🔄 五、和传统分层的区别
| 对比点 | 传统三层架构 | DDD 分层架构 |
|---|---|---|
| 关注点 | 技术(Controller、Service、DAO) | 业务(Domain、Usecase、Repository) |
| 代码驱动力 | 框架驱动 | 领域驱动 |
| 可测试性 | 较低 | 高(接口分层、Mock仓储) |
| 可维护性 | 逻辑易散落 | 高内聚、低耦合 |
| 适用场景 | 简单 CRUD 系统 | 复杂业务系统(如订单、结算、审批流程) |
💬 六、总结一句话:
DDD(Domain-Driven Design) 是一种让软件结构直接反映业务逻辑的设计方法。
它通过“领域模型 + 明确分层 + 接口解耦”,让系统既符合业务,又具备良好的可维护性和可演进性。
是否希望我接着帮你画一张「DDD 四层架构关系图(含箭头和职责说明)」?
👉 一张图你就能完全看懂 DDD 各层之间的作用和调用方向。
You said:
Aggregate 举个例子
ChatGPT said:
好的,我们来讲 Aggregate(聚合) ,这是 DDD 里一个非常重要但容易混淆的概念,我会从本质、特征、例子讲清楚。
1️⃣ 什么是 Aggregate(聚合)
聚合 = “一组相关对象的集合,它们作为一个整体被管理,有一个根对象(Aggregate Root)对外提供访问”。
特点:
- **聚合根(Aggregate Root)**是唯一对外暴露的对象,外部只能通过它操作聚合内部的对象;
- 聚合内部对象(实体或值对象)只对聚合根可见;
- 聚合保证业务规则的一致性。
2️⃣ 举例:订单系统
在订单系统中,一个典型聚合是 Order(订单聚合) :
Order (Aggregate Root)
├── OrderItem (实体)
├── Address (值对象)
└── PaymentInfo (值对象或实体)
解释:
- Order 是聚合根,对外提供创建、支付、取消等行为;
- OrderItem 是聚合内部实体,只能通过 Order 修改;
- Address、PaymentInfo 是值对象,属于 Order 聚合的一部分;
- 外部系统(如 UseCase、Service)不能直接修改 OrderItem,必须通过 Order 进行操作。
3️⃣ Go 代码示例
// order.go
package order
import "time"
// 聚合根
type Order struct {
ID string
UserID string
Items []OrderItem
Address Address
Status string
CreatedAt time.Time
}
// 内部实体
type OrderItem struct {
ProductID string
Quantity int
Price float64
}
// 值对象
type Address struct {
Province string
City string
Street string
ZipCode string
}
// 聚合根行为
func (o *Order) AddItem(item OrderItem) {
o.Items = append(o.Items, item)
}
func (o *Order) Cancel() {
if o.Status != "Paid" {
o.Status = "Cancelled"
}
}
func (o *Order) Pay() {
o.Status = "Paid"
}
✅ 关键点:
Order是唯一聚合根,外部只能通过它添加商品、支付或取消订单;OrderItem不暴露在外部,保证数据一致性;Address作为值对象不可变。
4️⃣ 聚合和仓储的关系
通常 仓储只针对聚合根:
type OrderRepository interface {
Save(order *Order) error
FindByID(id string) (*Order, error)
}
注意:你不会单独给
OrderItem创建仓储,因为它是聚合内部对象。
5️⃣ 总结
| 名称 | 作用 | 示例 |
|---|---|---|
| Aggregate Root | 聚合唯一对外访问对象 | Order |
| Entity(内部) | 聚合内部实体 | OrderItem |
| Value Object | 聚合内部不可变对象 | Address、PaymentInfo |
| Repository | 针对聚合根访问数据 | OrderRepository |
一句话理解:
聚合 = 一棵树,聚合根是树根,外界只能通过树根访问叶子节点,保证一致性。
如果你需要,我可以帮你画一张 Order 聚合示意图,把 聚合根、内部实体、值对象 的关系直观展示出来,让概念更清晰。
你希望我画吗?
You said:
聚合根是一个 entity 吗
ChatGPT said:
✅ 是的,聚合根(Aggregate Root)本质上是一个实体(Entity) ,但它有一个特殊职责:它是聚合内部所有对象对外的唯一入口。
我们来拆开理解:
1️⃣ 实体 vs 聚合根
| 特性 | Entity(实体) | Aggregate Root(聚合根) |
|---|---|---|
| 唯一标识 | 有唯一 ID | 也有唯一 ID(本质上是实体) |
| 作用域 | 单个对象 | 管理一个聚合内部的所有实体和值对象 |
| 外部访问 | 可以直接被外部访问 | 外部只能通过聚合根访问内部对象 |
| 行为 | 可以包含业务方法 | 包含业务方法,并保证聚合一致性 |
2️⃣ 聚合根的职责
-
保证聚合内部的一致性
- 比如
Order聚合根负责确保OrderItem的数量、价格总额、状态合法性。
- 比如
-
对外唯一入口
- 外部只能通过
Order操作订单,不能直接修改OrderItem。
- 外部只能通过
-
持久化的单位
- 仓储(Repository)只针对聚合根,而不是聚合内部对象。
3️⃣ 代码示例
// 聚合根
type Order struct { // <- 它是一个实体
ID string // 唯一标识
Status string
Items []OrderItem
}
// 内部实体
type OrderItem struct {
ProductID string
Quantity int
}
// 聚合根方法
func (o *Order) AddItem(item OrderItem) {
o.Items = append(o.Items, item)
}
func (o *Order) Cancel() {
if o.Status != "Paid" {
o.Status = "Cancelled"
}
}
✅ 特点:
Order是实体:有 ID、可以被识别;Order是聚合根:外部只能通过它来操作Items,保证聚合完整性;OrderItem是聚合内部实体,不直接暴露给外部。
4️⃣ 小结
聚合根 = 一个特殊的实体,它管理聚合内部对象、保证一致性,并对外暴露操作接口;
聚合内部实体和值对象只在聚合根内部可见,不直接对外暴露。