目录

前言:为什么要学习DDD?

第一章:DDD核心思想

1.1 什么是DDD

1.2 简单对比

传统方式(贫血模型)

DDD方式(富领域模型)

第二章:DDD战略设计(理解业务边界)

2.1 领域(Domain)

2.2 子域(Subdomain)

 2.3 限界上下文(Bounded Context)

第三章:DDD战术设计(代码实现)

3.1 实体(Entity)

3.2 值对象(Value Object)

3.3 聚合(Aggregate)

3.4 领域服务(Domain Service)

3.5 仓储(Repository)

第四章:DDD分层架构

4.1 各层职责

Interface Layer(接口层)

Application Layer(应用层)

Domain Layer(领域层)

Infrastructure Layer(基础设施层)

第五章:实战项目解析

5.1 业务背景

5.2 领域建模

5.3 核心领域对象

5.4 应用服务实现

5.5 基础设施层实现

第六章:DDD的优势与挑战

6.1 优势

6.2 挑战

总结


前言:为什么要学习DDD?

作为一名程序员,你是否遇到过这些问题:

        - 业务逻辑散落在各个Service类中,难以维护?

        - 代码中充斥着贫血模型,实体类只是数据容器?

        - 业务专家说的话和代码实现完全是两个世界?

        - 系统越来越复杂,但代码却越来越难以理解?

如果你有这些困扰,那么DDD(领域驱动设计)正是你需要的解决方案。


第一章:DDD核心思想

1.1 什么是DDD

DDD不是一种技术,而是一种思维方式

  •         以业务为中心,而不是以技术为中心
  •         代码即文档,业务规则直接体现在代码中
  •         建立通用语言,让技术人员和业务专家能够无障碍沟通

        领域驱动设计是Eric Evans在2003年提出的软件设计方法论,其核心思想是将复杂的软件系统建模为业务领域的反映。DDD强调:

        领域优先:以业务领域为设计的出发点和中心

        模型驱动:通过领域模型来指导软件设计  

        协作沟通:建立开发团队与业务专家之间的通用语言

1.2 简单对比

传统方式(贫血模型)

只有数据,没有行为

type BankAccount struct {
    ID      string
    Balance decimal.Decimal
    Status  string
}

业务逻辑在Service层

type BankService struct{}

func (s *BankService) Withdraw(accountID string, amount decimal.Decimal) error {
    account := s.repo.FindByID(accountID)
    if account.Status != "ACTIVE" {
        return errors.New("账户未激活")
    }
    if account.Balance.LessThan(amount) {
        return errors.New("余额不足")
    }
    account.Balance = account.Balance.Sub(amount)
    return s.repo.Save(account)
}

问题分析

        - 领域对象只是数据容器,缺乏行为

        - 业务逻辑分散在Service层,难以维护

        - 违反了面向对象的封装原则

DDD方式(富领域模型)

既有数据,也有行为

type BankAccount struct {
    id      AccountID
    balance Money
    status  AccountStatus
}

// 业务逻辑在领域对象内部
func (a *BankAccount) Withdraw(amount Money) error {
    if !a.IsActive() {
        return ErrAccountNotActive
    }
    if a.balance.LessThan(amount) {
        return ErrInsufficientBalance
    }
    a.balance = a.balance.Subtract(amount)
    return nil
}

func (a *BankAccount) IsActive() bool {
    return a.status == AccountStatusActive
}

在这样的对比中:

  •         DDD方式中,业务规则(如"只有激活的账户才能取款")直接体现在代码中
  •         代码读起来就像在描述业务流程
  •         业务专家看到这样的代码也能理解

第二章:DDD战略设计(理解业务边界)

        在深入代码之前,我们需要先理解业务。这就是DDD的战略设计。

2.1 领域(Domain)

定义

        你的软件要解决的业务问题空间

例子

  1.         电商系统的领域:商品管理、订单处理、支付、物流
  2.         银行系统的领域:账户管理、转账、贷款、风控

2.2 子域(Subdomain)

定义

        领域的细分,通常分为三类

 2.3 限界上下文(Bounded Context)

定义

        明确的概念边界,在此边界内,术语具有特定含义

例子

        同样是"商品"这个概念

- 在商品管理上下文中:商品 = 名称 + 价格 + 库存 + 描述

- 在订单上下文中:商品 = 商品ID + 购买数量 + 单价

- 在推荐上下文中:商品 = 商品ID + 类别 + 标签 + 销量


第三章:DDD战术设计(代码实现)

现在我们来看具体的代码实现模式。

