领域驱动设计(Domain-Driven Design,DDD)是一种软件开发方法论,由Eric Evans在2003年提出。它强调以业务领域为核心,通过领域模型来驱动软件设计,帮助开发团队更好地理解和表达业务逻辑。
DDD概述 领域驱动设计是一种处理复杂领域软件设计的方法论,它通过建立领域模型来连接业务专家和开发人员,使软件能够准确反映业务需求。
DDD的核心思想 graph TB
A[业务领域] --> B[领域模型]
B --> C[软件实现]
C --> D[业务价值]
D --> A
style A fill:#4DABF7
style B fill:#51CF66
style C fill:#FFE66D
style D fill:#FF6B6B
核心原则:
以领域为中心 :业务领域是软件设计的核心
统一语言(Ubiquitous Language) :业务专家和开发人员使用相同的语言
领域模型驱动 :通过领域模型来表达和实现业务逻辑
分层架构 :将领域逻辑与技术实现分离
DDD解决的问题 传统开发的问题 graph LR
A[需求文档] --> B[开发人员理解]
B --> C[代码实现]
C --> D[业务人员验证]
D -->|不一致| A
style D fill:#FF6B6B
问题:
需求理解偏差
业务逻辑分散
代码难以维护
业务变更困难
DDD的解决方案 graph LR
A[业务专家] --> B[统一语言]
B --> C[领域模型]
C --> D[代码实现]
D --> E[业务验证]
E -->|一致| F[业务价值]
style B fill:#51CF66
style C fill:#51CF66
style E fill:#51CF66
优势:
业务和代码一致
领域逻辑集中
易于理解和维护
快速响应业务变化
DDD的分层架构 DDD采用分层架构,将领域逻辑与技术实现分离。
分层结构 graph TB
A[用户界面层 User Interface] --> B[应用层 Application]
B --> C[领域层 Domain]
B --> D[基础设施层 Infrastructure]
C --> D
style A fill:#FFE66D
style B fill:#4DABF7
style C fill:#51CF66
style D fill:#FF6B6B
1. 用户界面层(User Interface Layer) 职责:
不包含:
2. 应用层(Application Layer) 职责:
协调领域对象完成业务用例
事务管理
安全控制
工作流编排
特点:
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 type OrderApplicationService struct { customerRepo CustomerRepository productRepo ProductRepository orderRepo OrderRepository orderFactory OrderFactory eventPub EventPublisher } func (s *OrderApplicationService) CreateOrder(cmd CreateOrderCommand) error { customer, err := s.customerRepo.FindByID(cmd.CustomerID) if err != nil { return err } product, err := s.productRepo.FindByID(cmd.ProductID) if err != nil { return err } order, err := s.orderFactory.Create(customer, product, cmd.Quantity) if err != nil { return err } if err := s.orderRepo.Save(order); err != nil { return err } event := NewOrderCreatedEvent(order.ID()) return s.eventPub.Publish(event) }
3. 领域层(Domain Layer) 职责:
特点:
核心层,包含所有业务逻辑
不依赖其他层
最稳定的一层
4. 基础设施层(Infrastructure Layer) 职责:
特点:
核心概念:战术设计 1. 实体(Entity) 实体是具有唯一标识的对象,通过ID来区分,即使属性相同,ID不同也是不同的对象。
实体特征 graph TB
A[实体 Entity] --> B[唯一标识]
A --> C[可变状态]
A --> D[生命周期]
style A fill:#51CF66
特征:
有唯一标识(ID)
可变状态
通过ID判断相等性
有生命周期
实体示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 type Order struct { id OrderID customerID CustomerID status OrderStatus items []*OrderItem totalAmount Money domainEvents []DomainEvent } func NewOrder (id OrderID, customerID CustomerID) *Order { return &Order{ id: id, customerID: customerID, status: OrderStatusDraft, items: make ([]*OrderItem, 0 ), totalAmount: NewMoney(0 , "CNY" ), domainEvents: make ([]DomainEvent, 0 ), } } func (o *Order) ID() OrderID { return o.id } func (o *Order) AddItem(product *Product, quantity Quantity) error { if o.status != OrderStatusDraft { return errors.New("只能向草稿订单添加商品" ) } item := NewOrderItem(product, quantity) o.items = append (o.items, item) o.calculateTotal() return nil } func (o *Order) Confirm() error { if len (o.items) == 0 { return errors.New("订单不能为空" ) } o.status = OrderStatusConfirmed event := NewOrderConfirmedEvent(o.id, o.customerID) o.domainEvents = append (o.domainEvents, event) return nil } func (o *Order) calculateTotal() { total := NewMoney(0 , "CNY" ) for _, item := range o.items { total = total.Add(item.Subtotal()) } o.totalAmount = total } func (o *Order) DomainEvents() []DomainEvent { return o.domainEvents } func (o *Order) ClearDomainEvents() { o.domainEvents = make ([]DomainEvent, 0 ) }
2. 值对象(Value Object) 值对象是没有唯一标识的对象,通过属性值来判断相等性。
值对象特征 graph TB
A[值对象 Value Object] --> B[无唯一标识]
A --> C[不可变]
A --> D[通过值判断相等]
style A fill:#4DABF7
特征:
没有唯一标识
不可变(Immutable)
通过属性值判断相等性
可以替换
值对象示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 type Money struct { amount float64 currency string } func NewMoney (amount float64 , currency string ) Money { if amount < 0 { panic ("金额必须大于等于0" ) } return Money{ amount: amount, currency: currency, } } func (m Money) Amount() float64 { return m.amount } func (m Money) Currency() string { return m.currency } func (m Money) Add(other Money) Money { if m.currency != other.currency { panic ("货币类型不一致" ) } return NewMoney(m.amount+other.amount, m.currency) } func (m Money) Multiply(factor float64 ) Money { return NewMoney(m.amount*factor, 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 detail string } func NewAddress (province, city, district, detail string ) Address { return Address{ province: province, city: city, district: district, detail: detail, } } func (a Address) Province() string { return a.province } func (a Address) City() string { return a.city } func (a Address) District() string { return a.district } func (a Address) Detail() string { return a.detail } func (a Address) Equals(other Address) bool { return a.province == other.province && a.city == other.city && a.district == other.district && a.detail == other.detail }
实体 vs 值对象
特征
实体
值对象
标识
有唯一ID
无ID
可变性
可变
不可变
相等性
通过ID判断
通过值判断
生命周期
有生命周期
可替换
示例
订单、用户
金额、地址
3. 聚合(Aggregate) 聚合是一组相关对象的集合,作为数据修改的单元。聚合根(Aggregate Root)是聚合的入口。
聚合结构 graph TB
A[聚合根 Order] --> B[实体 OrderItem]
A --> C[值对象 Address]
A --> D[值对象 Money]
E[外部对象] -.->|只能通过聚合根访问| A
style A fill:#FF6B6B
style B fill:#51CF66
style C fill:#4DABF7
style D fill:#4DABF7
聚合原则:
聚合根是唯一入口 :外部只能通过聚合根访问聚合内部
聚合内一致性 :聚合内保证强一致性
聚合间最终一致性 :聚合间通过ID引用,保证最终一致性
事务边界 :一个事务只能修改一个聚合
聚合示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 type Order struct { id OrderID customerID CustomerID items []*OrderItem shippingAddress Address totalAmount Money domainEvents []DomainEvent } func NewOrder (id OrderID, customerID CustomerID) *Order { return &Order{ id: id, customerID: customerID, items: make ([]*OrderItem, 0 ), totalAmount: NewMoney(0 , "CNY" ), domainEvents: make ([]DomainEvent, 0 ), } } func (o *Order) AddItem(product *Product, quantity Quantity) { for _, item := range o.items { if item.ProductID().Equals(product.ID()) { item.IncreaseQuantity(quantity) o.calculateTotal() return } } item := NewOrderItem(product, quantity) o.items = append (o.items, item) o.calculateTotal() } func (o *Order) RemoveItem(productID ProductID) { newItems := make ([]*OrderItem, 0 ) for _, item := range o.items { if !item.ProductID().Equals(productID) { newItems = append (newItems, item) } } o.items = newItems o.calculateTotal() } func (o *Order) calculateTotal() { total := NewMoney(0 , "CNY" ) for _, item := range o.items { total = total.Add(item.Subtotal()) } o.totalAmount = total } type OrderItem struct { productID ProductID quantity Quantity unitPrice Money } func NewOrderItem (product *Product, quantity Quantity) *OrderItem { return &OrderItem{ productID: product.ID(), quantity: quantity, unitPrice: product.Price(), } } func (oi *OrderItem) ProductID() ProductID { return oi.productID } func (oi *OrderItem) Quantity() Quantity { return oi.quantity } func (oi *OrderItem) Subtotal() Money { return oi.unitPrice.Multiply(float64 (oi.quantity)) } func (oi *OrderItem) IncreaseQuantity(additional Quantity) { oi.quantity += additional }
聚合设计原则 graph TB
A[聚合设计] --> B[保持聚合小]
A --> C[通过ID引用其他聚合]
A --> D[最终一致性]
A --> E[一个事务一个聚合]
style A fill:#FFE66D
原则:
保持聚合小 :聚合应该尽可能小,只包含必须一起修改的对象
通过ID引用 :聚合间通过ID引用,不直接持有对象引用
最终一致性 :跨聚合的操作使用最终一致性
事务边界 :一个事务只修改一个聚合
4. 领域服务(Domain Service) 当业务逻辑不属于任何实体或值对象时,使用领域服务。
领域服务特征 graph TB
A[领域服务] --> B[无状态]
A --> C[业务逻辑]
A --> D[跨聚合操作]
style A fill:#9B59B6
使用场景:
业务逻辑涉及多个实体
业务逻辑不属于任何实体
需要访问基础设施(如计算、外部服务)
领域服务示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 type TransferService struct { eventPub EventPublisher } func NewTransferService (eventPub EventPublisher) *TransferService { return &TransferService{ eventPub: eventPub, } } func (s *TransferService) Transfer(fromAccount, toAccount *Account, amount Money) error { if fromAccount.Balance().Amount() < amount.Amount() { return errors.New("余额不足" ) } if err := fromAccount.Withdraw(amount); err != nil { return err } if err := toAccount.Deposit(amount); err != nil { return err } event := NewMoneyTransferredEvent( fromAccount.ID(), toAccount.ID(), amount, ) return s.eventPub.Publish(event) } type PricingService struct { discountRepo DiscountRepository } func NewPricingService (discountRepo DiscountRepository) *PricingService { return &PricingService{ discountRepo: discountRepo, } } func (s *PricingService) CalculatePrice( product *Product, customer *Customer, quantity Quantity, ) Money { basePrice := product.Price().Multiply(float64 (quantity)) discount, err := s.discountRepo.FindByCustomer(customer.ID()) if err == nil && discount != nil { basePrice = discount.Apply(basePrice) } return basePrice }
5. 领域事件(Domain Event) 领域事件表示领域中发生的重要业务事件。
领域事件特征 graph TB
A[领域事件] --> B[不可变]
A --> C[业务含义]
A --> D[时间戳]
A --> E[事件发布]
style A fill:#E67E22
特征:
表示业务事件
不可变
包含事件发生时间
用于解耦和最终一致性
领域事件示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 type DomainEvent interface { OccurredOn() time.Time EventType() string } type OrderCreatedEvent struct { orderID OrderID customerID CustomerID totalAmount Money occurredOn time.Time } func NewOrderCreatedEvent (orderID OrderID, customerID CustomerID, totalAmount Money) *OrderCreatedEvent { return &OrderCreatedEvent{ orderID: orderID, customerID: customerID, totalAmount: totalAmount, occurredOn: time.Now(), } } func (e *OrderCreatedEvent) OrderID() OrderID { return e.orderID } func (e *OrderCreatedEvent) CustomerID() CustomerID { return e.customerID } func (e *OrderCreatedEvent) TotalAmount() Money { return e.totalAmount } func (e *OrderCreatedEvent) OccurredOn() time.Time { return e.occurredOn } func (e *OrderCreatedEvent) EventType() string { return "OrderCreated" } type Order struct { domainEvents []DomainEvent } func (o *Order) Confirm() error { if len (o.items) == 0 { return errors.New("订单不能为空" ) } o.status = OrderStatusConfirmed event := NewOrderConfirmedEvent(o.id, o.customerID) o.domainEvents = append (o.domainEvents, event) return nil } func (o *Order) DomainEvents() []DomainEvent { events := make ([]DomainEvent, len (o.domainEvents)) copy (events, o.domainEvents) return events } func (o *Order) ClearDomainEvents() { o.domainEvents = make ([]DomainEvent, 0 ) }
事件驱动架构 sequenceDiagram
participant OrderService as 订单服务
participant EventBus as 事件总线
participant PaymentService as 支付服务
participant InventoryService as 库存服务
participant NotificationService as 通知服务
OrderService->>EventBus: 发布订单已创建事件
EventBus->>PaymentService: 通知创建支付单
EventBus->>InventoryService: 通知扣减库存
EventBus->>NotificationService: 通知用户
6. 仓储(Repository) 仓储封装了获取对象引用所需的逻辑,提供领域对象的持久化抽象。
仓储模式 graph TB
A[应用层] --> B[仓储接口 Domain Layer]
B --> C[仓储实现 Infrastructure Layer]
C --> D[数据库]
style B fill:#51CF66
style C fill:#FF6B6B
职责:
提供领域对象的持久化抽象
封装数据访问逻辑
接口定义在领域层,实现在基础设施层
仓储示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 type OrderRepository interface { FindByID(id OrderID) (*Order, error ) FindByCustomerID(customerID CustomerID) ([]*Order, error ) Save(order *Order) error Remove(order *Order) error } type JpaOrderRepository struct { db *sql.DB eventPub EventPublisher } func NewJpaOrderRepository (db *sql.DB, eventPub EventPublisher) *JpaOrderRepository { return &JpaOrderRepository{ db: db, eventPub: eventPub, } } func (r *JpaOrderRepository) FindByID(id OrderID) (*Order, error ) { var entity OrderEntity err := r.db.QueryRow( "SELECT id, customer_id, status, total_amount FROM orders WHERE id = ?" , id.Value(), ).Scan(&entity.ID, &entity.CustomerID, &entity.Status, &entity.TotalAmount) if err == sql.ErrNoRows { return nil , nil } if err != nil { return nil , err } return r.toDomain(&entity) } func (r *JpaOrderRepository) Save(order *Order) error { entity := r.toEntity(order) _, err := r.db.Exec( "INSERT INTO orders (id, customer_id, status, total_amount) VALUES (?, ?, ?, ?) " + "ON DUPLICATE KEY UPDATE status = ?, total_amount = ?" , entity.ID, entity.CustomerID, entity.Status, entity.TotalAmount, entity.Status, entity.TotalAmount, ) if err != nil { return err } for _, event := range order.DomainEvents() { if err := r.eventPub.Publish(event); err != nil { return err } } order.ClearDomainEvents() return nil } func (r *JpaOrderRepository) toDomain(entity *OrderEntity) (*Order, error ) { order := NewOrder(OrderID(entity.ID), CustomerID(entity.CustomerID)) return order, nil } func (r *JpaOrderRepository) toEntity(order *Order) *OrderEntity { return &OrderEntity{ ID: order.ID().Value(), CustomerID: order.CustomerID().Value(), Status: string (order.Status()), TotalAmount: order.TotalAmount().Amount(), } }
7. 工厂(Factory) 工厂封装了复杂对象的创建逻辑。
工厂模式 graph TB
A[复杂创建逻辑] --> B[工厂]
B --> C[领域对象]
style B fill:#3498DB
使用场景:
对象创建逻辑复杂
需要封装创建细节
保证对象创建的正确性
工厂示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 type OrderFactory struct { productRepo ProductRepository pricingService *PricingService } func NewOrderFactory ( productRepo ProductRepository, pricingService *PricingService, ) *OrderFactory { return &OrderFactory{ productRepo: productRepo, pricingService: pricingService, } } func (f *OrderFactory) Create( customerID CustomerID, itemRequests []OrderItemRequest, ) (*Order, error ) { order := NewOrder(GenerateOrderID(), customerID) for _, request := range itemRequests { product, err := f.productRepo.FindByID(request.ProductID) if err != nil { return nil , fmt.Errorf("商品不存在: %w" , err) } if product == nil { return nil , errors.New("商品不存在" ) } customer := &Customer{id: customerID} price := f.pricingService.CalculatePrice( product, customer, request.Quantity, ) item := NewOrderItemWithPrice(product, request.Quantity, price) order.AddItem(product, request.Quantity) } return order, nil }
核心概念:战略设计 1. 限界上下文(Bounded Context) 限界上下文是领域模型的边界,在这个边界内,所有术语和概念都有明确的含义。
限界上下文概念 graph TB
A[电商系统] --> B[用户上下文]
A --> C[订单上下文]
A --> D[商品上下文]
A --> E[支付上下文]
B --> B1[用户模型]
C --> C1[订单模型]
D --> D1[商品模型]
E --> E1[支付模型]
style B fill:#51CF66
style C fill:#4DABF7
style D fill:#FFE66D
style E fill:#FF6B6B
特征:
明确的业务边界
统一的数据模型
独立的业务语言
可以映射为一个微服务
上下文映射(Context Mapping) graph LR
A[订单上下文] -->|上游| B[商品上下文]
A -->|上游| C[用户上下文]
D[支付上下文] -->|下游| A
E[物流上下文] -->|下游| A
style A fill:#FF6B6B
style B fill:#51CF66
style C fill:#51CF66
style D fill:#4DABF7
style E fill:#4DABF7
关系类型:
共享内核(Shared Kernel) :共享部分模型
客户-供应商(Customer-Supplier) :上下游关系
遵奉者(Conformist) :下游遵从上游
防腐层(Anti-Corruption Layer) :隔离外部系统
独立方式(Separate Ways) :完全独立
开放主机服务(Open Host Service) :提供统一协议
限界上下文示例 订单上下文:
订单实体
订单项实体
订单状态值对象
订单领域服务
商品上下文:
2. 统一语言(Ubiquitous Language) 统一语言是业务专家和开发人员共同使用的语言,代码中的类名、方法名都使用统一语言。
统一语言的作用 graph LR
A[业务专家] --> B[统一语言]
B --> C[领域模型]
C --> D[代码实现]
D --> B
style B fill:#51CF66
原则:
统一语言示例 业务术语:
订单(Order)
订单项(OrderItem)
订单状态(OrderStatus)
确认订单(Confirm Order)
取消订单(Cancel Order)
代码体现:
1 2 3 4 5 6 7 8 9 10 11 12 13 type Order struct { } func (o *Order) Confirm() error { return nil } func (o *Order) Cancel() error { return nil }
3. 子域(Subdomain) 子域是领域的一部分,根据重要性分为核心域、支撑域和通用域。
子域分类 graph TB
A[领域 Domain] --> B[核心域 Core Domain]
A --> C[支撑域 Supporting Subdomain]
A --> D[通用域 Generic Subdomain]
style B fill:#FF6B6B
style C fill:#FFE66D
style D fill:#4DABF7
核心域(Core Domain):
支撑域(Supporting Subdomain):
通用域(Generic Subdomain):
电商系统子域示例
子域类型
示例
说明
核心域
订单处理、推荐算法
核心竞争力
支撑域
用户管理、商品管理
支持功能
通用域
支付、物流、通知
通用功能
DDD实践指南 1. 领域建模流程 graph TB
A[业务需求] --> B[识别子域]
B --> C[识别限界上下文]
C --> D[建立统一语言]
D --> E[识别聚合]
E --> F[设计领域模型]
F --> G[实现代码]
style A fill:#4DABF7
style G fill:#51CF66
步骤详解
业务需求分析
识别子域
核心域:核心竞争力
支撑域:支持功能
通用域:通用功能
识别限界上下文
建立统一语言
识别聚合
设计领域模型
实现代码
2. 聚合设计实践 聚合识别 graph TB
A[业务场景] --> B{需要一起修改?}
B -->|是| C{需要强一致性?}
B -->|否| D[分开的聚合]
C -->|是| E[同一聚合]
C -->|否| F[不同聚合]
style E fill:#51CF66
style F fill:#4DABF7
判断标准:
是否需要一起修改?
是否需要强一致性?
是否属于同一业务概念?
聚合大小 graph LR
A[小聚合] --> B[优点: 性能好 缺点: 可能跨聚合]
C[大聚合] --> D[优点: 一致性好 缺点: 性能差]
E[适中聚合] --> F[平衡性能和一致性]
style E fill:#51CF66
原则:
保持聚合小
只包含必须一起修改的对象
通过ID引用其他聚合
3. 领域事件实践 事件设计 graph TB
A[业务操作] --> B[状态变更]
B --> C[发布领域事件]
C --> D[事件处理]
D --> E[最终一致性]
style C fill:#E67E22
事件命名:
使用过去时:OrderCreated, PaymentCompleted
表达业务含义
包含必要信息
事件处理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 func (o *Order) Confirm() error { if len (o.items) == 0 { return errors.New("订单不能为空" ) } o.status = OrderStatusConfirmed event := NewOrderConfirmedEvent(o.id, o.customerID, o.totalAmount) o.domainEvents = append (o.domainEvents, event) return nil } type OrderConfirmedHandler struct { paymentService PaymentService inventoryService InventoryService notificationService NotificationService } func NewOrderConfirmedHandler ( paymentService PaymentService, inventoryService InventoryService, notificationService NotificationService, ) *OrderConfirmedHandler { return &OrderConfirmedHandler{ paymentService: paymentService, inventoryService: inventoryService, notificationService: notificationService, } } func (h *OrderConfirmedHandler) Handle(event *OrderConfirmedEvent) error { if err := h.paymentService.CreatePayment( event.OrderID(), event.TotalAmount(), ); err != nil { return err } if err := h.inventoryService.Reserve( event.OrderID(), event.Items(), ); err != nil { return err } if err := h.notificationService.NotifyCustomer( event.CustomerID(), "订单已确认" , ); err != nil { return err } return nil }
4. 仓储实践 仓储设计 graph TB
A[领域层] --> B[仓储接口]
B --> C[查询方法]
B --> D[保存方法]
E[基础设施层] --> F[仓储实现]
F --> G[JPA/MyBatis]
F --> H[NoSQL]
style B fill:#51CF66
style F fill:#FF6B6B
原则:
接口在领域层
实现在基础设施层
只保存聚合根
封装查询逻辑
查询优化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 type OrderSpecification interface { IsSatisfiedBy(order *Order) bool } type OrderByCustomerSpec struct { customerID CustomerID } func NewOrderByCustomerSpec (customerID CustomerID) *OrderByCustomerSpec { return &OrderByCustomerSpec{ customerID: customerID, } } func (s *OrderByCustomerSpec) IsSatisfiedBy(order *Order) bool { return order.CustomerID().Equals(s.customerID) } type OrderRepository interface { FindBySpecification(spec OrderSpecification) ([]*Order, error ) } func (r *JpaOrderRepository) FindBySpecification( spec OrderSpecification, ) ([]*Order, error ) { allOrders, err := r.findAll() if err != nil { return nil , err } result := make ([]*Order, 0 ) for _, order := range allOrders { if spec.IsSatisfiedBy(order) { result = append (result, order) } } return result, nil }
DDD与微服务 DDD在微服务中的应用 graph TB
A[限界上下文] --> B[微服务边界]
C[聚合] --> D[服务内一致性]
E[领域事件] --> F[服务间通信]
G[统一语言] --> H[API设计]
style A fill:#51CF66
style C fill:#4DABF7
style E fill:#E67E22
style G fill:#FFE66D
映射关系:
限界上下文 → 微服务 :一个限界上下文对应一个微服务
聚合 → 服务内一致性 :聚合保证服务内强一致性
领域事件 → 服务间通信 :通过事件实现服务间解耦
统一语言 → API设计 :API使用统一语言
服务拆分策略 基于限界上下文拆分 graph TB
A[电商领域] --> B[用户上下文 → 用户服务]
A --> C[订单上下文 → 订单服务]
A --> D[商品上下文 → 商品服务]
A --> E[支付上下文 → 支付服务]
style B fill:#51CF66
style C fill:#4DABF7
style D fill:#FFE66D
style E fill:#FF6B6B
优势:
清晰的业务边界
独立的数据模型
团队自治
易于维护
跨服务一致性 最终一致性 sequenceDiagram
participant OrderService as 订单服务
participant EventBus as 事件总线
participant PaymentService as 支付服务
participant InventoryService as 库存服务
OrderService->>OrderService: 创建订单(强一致性)
OrderService->>EventBus: 发布订单已创建事件
EventBus->>PaymentService: 创建支付单(最终一致性)
EventBus->>InventoryService: 扣减库存(最终一致性)
策略:
服务内:强一致性(聚合保证)
服务间:最终一致性(事件驱动)
DDD常见问题 1. 如何识别聚合? 判断标准:
是否需要一起修改?
是否需要强一致性?
是否属于同一业务概念?
示例:
✅ 订单和订单项 → 同一聚合
❌ 订单和商品 → 不同聚合
❌ 订单和支付 → 不同聚合
2. 实体和值对象如何选择? 实体:
值对象:
示例:
订单 → 实体(有ID,需要跟踪)
金额 → 值对象(无ID,不可变)
地址 → 值对象(无ID,可替换)
3. 何时使用领域服务? 使用场景:
业务逻辑涉及多个实体
业务逻辑不属于任何实体
需要访问基础设施
示例:
转账服务(涉及两个账户)
价格计算服务(需要访问折扣规则)
邮件发送服务(需要访问基础设施)
4. 如何设计限界上下文? 步骤:
识别业务领域
分析业务边界
确定数据模型边界
识别团队边界
定义统一语言
DDD工具和框架 Go生态
GORM :ORM框架,支持持久化
go-eventstore :事件存储实现
watermill :事件驱动和消息传递库
go-cqrs :CQRS模式实现
ent :Facebook开源的实体框架
Java生态
Spring Data JPA :持久化支持
Axon Framework :CQRS和事件溯源
jMolecules :DDD注解支持
.NET生态
ABP Framework :DDD应用框架
MediatR :领域事件处理
其他
EventStore :事件存储
Apache Kafka :事件总线
RabbitMQ :消息队列
总结 领域驱动设计是一种强大的软件设计方法论,它帮助我们:
理解业务 :通过领域模型深入理解业务
统一语言 :业务和代码使用相同语言
清晰边界 :限界上下文明确业务边界
合理拆分 :基于领域模型进行服务拆分
易于维护 :领域逻辑集中,易于理解和修改
关键要点:
以领域为中心,而非技术
使用统一语言连接业务和代码
通过限界上下文划分业务边界
使用聚合保证一致性
通过领域事件实现解耦
将DDD与微服务结合使用
DDD不是银弹,但在复杂业务场景下,它能够帮助我们构建更好的软件系统。记住:DDD的核心是理解业务,而不是技术实现 。