【问题标题】:Benefit of applying Unit-of-Work on Service-layer instead of Controller在服务层而不是控制器上应用工作单元的好处
【发布时间】:2017-08-08 03:56:14
【问题描述】:

在实现 MVC 项目时,我通常会添加服务层来执行实际工作。但实际上有时 1 个 Web 请求应该使用几个 AppService 方法来完成。那么工作单元(UoW)的位置可能会影响编码处理。

无论在 C# EF/Java Spring 中,Service Layer 方法中都有 Transaction 注解,所以事务是基于 Per-Service 的(即 Service 层上的 UoW)。这里以Java版本为例:

@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED)
Public class UserAppService{
  public UserDTO createUser() {
      // Do sth to create a new user
      userRepository.save(userBean);

      // Convert userBean to userDTO
      return userDTO;
  }
  public xxx DoSth() {
      // Break the operation here
      throw new Exception("Whatever");

      // (never execute actually)
      sthRepository.save(someBean);
  }
}

然后在控制器中:

Public class SomeController : Controller {
  Public xxx DoSth(){
    UserAppService Service = new UserAppService();
    Service.CreateUser(); // DB committed
    Service.DoSth();      //Exception thrown

  }
}

使用这种结构,如果在第二个服务方法调用中抛出任何异常,第一个服务方法仍然会将用户提交到数据库。如果我想要“全有或全无”处理,除非我将这些服务方法调用包装到具有单个事务的另一个包装服务调用中,否则此结构将不起作用。但这是一项额外的工作。

另一个版本是在控制器操作级别上使用事务(即控制器操作上的 UoW)。我们以 C# 代码为例:

备注:此处代码版本 2 中的 AppService 使用控制器中定义的 DbContext (sth like transaction),内部不做任何提交。

Public class SomeController : Controller {
  Public ActionResult DoSth(){
     using (var DB = new DbContext()){
       Var UserAppService = new UserAppService(DB);
       var userEntity = userAppService.GetUser(userId);
       UserAppService.DoSth(userEntity);

       Var AnotherAppService = new AnotherAppService(DB);
       AnotherAppService.DoSthElse(userEntity);

       // Throw exception here
       throw new Exception("Whatever");

       DB.Save(); // commit
     }
  }
}

在此示例中,不会对数据库进行部分提交。

在服务层应用 UoW 真的更好吗?

【问题讨论】:

    标签: entity-framework model-view-controller transactions unit-of-work


    【解决方案1】:

    在服务层应用 UoW 真的更好吗?

    IMO 没有。而且您已经知道原因了。如果服务方法是谨慎且可重用的,它们也不适合作为原子事务。

    在 .NET 中,控制器应控制事务生命周期,并在事务中登记服务方法。

    请注意,这意味着服务方法应该是本地方法调用,而不是远程或网络服务调用。

    【讨论】:

      【解决方案2】:

      这更好,因为您遵循面向对象编程的主要原则分离关注点。如果您制作了另一个控制器并想使用同一个对象进行更多的数据库处理怎么办?你不想实例化你做完全不同的事情的控制器。顺便看看外观服务模式http://soapatterns.org/design_patterns/service_facade它可能会帮助你理解为什么它如此性感。 .嗨,上图显示了这种模式,基本上你在服务层用事务包装你的数据库访问对象,这样一个 customerService 对象可以包装 1,2...inf 事务,要么全部失败,要么成功。

      【讨论】:

      • 如果遵循 UoW on Service 模式,考虑到需要在 2 个以上的服务方法上执行 1 个 HTTP POST 调用(即 1 个控制器操作)并更改数据库的常见情况,如何“部分提交”在不破坏 UoW on Service 模式的情况下以一种漂亮的方式进行预防?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-08-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多