具有远程服务调用的事务服务层

Transactional Service Layer with remote service invocations

答案可以笼统地涵盖所有框架,但我对 Spring MVC 案例特别感兴趣。我正在重构一个访问内部数据库和远程 service/s 的服务层。这些方法应该是事务性的,并且它们需要来自远程服务的数据。这是类似的虚拟代码:

@Service
public class MyService {
    @Autowired
    private SomeRepository repository1;

    @Autowired
    private OtherRepository repository2;

    @Autowired
    private RemoteGateway remoteGateway;

    @Transactional
    public void updateState(Long id) {
        Item item1 = repository1.get(id)
        item1.setSomeVal(remoteGateway.getValue());
        repository1.save();

        repository2.doSomethingElse(item1.getOtherVal());
    }
}

这样实现起来比较容易。但是也有很多缺点,比如当远程服务调用失败时不必要地创建和回滚事务,因为远程服务调用而导致的事务更长并且可能更多。我正在考虑将服务调用移动到单独的非事务性方法并调用事务性方法,就像在以下代码片段中所做的那样

@Service
public class MyService {
    @Autowired
    private SomeRepository repository1;

    @Autowired
    private OtherRepository repository2;

    @Autowired
    private RemoteGateway remoteGateway;

    public SomeType updateState(Long id) {
        SomeType valueFromRemote = remoteGateway.getValue();
        updateState(id, valueFromRemote);
    }

    @Transactional
    public void updateState(Long id, SomeType valueFromRemote) {
        Item item1 = repository1.get(id)
        item1.setSomeVal(valueFromRemote);
        repository1.save();

        repository2.doSomethingElse(item1.getOtherVal());
    }
}

假设 remoteGateway 有缓存、适当的超时、断路器,这样就不会无限期地挂起,并且在缓存间隔中已经请求值时会更快,重构有意义吗?或者什么是更好的设计决策?

如果事务的长度是一个问题,并且如果您知道在调用事务方法之前需要哪些值,我认为这是一个很好的重构。如果需要从远程服务获取很多值,可以考虑异步调用服务。

只有一个问题 - 如果您使用默认的 "proxy" 事务支持方式,那么如果您调用同一 bean 的方法则它不起作用。在您的情况下,从 updateState(Long id) 调用 updateState(id, valueFromRemote) 不会在事务中 运行,因为不会执行任何代理代码(您可以在调试事务方法时在调用堆栈中看到代理支持).要绕过它,您可以

  • 将事务方法移动到另一个 bean
  • 或者使用aspectj模式的transactionManager。见 Spring doc.