HiHuo
首页
博客
手册
工具
关于
首页
博客
手册
工具
关于
  • 分布式架构模式

    • 分布式架构模式手册
    • 第1章:分布式一致性
    • 第2章:分布式锁
    • 第3章:分布式协调
    • 第4章:服务发现与注册
    • 第5章:负载均衡
    • 第6章:熔断降级
    • 第7章:DDD领域驱动设计
    • 第8章:CQRS与Event Sourcing

第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  // 破坏封装

聚合设计原则:

  1. 小聚合优于大聚合
 大聚合(订单包含用户、商品、库存...)
 小聚合(订单只包含订单项和支付)
  1. 通过ID引用其他聚合
type Order struct {
    id      OrderID
    userID  UserID     //  引用用户ID,而非用户对象
    items   []*OrderItem
}
  1. 最终一致性跨聚合
订单聚合 ─[事件]→ 库存聚合
不在同一事务中,使用事件保证最终一致性

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
}

// 优势:
//  封装性强
//  业务规则集中
//  不易产生不一致状态

如何划分聚合边界?

答案:

原则:

  1. 一致性边界:
聚合内部必须保持强一致性

示例:订单总金额 = 所有订单项金额之和
→ OrderItem必须在Order聚合内
  1. 事务边界:
一个事务只修改一个聚合

 创建订单(修改Order聚合)
 创建订单 + 扣减库存(修改Order和Inventory两个聚合)
  1. 小聚合:
 合理: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()
}

领域事件的作用是什么?

答案:

作用:

  1. 解耦聚合:
订单支付 → 发布OrderPaidEvent
         → 库存服务监听 → 扣减库存
         → 积分服务监听 → 增加积分
         → 通知服务监听 → 发送通知

订单服务不需要知道下游服务
  1. 实现最终一致性:
跨聚合操作不在同一事务中
通过事件保证最终一致性
  1. 审计日志:
所有领域事件可以持久化
用于审计、溯源

实现:

// 领域事件
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
如果都是"否" → 简单架构即可

Prev
第6章:熔断降级
Next
第8章:CQRS与Event Sourcing