3.1 实体(Entity)

定义

        有唯一标识,生命周期贯穿业务流程的对象

// 订单实体 - 有唯一ID,状态会变化
type Order struct {
    id         OrderID        // 唯一标识
    customerID CustomerID
    items      []OrderItem
    status     OrderStatus
    totalAmount Money
    createdAt  time.Time
}

// 业务行为
func (o *Order) AddItem(productID ProductID, quantity int, unitPrice Money) error {
    if o.status != OrderStatusDraft {
        return ErrOrderNotEditable
    }
    
    item := OrderItem{
        ProductID: productID,
        Quantity:  quantity,
        UnitPrice: unitPrice,
    }
    
    o.items = append(o.items, item)
    o.recalculateTotal()
    return nil
}

func (o *Order) Confirm() error {
    if len(o.items) == 0 {
        return ErrEmptyOrder
    }
    
    o.status = OrderStatusConfirmed
    return nil
}

3.2 值对象(Value Object)

定义

        没有唯一标识,通过属性值区分的不可变对象

// 金额值对象
type Money struct {
    amount   decimal.Decimal
    currency string
}

// 值对象是不可变的,操作返回新对象
func (m Money) Add(other Money) Money {
    if m.currency != other.currency {
        panic("currency mismatch") // 简化处理
    }
    
    return Money{
        amount:   m.amount.Add(other.amount),
        currency: m.currency,
    }
}

func (m Money) Multiply(factor int) Money {
    return Money{
        amount:   m.amount.Mul(decimal.NewFromInt(int64(factor))),
        currency: m.currency,
    }
}

func (m Money) IsZero() bool {
    return m.amount.IsZero()
}

// 创建Money的构造函数
func NewMoney(amount float64, currency string) Money {
    return Money{
        amount:   decimal.NewFromFloat(amount),
        currency: currency,
    }
}
// 地址值对象
type Address struct {
    street   string
    city     string
    zipCode  string
    country  string
}

func (a Address) IsValid() bool {
    return a.street != "" && a.city != "" && a.zipCode != ""
}

3.3 聚合(Aggregate)

定义

        一组相关对象的集合,作为数据修改的单元

// 类型定义
type ProductID string
type OrderID string
type CustomerID string
type OrderStatus string

const (
    OrderStatusDraft     OrderStatus = "DRAFT"
    OrderStatusConfirmed OrderStatus = "CONFIRMED"
    OrderStatusShipped   OrderStatus = "SHIPPED"
)

// 错误定义
var (
    ErrOrderNotEditable = errors.New("订单不可编辑")
    ErrProductNotFound  = errors.New("商品不存在")
)

// 订单项 - 聚合内部实体
type OrderItem struct {
    ProductID ProductID  // 商品ID
    Quantity  int        // 数量
    UnitPrice Money      // 单价
    Subtotal  Money      // 小计
}

// 订单聚合 - Order是聚合根
type Order struct {
    id          OrderID
    customerID  CustomerID
    items       []OrderItem    // 聚合内部对象
    address     Address        // 值对象
    status      OrderStatus
    totalAmount Money
}

// 聚合根控制内部对象的访问
func (o *Order) ChangeQuantity(productID ProductID, newQuantity int) error {
    if o.status != OrderStatusDraft {
        return ErrOrderNotEditable
    }
    
    for i, item := range o.items {
        if item.ProductID == productID {
            if newQuantity <= 0 {
                // 删除商品项
                o.items = append(o.items[:i], o.items[i+1:]...)
            } else {
                // 更新数量
                o.items[i].Quantity = newQuantity
                o.items[i].Subtotal = item.UnitPrice.Multiply(newQuantity)
            }
            
            o.recalculateTotal() // 保证聚合内部一致性
            return nil
        }
    }
    
    return ErrProductNotFound
}

// 私有方法 - 重新计算总金额
func (o *Order) recalculateTotal() {
    var total Money
    for _, item := range o.items {
        total = total.Add(item.Subtotal)
    }
    o.totalAmount = total
}

3.4 领域服务(Domain Service)

定义

        不属于任何实体或值对象的业务逻辑

// 订单定价服务 - 复杂的定价逻辑
type OrderPricingService struct{}

func (s *OrderPricingService) CalculateDiscount(order *Order, customer *Customer) Money {
    var discount Money
    
    // VIP客户折扣
    if customer.IsVIP() {
        discount = order.TotalAmount().Multiply(0.1)
    }
    
    // 满减优惠
    if order.TotalAmount().GreaterThan(NewMoney(100, "CNY")) {
        fullReductionDiscount := NewMoney(20, "CNY")
        if fullReductionDiscount.GreaterThan(discount) {
            discount = fullReductionDiscount
        }
    }
    
    return discount
}

