基于微服务框架Micronaut和Eventuate Tram实现分布式事务的开源案例


Micronaut是一个类似Spring Boot的微服务框架,Eventuate Tram是提供事务性消息的框架,提供事务发件箱模式,也就是将发送的消息首先存储到带有主键的关系数据库,然后通过消息系统发送到接收者,保证近似正好一次的传递,该机制类似Apache Kafka事务性消息,只不过使用该框架可以在RabbitMQ或Akka的消息系统中实现事务性消息。
该源码案例点击标题进入。该开源作者是《微服务模式》书籍的作者Chris Richardson。
这个应用案例展示了跨微服务实现Saga的机制,以及CQRS模式。
订单服务是实现CQRS的命令模型,而Order History Service实现CQRS视图,其订阅了来自订单服务发出的领域事件。
这个案例的特殊之处在于,它不是基于DDD聚合设计的领域事件,而是直接在微服务中发出领域事件,Order订单沦为JPA的实体贫血对象了。以下是订单服务的代码:


@Singleton
public class OrderService {

  @Inject
  private DomainEventPublisher domainEventPublisher;

  @PersistenceContext
  private EntityManager entityManager;

  @Transactional
  public Order createOrder(OrderDetails orderDetails) {
    ResultWithEvents<Order> orderWithEvents = Order.createOrder(orderDetails);
    Order order = orderWithEvents.result;
    entityManager.persist(order);
    domainEventPublisher.publish(Order.class, order.getId(), orderWithEvents.events);
    return order;
  }

  public void approveOrder(Long orderId) {
    Order order = Optional.ofNullable(entityManager.find(Order.class, orderId))
            .orElseThrow(() -> new IllegalArgumentException(String.format("order with id %s not found", orderId)));
    order.noteCreditReserved();
    OrderDetails orderDetails = new OrderDetails(order.getOrderDetails().getCustomerId(), new Money(order.getOrderDetails().getOrderTotal().getAmount()));
    domainEventPublisher.publish(Order.class,
            orderId, singletonList(new OrderApprovedEvent(orderDetails)));
  }

  public void rejectOrder(Long orderId) {
    Order order = Optional
            .ofNullable(entityManager.find(Order.class, orderId))
            .orElseThrow(() -> new IllegalArgumentException(String.format(
"order with id %s not found", orderId)));
    order.noteCreditReservationFailed();
    OrderDetails orderDetails = new OrderDetails(order.getOrderDetails().getCustomerId(), new Money(order.getOrderDetails().getOrderTotal().getAmount()));
    domainEventPublisher.publish(Order.class,
            orderId, singletonList(new OrderRejectedEvent(orderDetails)));
  }
}

批准订单和拒绝订单这些应该属于订单聚合重要的业务动作被放入了微服务内,微服务成为业务决策者,而不是协调者,正如饭店服务员成为饭店的决策者了。

亮点是通过Saga组成的微服务分布式事务,每个服务内部通过JPA实现ACID,服务之间的消息通过事务性消息机制实现正好一次传递,通过Saga实现回滚回退补偿,以下是其流程,关系数据库在其中实现传递消息的幂等性。