第7章:DDD领域驱动设计
什么是DDD
定义
领域驱动设计(Domain-Driven Design,DDD):一种软件开发方法论,强调以业务领域为核心进行建模和设计。
核心思想:
传统开发:
需求 → 数据库设计 → 编码
DDD:
业务领域 → 领域模型 → 代码实现
↑ ↑ ↑
专家 统一语言 表达式代码
为什么需要DDD
传统开发的问题:
1. 贫血模型(Anemic Model):
class User {
long id;
String name;
// 只有get/set,无业务逻辑
}
class UserService {
void transfer(User from, User to, Money amount) {
// 业务逻辑全在Service层
from.setBalance(from.getBalance() - amount);
to.setBalance(to.getBalance() + amount);
}
}
问题:
业务逻辑分散在Service层
领域对象只是数据容器
缺乏封装,容易产生不一致状态
2. 数据库驱动设计:
- 先设计表结构
- 再编写业务逻辑
- 结果:业务被数据库约束
DDD的优势:
领域模型充血(Rich Domain Model)
业务逻辑聚集在领域对象中
统一语言(Ubiquitous Language)
面向对象,高内聚低耦合
战略设计
限界上下文(Bounded Context)
定义:明确的边界,在边界内模型具有特定的含义
示例:电商系统中的"订单"
┌──────────────────────────────────────────┐
│ 订单上下文(Order Context) │
│ Order: { id, items, total, status } │
│ 关注:订单状态流转、金额计算 │
└──────────────────────────────────────────┘
┌──────────────────────────────────────────┐
│ 物流上下文(Shipping Context) │
│ Order: { id, address, weight } │
│ 关注:配送地址、重量 │
└──────────────────────────────────────────┘
┌──────────────────────────────────────────┐
│ 财务上下文(Finance Context) │
│ Order: { id, amount, payment_method } │
│ 关注:金额、支付方式、对账 │
└──────────────────────────────────────────┘
同一个"订单",在不同上下文中含义不同!
代码示例:
// 订单上下文
package order
type Order struct {
ID string
Items []OrderItem
TotalPrice Money
Status OrderStatus
CreatedAt time.Time
}
func (o *Order) Calculate Total() Money {
total := Money{Amount: 0}
for _, item := range o.Items {
total = total.Add(item.Price.Multiply(item.Quantity))
}
return total
}
func (o *Order) Cancel() error {
if o.Status != StatusPending {
return errors.New("only pending order can be cancelled")
}
o.Status = StatusCancelled
return nil
}
// 物流上下文
package shipping
type ShippingOrder struct {
OrderID string
RecipientName string
Address Address
Weight Weight
ShippingStatus ShippingStatus
}
func (so *ShippingOrder) Ship() error {
if so.ShippingStatus != StatusPreparing {
return errors.New("order is not ready to ship")
}
so.ShippingStatus = StatusShipped
return nil
}
上下文映射(Context Mapping)
上下文间的关系:
1. 共享内核(Shared Kernel)
Context A ←→ Context B
共享部分领域模型
示例:订单和支付共享"金额"概念
2. 客户-供应商(Customer-Supplier)
Context A (上游) → Context B (下游)
示例:订单服务 → 库存服务
3. 防腐层(Anti-Corruption Layer,ACL)
Context A → [ACL] → External System
作用:隔离外部系统,防止污染领域模型
示例:
type PaymentACL struct {
thirdPartyPayment *ThirdPartyPaymentSDK
}
func (acl *PaymentACL) CreatePayment(order *Order) (*Payment, error) {
// 将领域模型转换为第三方格式
request := acl.toThirdPartyRequest(order)
response := acl.thirdPartyPayment.Pay(request)
// 将第三方响应转换回领域模型
return acl.toPayment(response), nil
}
战术设计
核心构建块
1. 实体(Entity)
定义:有唯一标识的对象,生命周期中属性可能变化,但标识不变
特征:
- 有唯一ID
- 可变性
- 连续性(生命周期)
代码示例:
// 订单实体
type Order struct {
id OrderID // 唯一标识
userID UserID
items []OrderItem
totalPrice Money
status OrderStatus
createdAt time.Time
updatedAt time.Time
}
// 构造函数(工厂方法)
func NewOrder(userID UserID, items []OrderItem) (*Order, error) {
if len(items) == 0 {
return nil, errors.New("order must have at least one item")
}
order := &Order{
id: NewOrderID(),
userID: userID,
items: items,
status: StatusPending,
createdAt: time.Now(),
updatedAt: time.Now(),
}
order.calculateTotal()
return order, nil
}
// 业务方法
func (o *Order) AddItem(item OrderItem) error {
if o.status != StatusPending {
return errors.New("cannot add item to non-pending order")
}
o.items = append(o.items, item)
o.calculateTotal()
o.updatedAt = time.Now()
return nil
}
func (o *Order) Pay() error {
if o.status != StatusPending {
return errors.New("order is not pending")
}
o.status = StatusPaid
o.updatedAt = time.Now()
// 发布领域事件
o.publishEvent(OrderPaidEvent{OrderID: o.id})
return nil
}
func (o *Order) Cancel() error {
if o.status == StatusShipped || o.status == StatusCompleted {
return errors.New("cannot cancel shipped or completed order")
}
o.status = StatusCancelled
o.updatedAt = time.Now()
return nil
}
// 私有方法
func (o *Order) calculateTotal() {
total := Money{Amount: 0}
for _, item := range o.items {
total = total.Add(item.SubTotal())
}
o.totalPrice = total
}
// 等价性(基于ID)
func (o *Order) Equals(other *Order) bool {
return o.id == other.id
}
2. 值对象(Value Object)
定义:无唯一标识,通过属性值来区分,不可变
特征:
- 无ID
- 不可变(Immutable)
- 通过值比较相等性
代码示例:
// 金额值对象
type Money struct {
Amount int64 // 以分为单位
Currency string
}
// 不可变:所有操作返回新对象
func (m Money) Add(other Money) Money {
if m.Currency != other.Currency {
panic("cannot add money with different currencies")
}
return Money{
Amount: m.Amount + other.Amount,
Currency: m.Currency,
}
}
func (m Money) Multiply(multiplier int) Money {
return Money{
Amount: m.Amount * int64(multiplier),
Currency: m.Currency,
}
}
// 值比较
func (m Money) Equals(other Money) bool {
return m.Amount == other.Amount && m.Currency == other.Currency
}
// 地址值对象
type Address struct {
province string
city string
district string
street string
zipCode string
}
func NewAddress(province, city, district, street, zipCode string) (Address, error) {
// 验证规则
if province == "" || city == "" {
return Address{}, errors.New("province and city are required")
}
return Address{
province: province,
city: city,
district: district,
street: street,
zipCode: zipCode,
}, nil
}
func (a Address) FullAddress() string {
return fmt.Sprintf("%s%s%s%s", a.province, a.city, a.district, a.street)
}
// 值比较
func (a Address) Equals(other Address) bool {
return a.province == other.province &&
a.city == other.city &&
a.district == other.district &&
a.street == other.street &&
a.zipCode == other.zipCode
}
实体 vs 值对象:
| 对比维度 | 实体 | 值对象 |
|---|---|---|
| 标识 | 有唯一ID | 无ID,通过值比较 |
| 可变性 | 可变 | 不可变 |
| 相等性 | ID相等即相等 | 所有属性相等才相等 |
| 示例 | 订单、用户、商品 | 金额、地址、日期范围 |
3. 聚合(Aggregate)
定义:一组相关对象的集合,作为数据修改的单元
聚合根(Aggregate Root):聚合的入口,外部只能通过聚合根访问聚合内部对象
┌─────────────────────────────────────┐
│ Order (聚合根) │
│ - id: OrderID │
│ - items: []OrderItem │
│ - payment: Payment │
│ + AddItem() │
│ + RemoveItem() │
│ + Pay() │
└─────────────────────────────────────┘
│
├─→ OrderItem (聚合内部对象)
│ - productID
│ - quantity
│ - price
│
└─→ Payment (聚合内部对象)
- amount
- method
- status
规则:
1. 外部只能持有聚合根的引用
2. 聚合内部对象只能通过聚合根访问
3. 聚合边界内保证一致性
代码示例:
// 订单聚合根
type Order struct {
id OrderID
items []*OrderItem // 聚合内部对象
payment *Payment // 聚合内部对象
status OrderStatus
}
// 正确:通过聚合根操作
func (o *Order) AddItem(productID string, quantity int, price Money) error {
item := &OrderItem{
productID: productID,
quantity: quantity,
price: price,
}
o.items = append(o.items, item)
o.recalculateTotal()
return nil
}
// 正确:聚合根保证一致性
func (o *Order) Pay(method PaymentMethod, amount Money) error {
// 验证金额是否匹配
if !amount.Equals(o.GetTotalPrice()) {
return errors.New("payment amount mismatch")
}
// 创建支付对象
o.payment = &Payment{
amount: amount,
method: method,
status: PaymentStatusPending,
}
o.status = OrderStatusPaid
return nil
}
// 错误:直接操作内部对象
// order.items[0].quantity = 10 // 绕过聚合根,破坏一致性
// 错误:外部持有内部对象引用
// item := order.items[0]
// item.price = newPrice // 破坏封装
聚合设计原则:
- 小聚合优于大聚合
大聚合(订单包含用户、商品、库存...)
小聚合(订单只包含订单项和支付)
- 通过ID引用其他聚合
type Order struct {
id OrderID
userID UserID // 引用用户ID,而非用户对象
items []*OrderItem
}
- 最终一致性跨聚合
订单聚合 ─[事件]→ 库存聚合
不在同一事务中,使用事件保证最终一致性
4. 领域服务(Domain Service)
定义:无状态的服务,协调多个聚合或执行不属于任何聚合的业务逻辑
何时使用:
- 操作涉及多个聚合
- 业务逻辑不自然地属于某个实体或值对象
代码示例:
// 转账领域服务
type TransferService struct {
accountRepo AccountRepository
}
func (ts *TransferService) Transfer(fromAccountID, toAccountID AccountID, amount Money) error {
// 1. 加载两个聚合
fromAccount, err := ts.accountRepo.FindByID(fromAccountID)
if err != nil {
return err
}
toAccount, err := ts.accountRepo.FindByID(toAccountID)
if err != nil {
return err
}
// 2. 执行转账(协调两个聚合)
if err := fromAccount.Debit(amount); err != nil {
return err
}
if err := toAccount.Credit(amount); err != nil {
// 回滚
fromAccount.Credit(amount)
return err
}
// 3. 持久化
ts.accountRepo.Save(fromAccount)
ts.accountRepo.Save(toAccount)
return nil
}
5. 领域事件(Domain Event)
定义:领域中发生的重要业务事件
作用:
- 解耦聚合
- 实现最终一致性
- 审计日志
代码示例:
// 领域事件接口
type DomainEvent interface {
OccurredOn() time.Time
EventType() string
}
// 订单已支付事件
type OrderPaidEvent struct {
orderID OrderID
amount Money
occurredAt time.Time
}
func (e OrderPaidEvent) OccurredOn() time.Time {
return e.occurredAt
}
func (e OrderPaidEvent) EventType() string {
return "order.paid"
}
// 实体发布事件
type Order struct {
id OrderID
events []DomainEvent // 未提交的事件
}
func (o *Order) Pay(amount Money) error {
// 业务逻辑
o.status = OrderStatusPaid
// 发布事件
event := OrderPaidEvent{
orderID: o.id,
amount: amount,
occurredAt: time.Now(),
}
o.events = append(o.events, event)
return nil
}
func (o *Order) GetUncommittedEvents() []DomainEvent {
return o.events
}
func (o *Order) ClearEvents() {
o.events = nil
}
// 事件发布器
type EventPublisher interface {
Publish(event DomainEvent) error
}
// 应用层发布事件
func (app *OrderApplication) PayOrder(orderID OrderID, amount Money) error {
// 1. 加载聚合
order, _ := app.orderRepo.FindByID(orderID)
// 2. 执行业务
order.Pay(amount)
// 3. 保存聚合
app.orderRepo.Save(order)
// 4. 发布事件
for _, event := range order.GetUncommittedEvents() {
app.eventPublisher.Publish(event)
}
order.ClearEvents()
return nil
}
// 事件处理器(其他聚合响应事件)
type OrderPaidEventHandler struct {
inventoryService *InventoryService
}
func (h *OrderPaidEventHandler) Handle(event OrderPaidEvent) error {
// 扣减库存
return h.inventoryService.DeductStock(event.orderID)
}
分层架构
经典四层架构
┌─────────────────────────────────────┐
│ User Interface Layer │
│ (用户界面层) │
│ - HTTP Handler │
│ - GraphQL Resolver │
│ - gRPC Service │
└─────────────────────────────────────┘
↓
┌─────────────────────────────────────┐
│ Application Layer │
│ (应用层) │
│ - Use Case │
│ - Application Service │
│ - DTO Transformation │
└─────────────────────────────────────┘
↓
┌─────────────────────────────────────┐
│ Domain Layer │
│ (领域层) ← 核心 │
│ - Entity │
│ - Value Object │
│ - Aggregate │
│ - Domain Service │
│ - Domain Event │
└─────────────────────────────────────┘
↓
┌─────────────────────────────────────┐
│ Infrastructure Layer │
│ (基础设施层) │
│ - Repository Implementation │
│ - Database │
│ - Message Queue │
│ - External Service │
└─────────────────────────────────────┘
依赖方向:
UI → Application → Domain ← Infrastructure
核心原则:
- 领域层不依赖任何外层
- 基础设施层依赖领域层(实现接口)
代码示例:
// ========== Domain Layer ==========
package domain
// 订单聚合根
type Order struct {
id OrderID
userID UserID
items []*OrderItem
status OrderStatus
}
func (o *Order) Pay() error {
// 领域逻辑
return nil
}
// 仓储接口(定义在领域层)
type OrderRepository interface {
FindByID(id OrderID) (*Order, error)
Save(order *Order) error
}
// ========== Application Layer ==========
package application
type OrderApplicationService struct {
orderRepo domain.OrderRepository // 依赖接口
eventBus EventBus
}
// 用例:支付订单
func (app *OrderApplicationService) PayOrder(orderID string) error {
// 1. 加载聚合
order, err := app.orderRepo.FindByID(OrderID(orderID))
if err != nil {
return err
}
// 2. 执行领域逻辑
if err := order.Pay(); err != nil {
return err
}
// 3. 持久化
if err := app.orderRepo.Save(order); err != nil {
return err
}
// 4. 发布事件
app.eventBus.Publish(OrderPaidEvent{OrderID: order.id})
return nil
}
// ========== Infrastructure Layer ==========
package infrastructure
// 仓储实现
type MySQLOrderRepository struct {
db *sql.DB
}
func (repo *MySQLOrderRepository) FindByID(id domain.OrderID) (*domain.Order, error) {
// 查询数据库
row := repo.db.QueryRow("SELECT * FROM orders WHERE id = ?", id)
// 转换为领域对象
var orderDO OrderDO
row.Scan(&orderDO)
return repo.toDomain(orderDO), nil
}
func (repo *MySQLOrderRepository) Save(order *domain.Order) error {
// 转换为数据对象
orderDO := repo.toDO(order)
// 保存到数据库
_, err := repo.db.Exec("INSERT INTO orders ...", orderDO)
return err
}
// ========== User Interface Layer ==========
package interfaces
type OrderHandler struct {
orderApp *application.OrderApplicationService
}
func (h *OrderHandler) PayOrder(w http.ResponseWriter, r *http.Request) {
var req PayOrderRequest
json.NewDecoder(r.Body).Decode(&req)
// 调用应用层
err := h.orderApp.PayOrder(req.OrderID)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
json.NewEncoder(w).Encode(map[string]string{"status": "success"})
}
聚合设计
聚合边界划分
原则1:不变性规则(Invariants)
聚合边界 = 一致性边界
示例:订单总金额必须等于所有订单项金额之和
→ OrderItem必须在Order聚合内
原则2:小聚合
Order聚合:Order + OrderItem + Payment
Order聚合:Order + User + Product + Inventory
原则3:通过ID引用
type Order struct {
id OrderID
userID UserID // ID引用
user *User // 对象引用(跨聚合)
items []*OrderItem // 聚合内部对象
}
实战案例
电商订单系统DDD建模
领域模型:
package order
import (
"errors"
"time"
)
// ========== 值对象 ==========
// 金额
type Money struct {
amount int64 // 以分为单位
currency string
}
func NewMoney(amount int64, currency string) Money {
return Money{amount: amount, currency: currency}
}
func (m Money) Add(other Money) Money {
return Money{amount: m.amount + other.amount, currency: m.currency}
}
// 地址
type ShippingAddress struct {
recipient string
phone string
address string
}
// ========== 实体 ==========
// 订单项
type OrderItem struct {
productID string
productName string
quantity int
price Money
}
func (item *OrderItem) SubTotal() Money {
return Money{
amount: item.price.amount * int64(item.quantity),
currency: item.price.currency,
}
}
// ========== 聚合根 ==========
type OrderID string
type OrderStatus int
const (
OrderStatusPending OrderStatus = iota
OrderStatusPaid
OrderStatusShipped
OrderStatusCompleted
OrderStatusCancelled
)
// 订单聚合
type Order struct {
// 基本信息
id OrderID
userID string
items []*OrderItem
shippingAddress ShippingAddress
// 金额信息
subtotal Money
discount Money
shipping Money
total Money
// 状态
status OrderStatus
createdAt time.Time
updatedAt time.Time
// 领域事件
events []DomainEvent
}
// 工厂方法
func NewOrder(userID string, items []*OrderItem, address ShippingAddress) (*Order, error) {
if len(items) == 0 {
return nil, errors.New("order must have at least one item")
}
order := &Order{
id: OrderID(generateID()),
userID: userID,
items: items,
shippingAddress: address,
status: OrderStatusPending,
createdAt: time.Now(),
updatedAt: time.Now(),
}
order.calculateAmounts()
order.addEvent(OrderCreatedEvent{OrderID: order.id})
return order, nil
}
// 业务方法:添加商品
func (o *Order) AddItem(productID, productName string, quantity int, price Money) error {
if o.status != OrderStatusPending {
return errors.New("cannot add item to non-pending order")
}
item := &OrderItem{
productID: productID,
productName: productName,
quantity: quantity,
price: price,
}
o.items = append(o.items, item)
o.calculateAmounts()
o.updatedAt = time.Now()
return nil
}
// 业务方法:应用优惠
func (o *Order) ApplyDiscount(discount Money) error {
if o.status != OrderStatusPending {
return errors.New("cannot apply discount to non-pending order")
}
o.discount = discount
o.calculateAmounts()
o.updatedAt = time.Now()
return nil
}
// 业务方法:支付
func (o *Order) Pay() error {
if o.status != OrderStatusPending {
return errors.New("order is not pending")
}
o.status = OrderStatusPaid
o.updatedAt = time.Now()
o.addEvent(OrderPaidEvent{
OrderID: o.id,
Amount: o.total,
})
return nil
}
// 业务方法:发货
func (o *Order) Ship() error {
if o.status != OrderStatusPaid {
return errors.New("order is not paid")
}
o.status = OrderStatusShipped
o.updatedAt = time.Now()
o.addEvent(OrderShippedEvent{OrderID: o.id})
return nil
}
// 业务方法:取消
func (o *Order) Cancel() error {
if o.status == OrderStatusShipped || o.status == OrderStatusCompleted {
return errors.New("cannot cancel shipped or completed order")
}
o.status = OrderStatusCancelled
o.updatedAt = time.Now()
o.addEvent(OrderCancelledEvent{OrderID: o.id})
return nil
}
// 私有方法:计算金额
func (o *Order) calculateAmounts() {
subtotal := Money{amount: 0, currency: "CNY"}
for _, item := range o.items {
subtotal = subtotal.Add(item.SubTotal())
}
o.subtotal = subtotal
o.total = subtotal.Add(o.shipping).Add(Money{amount: -o.discount.amount, currency: "CNY"})
}
// 领域事件
func (o *Order) addEvent(event DomainEvent) {
o.events = append(o.events, event)
}
func (o *Order) GetEvents() []DomainEvent {
return o.events
}
func (o *Order) ClearEvents() {
o.events = nil
}
// ========== 仓储接口 ==========
type OrderRepository interface {
FindByID(id OrderID) (*Order, error)
FindByUserID(userID string) ([]*Order, error)
Save(order *Order) error
Delete(id OrderID) error
}
// ========== 领域服务 ==========
// 订单定价服务
type OrderPricingService struct {
promotionRepo PromotionRepository
}
func (s *OrderPricingService) CalculateDiscount(order *Order) (Money, error) {
// 查询适用的促销活动
promotions, _ := s.promotionRepo.FindApplicable(order)
maxDiscount := Money{amount: 0, currency: "CNY"}
for _, promotion := range promotions {
discount := promotion.Calculate(order)
if discount.amount > maxDiscount.amount {
maxDiscount = discount
}
}
return maxDiscount, nil
}
应用层:
package application
type OrderApplicationService struct {
orderRepo order.OrderRepository
pricingService *order.OrderPricingService
eventBus EventBus
}
// 用例:创建订单
func (app *OrderApplicationService) CreateOrder(cmd CreateOrderCommand) (string, error) {
// 1. 构建领域对象
items := make([]*order.OrderItem, 0)
for _, item := range cmd.Items {
items = append(items, &order.OrderItem{
ProductID: item.ProductID,
ProductName: item.ProductName,
Quantity: item.Quantity,
Price: order.NewMoney(item.Price, "CNY"),
})
}
address := order.ShippingAddress{
Recipient: cmd.Recipient,
Phone: cmd.Phone,
Address: cmd.Address,
}
// 2. 创建订单聚合
newOrder, err := order.NewOrder(cmd.UserID, items, address)
if err != nil {
return "", err
}
// 3. 计算优惠
discount, _ := app.pricingService.CalculateDiscount(newOrder)
newOrder.ApplyDiscount(discount)
// 4. 持久化
if err := app.orderRepo.Save(newOrder); err != nil {
return "", err
}
// 5. 发布事件
for _, event := range newOrder.GetEvents() {
app.eventBus.Publish(event)
}
return string(newOrder.ID()), nil
}
// 用例:支付订单
func (app *OrderApplicationService) PayOrder(orderID string) error {
// 1. 加载聚合
existingOrder, err := app.orderRepo.FindByID(order.OrderID(orderID))
if err != nil {
return err
}
// 2. 执行业务逻辑
if err := existingOrder.Pay(); err != nil {
return err
}
// 3. 持久化
if err := app.orderRepo.Save(existingOrder); err != nil {
return err
}
// 4. 发布事件
for _, event := range existingOrder.GetEvents() {
app.eventBus.Publish(event)
}
return nil
}
面试问答
DDD中的实体和值对象有什么区别?
答案:
| 维度 | 实体 | 值对象 |
|---|---|---|
| 标识 | 有唯一ID | 无ID |
| 可变性 | 可变 | 不可变 |
| 相等性 | ID相等 | 值相等 |
| 生命周期 | 有连续性 | 无连续性 |
| 示例 | 订单、用户 | 金额、地址 |
示例:
// 实体:订单
type Order struct {
id OrderID // 唯一标识
total Money
}
// ID相等即相等
func (o *Order) Equals(other *Order) bool {
return o.id == other.id
}
// 值对象:金额
type Money struct {
amount int64
currency string
}
// 所有属性相等才相等
func (m Money) Equals(other Money) bool {
return m.amount == other.amount && m.currency == other.currency
}
// 不可变:返回新对象
func (m Money) Add(other Money) Money {
return Money{amount: m.amount + other.amount, currency: m.currency}
}
什么是贫血模型和充血模型?
答案:
贫血模型(Anemic Model):
// 对象只有数据,无行为
type Order struct {
ID string
Total float64
Status string
}
// 业务逻辑在Service层
type OrderService struct{}
func (s *OrderService) Pay(order *Order) {
order.Status = "PAID" // 直接修改字段
// 缺乏封装,容易产生不一致状态
}
充血模型(Rich Domain Model,DDD推荐):
// 对象包含数据和行为
type Order struct {
id OrderID
total Money
status OrderStatus
}
// 业务逻辑在领域对象中
func (o *Order) Pay() error {
if o.status != StatusPending {
return errors.New("order is not pending")
}
o.status = StatusPaid
return nil
}
// 优势:
// 封装性强
// 业务规则集中
// 不易产生不一致状态
如何划分聚合边界?
答案:
原则:
- 一致性边界:
聚合内部必须保持强一致性
示例:订单总金额 = 所有订单项金额之和
→ OrderItem必须在Order聚合内
- 事务边界:
一个事务只修改一个聚合
创建订单(修改Order聚合)
创建订单 + 扣减库存(修改Order和Inventory两个聚合)
- 小聚合:
合理:Order + OrderItem
过大:Order + User + Product + Inventory
示例:
// 正确的聚合设计
type Order struct {
id OrderID
userID UserID // ID引用,不包含User对象
items []*OrderItem // 聚合内部对象
}
// 跨聚合操作:使用领域事件
func (o *Order) Pay() error {
o.status = StatusPaid
// 发布事件,异步扣减库存
o.addEvent(OrderPaidEvent{OrderID: o.id})
return nil
}
// 库存聚合响应事件
type OrderPaidEventHandler struct {
inventoryRepo InventoryRepository
}
func (h *OrderPaidEventHandler) Handle(event OrderPaidEvent) {
// 扣减库存(另一个事务)
inventory, _ := h.inventoryRepo.FindByOrderID(event.OrderID)
inventory.Deduct()
}
领域事件的作用是什么?
答案:
作用:
- 解耦聚合:
订单支付 → 发布OrderPaidEvent
→ 库存服务监听 → 扣减库存
→ 积分服务监听 → 增加积分
→ 通知服务监听 → 发送通知
订单服务不需要知道下游服务
- 实现最终一致性:
跨聚合操作不在同一事务中
通过事件保证最终一致性
- 审计日志:
所有领域事件可以持久化
用于审计、溯源
实现:
// 领域事件
type OrderPaidEvent struct {
OrderID OrderID
Amount Money
Timestamp time.Time
}
// 实体发布事件
type Order struct {
events []DomainEvent
}
func (o *Order) Pay() error {
o.status = StatusPaid
o.addEvent(OrderPaidEvent{
OrderID: o.id,
Amount: o.total,
Timestamp: time.Now(),
})
return nil
}
// 应用层发布事件到消息队列
func (app *OrderApp) PayOrder(orderID string) error {
order, _ := app.repo.FindByID(orderID)
order.Pay()
app.repo.Save(order)
// 发布事件
for _, event := range order.GetEvents() {
app.eventBus.Publish(event)
}
return nil
}
DDD适合什么样的项目?
答案:
适合:
复杂业务逻辑(金融、电商、ERP)
长期维护的项目
需求频繁变化
团队规模较大
不适合:
CRUD简单应用
短期项目
业务逻辑简单
小团队(学习成本高)
判断标准:
问自己:
1. 业务规则是否复杂?
2. 需求是否频繁变化?
3. 项目是否长期维护?
如果3个问题都是"是" → 考虑DDD
如果都是"否" → 简单架构即可