视频加载失败

DDD 领域驱动设计入门

973 字
5 分钟
DDD 领域驱动设计入门

DDD(Domain-Driven Design,领域驱动设计) 不是一种框架,而是一种面向复杂业务系统的软件设计方法论。

它的核心思想是:

软件结构应该围绕业务领域(Domain)组织,而不是围绕数据库、接口、框架组织。


为什么会有 DDD#

很多项目一开始都是这样:

controller
service
repository
model

例如:

user_controller.go
user_service.go
user_repo.go

当业务简单时没问题。

但随着需求增加:

用户
订单
支付
优惠券
库存
物流
积分

Service 会越来越大:

func CreateOrder() {
检查库存
检查优惠券
扣减库存
创建订单
创建支付单
发MQ
}

最后:

OrderService
3000行

业务规则散落在:

  • Controller
  • Service
  • SQL
  • MQ Consumer

维护起来很痛苦。

DDD 就是为了解决:

复杂业务逻辑如何长期演进。


DDD 的核心概念#

1. Domain(领域)#

领域就是业务范围。

例如电商:

电商系统
├── 用户领域
├── 订单领域
├── 支付领域
├── 库存领域
└── 物流领域

每个领域独立思考。

“这个东西如果脱离 HTTP、数据库、缓存、消息队列,仍然有业务意义吗?”

  • 如果有,可能属于 domain
  • 如果没有,多半不属于 domain

2. Entity(实体)#

有唯一身份的对象。

例如:

type User struct {
ID int64
Name string
}

即使名字变了:

张三 → 李四

还是同一个用户。

因为:

ID没变

3. Value Object(值对象)#

没有身份。

只关心值。

例如:

type Money struct {
Amount int64
Currency string
}

两个:

100 CNY
100 CNY

就是相等的。


4. Aggregate(聚合)#

DDD最重要概念之一。

例如订单:

Order
├── OrderItem
├── Address
└── PaymentInfo

这些对象一起维护一致性。

形成:

Order Aggregate

聚合根(Aggregate Root)#

外部只能访问:

Order

不能直接改:

OrderItem

例如:

order.AddItem(...)

而不是:

order.Items[0].Count++

这样业务规则集中。


Repository#

Repository 不是 DAO。

DAO:

db.Create(...)

Repository:

type OrderRepository interface {
Save(order *Order)
FindByID(id string)
}

领域层不知道:

MySQL
PostgreSQL
Redis

Domain Service#

有些业务不属于某个实体。

例如:

订单创建
需要:
库存
优惠券
支付

放进 Order 也不合适。

这时:

type OrderDomainService struct {}

负责跨聚合逻辑。


Application Service#

很多人容易混淆。

DDD里通常:

Controller
↓
Application Service
↓
Domain
↓
Repository

Application Service:

负责:

  • 编排流程
  • 事务
  • 调用领域对象

例如:

func CreateOrder(cmd CreateOrderCommand) {
order := domain.NewOrder()
order.AddItem(...)
repo.Save(order)
mq.Publish(...)
}

分层架构#

经典 DDD:

┌──────────────────┐
│ Presentation │
│ Controller/API │
└────────┬─────────┘
▼
┌──────────────────┐
│ Application │
│ Use Case │
└────────┬─────────┘
▼
┌──────────────────┐
│ Domain │
│ Entity │
│ Aggregate │
│ Domain Service │
└────────┬─────────┘
▼
┌──────────────────┐
│ Infrastructure │
│ DB MQ Redis │
└──────────────────┘

Go 项目常见目录#

internal/
├── application
│ └── order
│ └── service.go
├── domain
│ └── order
│ ├── entity.go
│ ├── repository.go
│ └── service.go
├── infrastructure
│ └── repository
│ └── order_repo.go
└── interfaces
└── http
└── order_handler.go

在微服务里怎么用#

假设你之前说的在线判题系统:

gateway
user
problem
judge
sandbox

每个微服务都可以有自己的领域。

例如:

problem-service#

Problem
Tag
SubmissionRule

judge-service#

Submission
JudgeTask
JudgeResult

user-service#

User
Role
Permission

DDD 和 MVC 的区别#

MVC:

Controller
↓
Service
↓
DAO

关注:

代码分层

DDD:

Domain
↓
Aggregate
↓
Business Rules

关注:

业务建模

所以:

MVC 是技术视角,DDD 是业务视角。

两者并不冲突。


什么时候值得引入 DDD#

如果是学习性质的:

Gateway
User
Problem
Judge
Sandbox

我的建议是:

第一阶段#

普通三层:

handler
service
repo

先把功能跑通。


第二阶段#

当出现:

提交代码
↓
创建Submission
↓
发MQ
↓
Judge消费
↓
更新结果
↓
更新排行榜

这种复杂业务流时,

再引入:

Aggregate
Domain Service
Repository

会更容易体会 DDD 的价值。

否则很容易变成:

为了DDD而DDD

写了很多目录和接口,但业务复杂度还没到那个程度。对于一个在线判题系统这类项目,比较合适的路线通常是:

三层架构
↓
理解微服务
↓
事件驱动(MQ)
↓
DDD Lite
↓
完整DDD

这样学习曲线会平滑很多,也更符合实际项目的演进过程。

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

DDD 领域驱动设计入门
https://blog.81vm3.xyz/posts/ddd/
作者
Blume
发布于
2025-10-11
许可协议
CC BY-NC-SA 4.0
Profile Image of the Author
Blume
I build interesting things.
公告
欢迎来到我的博客!
分类
标签
最新动态
站点统计
文章
38
分类
5
标签
98
总字数
23,078
运行时长
0 天
最后活动
0 天前
站点信息
构建平台
Local
博客版本
Firefly v6.16.8
文章许可
CC BY-NC-SA 4.0
文章目录