3.5 仓储(Repository)

定义

        封装数据访问逻辑的接口

// 领域层定义接口
type OrderRepository interface {
    Save(order *Order) error
    FindByID(id OrderID) (*Order, error)
    FindByCustomer(customerID CustomerID) ([]*Order, error)
}

// 基础设施层实现接口
type PostgreSQLOrderRepository struct {
    db *sql.DB
}

func (r *PostgreSQLOrderRepository) Save(order *Order) error {
    // 数据库持久化逻辑
    return r.db.Exec("INSERT INTO orders ...")
}

第四章:DDD分层架构

DDD推荐使用四层架构:

internal/

├── interfaces/          # 接口层 - 外部交互

│   └── http/          

│       ├── handler/    # HTTP处理器

│       └── middleware/ # 中间件

├── application/         # 应用层 - 业务流程编排

│   ├── service/        # 应用服务

│   └── dto/            # 数据传输对象

├── domain/             # 领域层 - 核心业务逻辑

│   ├── entity/         # 领域实体

│   ├── repository/     # 仓储接口

│   └── valueobj/       # 值对象

└── infrastructure/     # 基础设施层 - 技术实现

    ├── persistence/    # 数据持久化

    │   ├── postgres/   # PostgreSQL实现

    │   └── bytehouse/  # ByteHouse实现

    └── provider/       # 外部服务提供者

gRPC 是一种远程调用框架,CLI (Command Line Interface)是命令行界面

gRPC 是谷歌开源的高性能远程过程调用(RPC)框架,可以简单理解为:

  • 你在 A 服务器写了一个函数,B 服务器可以像调用本地函数一样调用它(跨服务器 / 跨语言)
  • 基于 HTTP/2 协议,用 Protocol Buffers(PB)做数据序列化(比 JSON 更高效、体积更小)
  • 常见于微服务之间的通信(比如 DDD 架构中不同域的服务调用)

4.1 各层职责

Interface Layer(接口层)

// HTTP控制器
type OrderController struct {
    orderService *application.OrderService
}

func (c *OrderController) CreateOrder(w http.ResponseWriter, r *http.Request) {
    var req CreateOrderRequest
    json.NewDecoder(r.Body).Decode(&req)
    
    // 转换为应用层命令
    cmd := application.CreateOrderCommand{
        CustomerID: req.CustomerID,
        Items:      req.Items,
    }
    
    result, err := c.orderService.CreateOrder(cmd)
    if err != nil {
        http.Error(w, err.Error(), http.StatusBadRequest)
        return
    }
    
    json.NewEncoder(w).Encode(result)
}

Application Layer(应用层)

type OrderService struct {
    orderRepo    domain.OrderRepository
    customerRepo domain.CustomerRepository
    pricingService *domain.OrderPricingService
}

func (s *OrderService) CreateOrder(cmd CreateOrderCommand) (*OrderDTO, error) {
    // 1. 输入验证
    if err := cmd.Validate(); err != nil {
        return nil, err
    }
    
    // 2. 加载领域对象
    customer, err := s.customerRepo.FindByID(cmd.CustomerID)
    if err != nil {
        return nil, err
    }
    
    // 3. 执行业务逻辑
    order := domain.NewOrder(customer.ID(), cmd.Items)
    discount := s.pricingService.CalculateDiscount(order, customer)
    order.ApplyDiscount(discount)
    
    // 4. 持久化
    if err := s.orderRepo.Save(order); err != nil {
        return nil, err
    }
    
    // 5. 返回结果
    return s.toDTO(order), nil
}

Domain Layer(领域层)

// 纯业务逻辑,不依赖外部技术
type Order struct {
    id          OrderID
    customerID  CustomerID
    items       []OrderItem
    status      OrderStatus
    totalAmount Money
}

func (o *Order) ApplyDiscount(discount Money) error {
    if discount.GreaterThan(o.totalAmount) {
        return ErrDiscountTooLarge
    }
    
    o.totalAmount = o.totalAmount.Subtract(discount)
    return nil
}

Infrastructure Layer(基础设施层)

/ 技术实现,可替换
type PostgreSQLOrderRepository struct {
    db *sql.DB
}

func (r *PostgreSQLOrderRepository) Save(order *domain.Order) error {
    // 具体的数据库操作
    return r.db.Exec("INSERT INTO orders ...")
}

第五章:实战项目解析

