DDD 设计模式建议中的事务处理

wcl*_*wcl 4 database domain-driven-design transactions go go-gorm

我们知道服务(控制器/用例)层是处理业务逻辑,repo 是处理数据库查询

现在我有:

func (s *OrderService) Create(order models.Order) (models.Order, error) {
  ...
  user := models.User{
    Contact: order.Address.Contact,
  }
  createdUser, err := s.UserRepo.Save(user)   
  // err handling...

  order.User = user
  createdOrder, err := s.OrderRepo.save(order)
  // err handling...

  return order, nil
}
Run Code Online (Sandbox Code Playgroud)
// user_repo.go
func (repo *UserRepo) Save(user models.User) (models.User, error) {
  err := repo.DB.Debug().Save(&user).Error
  // err handing...
  return user, nil
}

// order_repo.go
func (repo *OrderRepo) Save(order models.Order) (models.Order, error) {
  err := repo.DB.Debug().Save(&order).Error
  // err handing...
  return order, nil
}
Run Code Online (Sandbox Code Playgroud)

我希望应用 gormdb.Begin()事务变得更加灵活,而不是我当前的代码过于静态。那么我应该删除 gorm.DB 存储库但是

我。通过params传入gorm.DB?

tx := s.DB.Begin()
createdUser, err := s.UserRepo.Save(user, tx)
Run Code Online (Sandbox Code Playgroud)

二. 或者直接在服务层运行查询?(但是它打破了ddd的设计理念)

tx := s.DB.Begin()
createdUser, err := tx.Create(&user)
if err != nil {
  tx.Rollback()
}
createdOrder, err := tx.Create(&order)  
if err != nil {
  tx.Rollback()
}
tx.Commit()
Run Code Online (Sandbox Code Playgroud)

小智 6

根据 DDD,事务不应跨越聚合边界。

参考:

如果我们出于某种原因需要在事务中更新它们,您可能需要重新考虑它们是否应该成为某些聚合的一部分

在编写聚合存储库时,可以巧妙地将事务隐藏在存储库层中

我一般都是按照下面的界面

// holds the business logic to modify the aggregate, provided by business layer
type AggregateUpdateFunction func (a *Aggregate) error
    
type Repository interface {
      Create(ctx context.Context, aggregate *Aggregate)
      Read(ctx context.Context, id string) *Aggregate
      // starts a read-modify-write cycle internally in a transaction   
      Update(ctx context.Context, id string, updateFunc AggregateUpdateFunction) error
}
Run Code Online (Sandbox Code Playgroud)