遵循整洁架构/DDD原则,如何在服务层正确管理事务?
在Clean Architecture+DDD下服务层管控事务的Go实现方案
针对你提出的问题,核心解决思路是用上下文传递事务+抽象事务接口,既保持仓储接口的纯净性(不依赖底层数据库),又能在服务层完全管控事务生命周期。以下是具体实现:
1. 领域层:抽象事务与仓储接口
先在领域层定义和数据库无关的事务抽象,确保仓储接口不引入任何数据库依赖:
// 领域层:事务抽象接口,仅暴露必要操作 type Transaction interface { Commit() error Rollback() error } // 领域层:Item仓储接口,仅依赖上下文和领域对象 type ItemRepository interface { Add(ctx context.Context, item *domain.Item) (uint, error) }
2. 基础设施层:适配数据库事务
在基础设施层实现领域层的事务接口(以GORM为例),并让仓储实现从上下文读取事务:
// 基础设施层:GORM事务实现,适配领域层Transaction接口 type GormTransaction struct { tx *gorm.DB } func (gt *GormTransaction) Commit() error { return gt.tx.Commit().Error } func (gt *GormTransaction) Rollback() error { return gt.tx.Rollback().Error } // 基础设施层:Item仓储的GORM实现 type GormItemRepository struct { db *gorm.DB } func (r *GormItemRepository) Add(ctx context.Context, item *domain.Item) (uint, error) { // 从上下文获取事务实例,不存在则用普通连接 tx, hasTx := ctx.Value("tx").(*gorm.DB) if !hasTx { tx = r.db } if err := tx.Create(item).Error; err != nil { return 0, err } return item.ID, nil }
3. 应用服务层:管控事务生命周期
服务层负责开启事务、传递上下文、执行业务逻辑,在需要时触发回滚:
// 应用服务层:业务逻辑与事务管控 type ItemService struct { itemRepo ItemRepository emailSvc EmailService // 假设的邮件服务接口 dbConn *gorm.DB // 依赖注入的数据库连接 } func (s *ItemService) CreateItemWithNotification(ctx context.Context, item *domain.Item) error { // 开启事务 tx := s.dbConn.Begin() if tx.Error != nil { return tx.Error } // 将事务存入上下文 txCtx := context.WithValue(ctx, "tx", tx) // 执行仓储操作 _, err := s.itemRepo.Add(txCtx, item) if err != nil { tx.Rollback() return err } // 执行邮件发送 err = s.emailSvc.SendItemCreatedAlert(item) if err != nil { // 邮件失败,回滚事务 tx.Rollback() return fmt.Errorf("send notification failed: %w", err) } // 提交事务 return tx.Commit().Error }
关键优势说明
- 符合Clean Architecture+DDD原则:领域层仓储接口完全不依赖数据库细节,依赖方向始终是内层(领域)指向外层(基础设施)。
- 服务层完全掌控事务:可以在任意业务节点(比如邮件发送失败)触发回滚,满足业务需求。
- 无侵入式事务传递:通过Go原生的
context.Context传递事务,不需要修改仓储接口签名。
优化建议
为避免上下文key硬编码,可以在基础设施层定义包级常量:
const contextKeyTx = "transaction"
后续使用context.WithValue(ctx, contextKeyTx, tx)和ctx.Value(contextKeyTx)来操作。
内容的提问来源于stack exchange,提问作者Christian Gama
相关产品推荐
相关产品推荐