现在让我们看看在真实项目中是如何应用DDD的。以我们的商品分析项目为例:

5.1 业务背景

我们要构建一个AI驱动的商品合规性分析系统:

        - 输入:商品信息、素材数据、投诉数据

        - 处理:通过AI大模型分析商品是否违规

        - 输出:风险等级、违规类型、违规原因

5.2 领域建模

核心领域:商品合规性分析

支撑领域:素材管理、投诉处理

通用领域:AI服务调用、数据存储

限界上下文划分

商品分析系统

├── 商品上下文(Goods Context)

│   ├── 商品实体(Spu)

│   ├── 商品类型(SpuType)

│   └── 销售状态(SaleStatus)

├── 分析上下文(Analysis Context)

│   ├── 分析结果(GoodsAnalysisResult)

│   ├── 风险等级(RiskLevel)

│   └── 违规类型(ViolationType)

└── AI上下文(AI Context)

    ├── 提示词(Prompt)

    ├── AI请求(ChatRequest)

    └── AI响应(ChatResponse)

5.3 核心领域对象

分析结果实体

// 分析结果实体 - 有唯一标识,状态可变
type GoodsAnalysisResult struct {
    id              int64     // 唯一标识
    spuID           string    // 商品ID
    appID           string    // 店铺ID
    goodsName       string    // 商品名称
    riskLevel       string    // 风险等级
    violationType   string    // 违规类型
    violationReason string    // 违规原因
    nonce           string    // 幂等性标识
    createdAt       time.Time
    updatedAt       time.Time
}

// 业务行为 - 标准化违规类型
func (r *GoodsAnalysisResult) NormalizeViolationType() {
    r.violationType = normalizeViolationType(r.violationType)
}

// 业务查询 - 是否高风险
func (r *GoodsAnalysisResult) IsHighRisk() bool {
    return r.riskLevel == string(RiskLevelSerious)
}

风险等级值对象

// 风险等级值对象 - 不可变,通过值区分
type RiskLevel string

const (
    RiskLevelNoRisk  RiskLevel = "无风险"
    RiskLevelLight   RiskLevel = "轻度风险"
    RiskLevelMedium  RiskLevel = "中度风险"
    RiskLevelSerious RiskLevel = "严重风险"
)

func (r RiskLevel) IsValid() bool {
    switch r {
    case RiskLevelNoRisk, RiskLevelLight, RiskLevelMedium, RiskLevelSerious:
        return true
    default:
        return false
    }
}

func (r RiskLevel) IsSeriousOrAbove() bool {
    return r == RiskLevelSerious
}

5.4 应用服务实现

// 应用服务 - 编排业务流程
type GoodsAnalyzeService struct {
    // 依赖仓储接口,不依赖具体实现
    spuRepo             repository.SpuRepo
    materialRepo        repository.MaterialRepo
    resultRepo          repository.GoodsAnalysisRepo
    metaRouterService   *MetaRouterService // AI服务
}

// 核心业务流程
func (s *GoodsAnalyzeService) Analyze(ctx context.Context, req *dto.GoodsAnalyzeRequest) (*dto.GoodsAnalyzeResponse, error) {
    // 1. 参数验证
    if err := req.Validate(); err != nil {
        return nil, err
    }

    // 2. 加载Prompt(AI提示词)
    prompt, err := s.loadPrompt(ctx, req.WithComplaint)
    if err != nil {
        return nil, err
    }

    // 3. 使用工作流引擎执行分析
    return s.executeAnalysisWorkflow(ctx, req, prompt)
}

// 工作流执行 - 拆解复杂流程
func (s *GoodsAnalyzeService) executeAnalysisWorkflow(
    ctx context.Context,
    req *dto.GoodsAnalyzeRequest,
    prompt *entity.PromptEntity,
) (*dto.GoodsAnalyzeResponse, error) {
    
    // 创建工作流模板
    tmpl := workflow.NewTemplate()
    
    // 定义工作流节点
    tmpl.Add(workflow.NewAgent("load_goods", s.loadGoodsAgent))
    tmpl.Add(workflow.NewAgent("load_materials", s.loadMaterialsAgent),
        workflow.WithDependsOn("load_goods"))
    tmpl.Add(workflow.NewAgent("ai_analyze", s.aiAnalyzeAgent),
        workflow.WithDependsOn("load_materials"))
    tmpl.Add(workflow.NewAgent("save_results", s.saveResultsAgent),
        workflow.WithDependsOn("ai_analyze"))
    
    // 执行工作流
    wf := tmpl.Instantiate()
    if err := wf.Start(ctx); err != nil {
        return nil, err
    }
    
    // 获取结果
    result, err := workflow.Get[*dto.GoodsAnalyzeResponse](wf.GetStore(), "response")
    return result, err
}

