我来详细解释 Go 语言在 DDD 中关于依赖注入和并发处理的最佳实践。
// 1. 依赖注入实现
package main
import (
"context"
"sync"
)
// 依赖注入容器
type Container struct {
services map[string]interface{}
mutex sync.RWMutex
}
func NewContainer() *Container {
return &Container{
services: make(map[string]interface{}),
}
}
// 注册服务
func (c *Container) Register(name string, service interface{}) {
c.mutex.Lock()
defer c.mutex.Unlock()
c.services[name] = service
}
// 获取服务
func (c *Container) Get(name string) interface{} {
c.mutex.RLock()
defer c.mutex.RUnlock()
return c.services[name]
}
// 服务接口定义
type OrderRepository interface {
Save(ctx context.Context, order *Order) error
FindByID(ctx context.Context, id string) (*Order, error)
}
type OrderService interface {
CreateOrder(ctx context.Context, order *Order) error
ProcessOrder(ctx context.Context, orderID string) error
}
// 服务实现
type orderService struct {
repo OrderRepository
eventBus EventBus
}
// 使用构造函数注入依赖
func NewOrderService(repo OrderRepository, eventBus EventBus) OrderService {
return &orderService{
repo: repo,
eventBus: eventBus,
}
}
// 2. 并发处理实现
// 工作池模式处理订单
type OrderProcessor struct {
workers int
jobs chan *Order
results chan error
orderService OrderService
}
func NewOrderProcessor(workers int, orderService OrderService) *OrderProcessor {
return &OrderProcessor{
workers: workers,
jobs: make(chan *Order, workers),
results: make(chan error, workers),
orderService: orderService,
}
}
func (p *OrderProcessor) Start(ctx context.Context) {
wg := &sync.WaitGroup{}
// 启动工作池
for i := 0; i < p.workers; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for order := range p.jobs {
select {
case <-ctx.Done():
return
default:
err := p.processOrder(ctx, order)
p.results <- err
}
}
}()
}
// 等待所有工作完成
go func() {
wg.Wait()
close(p.results)
}()
}
func (p *OrderProcessor) processOrder(ctx context.Context, order *Order) error {
return p.orderService.ProcessOrder(ctx, order.ID)
}
// 3. 领域事件的并发处理
type EventHandler func(ctx context.Context, event Event) error
type EventBus struct {
handlers map[string][]EventHandler
mutex sync.RWMutex
}
func NewEventBus() *EventBus {
return &EventBus{
handlers: make(map[string][]EventHandler),
}
}
func (b *EventBus) Subscribe(eventType string, handler EventHandler) {
b.mutex.Lock()
defer b.mutex.Unlock()
b.handlers[eventType] = append(b.handlers[eventType], handler)
}
func (b *EventBus) Publish(ctx context.Context, event Event) {
b.mutex.RLock()
handlers := b.handlers[event.Type()]
b.mutex.RUnlock()
// 并发处理所有事件处理器
wg := &sync.WaitGroup{}
for _, handler := range handlers {
wg.Add(1)
go func(h EventHandler) {
defer wg.Done()
if err := h(ctx, event); err != nil {
// 处理错误
}
}(handler)
}
wg.Wait()
}
// 4. 应用示例
func main() {
// 创建依赖注入容器
container := NewContainer()
// 注册服务
container.Register("orderRepo", NewOrderRepository())
container.Register("eventBus", NewEventBus())
// 创建订单服务
orderService := NewOrderService(
container.Get("orderRepo").(OrderRepository),
container.Get("eventBus").(*EventBus),
)
// 创建订单处理器
processor := NewOrderProcessor(5, orderService)
// 启动处理
ctx := context.Background()
processor.Start(ctx)
}
让我解释一下关键点:
1、依赖注入的最佳实践:
- 使用接口而不是具体类型
type Service interface {
DoSomething() error
}
type Handler struct {
service Service // 使用接口
}
- 通过构造函数注入依赖
func NewHandler(service Service) *Handler {
return &Handler{service: service}
}
- 使用依赖注入容器管理服务生命周期
- 支持可测试性,便于mock
- 避免全局状态和单例
2、并发处理的考虑:
- 使用 context 控制超时和取消
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel()
- 合理使用goroutine池避免资源耗尽
pool := make(chan struct{}, maxGoroutines)
- 使用互斥锁保护共享资源
var mu sync.RWMutex mu.Lock() defer mu.Unlock()
- 正确处理channel的关闭
defer close(resultChan)
关键建议:
1、依赖注入:
- 始终面向接口编程
- 使用构造函数注入依赖
- 避免在构造函数中进行复杂操作
- 考虑使用依赖注入框架(如 wire)
2、并发处理:
- 始终使用 context 进行超时控制
- 正确处理 goroutine 的生命周期
- 使用适当的并发原语(Mutex, RWMutex, WaitGroup等)
- 考虑使用工作池模式处理并发任务
- 正确处理错误传播