5.5 基础设施层实现

仓储实现

// PostgreSQL仓储实现
type GoodsAnalysisRepoImpl struct {
    db *gorm.DB
}

// 实现领域层定义的接口
func (r *GoodsAnalysisRepoImpl) Upsert(ctx context.Context, results []*entity.GoodsAnalysisResult) error {
    if len(results) == 0 {
        return nil
    }
    
    // 转换为持久化对象
    pos := r.toPOs(results)
    
    // 使用数据库的UPSERT功能保证幂等性
    err := r.db.WithContext(ctx).Clauses(clause.OnConflict{
        Columns: []clause.Column{
            {Name: "app_id"}, 
            {Name: "spu_id"}, 
            {Name: "nonce"}, // 幂等性标识
        },
        DoUpdates: clause.AssignmentColumns([]string{
            "goods_name", "risk_level", "violation_type", 
            "violation_reason", "updated_at",
        }),
    }).Create(&pos).Error
    
    if err != nil {
        return fmt.Errorf("保存分析结果失败: %w", err)
    }
    
    return nil
}

// 对象映射 - 领域对象转持久化对象
func (r *GoodsAnalysisRepoImpl) toPO(e *entity.GoodsAnalysisResult) *goodsAnalysisRepoPO {
    var po goodsAnalysisRepoPO
    _ = copier.Copy(&po, e) // 使用copier库简化拷贝
    
    // 处理特殊字段
    po.CreatedAt = time.Now()
    po.UpdatedAt = time.Now()
    
    return &po
}

AI服务集成

// AI网关服务 - 防腐层模式
type MetaRouterService struct {
    baseURL string
    apiKey  string
}

func (s *MetaRouterService) CallChatCompletions(
    ctx context.Context,
    req *dto.MetaRouterChatRequest,
    appID string,
) (*dto.MetaRouterChatResponse, error) {
    
    url := s.baseURL + "/api/v1/chat/completions"
    
    var resp dto.MetaRouterChatResponse
    err := http.Post(url).
        Header("Authorization", "Bearer "+s.apiKey).
        Header("X-App-ID", appID). // 灰度发布支持
        Json(req).
        SendForJson(ctx, &resp)
    
    if err != nil {
        return nil, fmt.Errorf("AI调用失败: %w", err)
    }
    
    return &resp, nil
}

第六章:DDD的优势与挑战

6.1 优势

- 业务逻辑集中在领域对象内部,符合面向对象原则

- 代码即文档,业务规则清晰可见

- 降低了业务逻辑分散的风险

1. 业务表达力强

// 代码直接反映业务概念
func (order *Order) CanBeCancelled() bool {
    return order.status == OrderStatusPending || 
           order.status == OrderStatusConfirmed
}

// 业务专家看到这样的代码也能理解
if order.CanBeCancelled() {
    order.Cancel()
}

2. 可维护性高

  • 业务逻辑集中在领域对象中
  • 修改业务规则时,影响范围可控
  • 代码结构清晰,易于理解

3. 可测试性好

func TestOrder_CanBeCancelled(t *testing.T) {
    // Given
    order := NewOrder("customer-001")
    order.Confirm()
    
    // When & Then
    assert.True(t, order.CanBeCancelled())
    
    // When
    order.Ship()
    
    // Then
    assert.False(t, order.CanBeCancelled())
}

4. 团队协作效率高

  1. 建立了通用语言
  2. 技术人员和业务专家能够无障碍沟通
  3. 代码即文档,减少了文档维护成本

6.2 挑战

1. 学习曲线陡峭

  • 需要理解大量的概念和模式
  • 需要改变传统的编程思维

2. 过度设计的风险

// ❌ 过度抽象的例子
type AbstractOrderProcessor interface {
    ProcessOrder(order Order, context ProcessingContext) ProcessingResult
}

// ✅ 简单直接的例子
type OrderService struct {
    orderRepo OrderRepository
}

func (s *OrderService) ProcessOrder(order *Order) error {
    return s.orderRepo.Save(order)
}

3. 性能考虑

  • 富领域模型可能带来性能开销
  • 需要在业务表达力和性能之间找到平衡

总结

DDD的核心不在于技术,而在于思维方式的转变

        从技术驱动转向业务驱动

        从数据建模转向行为建模

        从代码实现转向问题解决

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