NestJS 中的 Mikro-orm 服务间事务

Mikro-orm inter-service transactions in NestJS

我正在为未来的项目评估 Mikro-Orm。有几个问题我要么在文档中找不到答案,要么没有完全理解它们。

让我描述一个最简单的复杂示例 (NestJS):我有一个包含两个实体的订单处理系统:OrdersInvoices 以及用于顺序发票的计数器 table数字(法律要求)。值得一提的是,OrderService create 方法并不总是由控制器调用,也可以通过 crobjob/queue 系统调用。我的问题是关于创建新订单的用例:

class OrderService {
    async createNewOrder(orderDto) {
        const order = new Order();
        order.customer = orderDto.customer;
        order.items = orderDto.items;

        const invoice = await this.InvoiceService.createInvoice(orderDto.items);
        order.invoice = invoice;

        await order.persistAndFlush();

        return order
    }
}

class InvoiceService {
    async create(items): Invoice {
        const invoice = new Invoice();

        invoice.number = await this.InvoiceNumberService.getNextInSequence();

        // the next two lines are external apis, if they throw, the whole transaction should roll back
        const pdf = await this.PdfCreator.createPdf(invoice);
        const upload = await s3Api.uplpad(pdf);

        return invoice;
    }
}

class InvoiceNumberService {
  async getNextInSequence(): number {
      return await db.collection("counter").findOneAndUpdate({ type: "INVOICE" }, { $inc: { value: 1 } });
  }
}

创建新订单以及所有后续服务调用的整个用例应该发生在一个 Mikro-Orm 事务中。因此,如果在 OrderService.createNewOrder() 或随后调用的方法之一中抛出任何内容,则应回滚整个事务。

  1. Mikro-Orm 不允许 InvoiceNumberService 中显示的原子更新增量。我可以回退到本机 mongo 驱动程序。但是我如何确保对 collection.findOneAndUpdate() 的调用与 Mikro-Orm 管理的实体共享相同的事务?

  2. Mikro-Orm 需要一个唯一的请求上下文。在 NestJS 的示例中,这个独特的上下文是在控制器级别创建的。在上面的示例中,服务方法不一定由控制器调用。因此,每次调用 OrderService.createNewOrder() 都需要一个新的上下文,该上下文的生命周期限定为函数调用,对吗?我怎样才能做到这一点?

  3. 如何在服务之间共享相同的请求上下文?在上面的示例中,InvoiceService 和 InvoiceNumberService 需要与 OrderService 相同的上下文才能使 Mikro-Orm 正常工作。

我将从坏消息开始,mongodb t运行MikroORM 尚不支持操作(尽管它们可能会在数周内登陆,已经实现了 PoC)。您可以在这里订阅更新:https://github.com/mikro-orm/mikro-orm/issues/34

但让我回答其余部分,因为它将适用:

您可以使用 const collection = (em as EntityManager<MongoDriver>).getConnection().getCollection('counter'); 从内部 mongo 连接实例获取集合。您还可以使用 orm.em.getTransactionContext() 来获取当前的交易上下文(目前仅在 sql 驱动程序中实现,但将来这可能会 return [=33= 中的 session 对象]).

另请注意,在 mongo 驱动程序中,默认情况下不会启用隐式 t运行 操作(尽管它是可配置的),因此您需要使用显式 t运行 通过 em.transactional(...).

的动作划分

RequestContext 助手自动工作。您只需将其注册为中间件(在 nestjs orm 适配器中自动完成),然后您的请求处理程序(route/endpoint/controller 方法)在共享上下文的域内 运行。由于这一点,DI 中的所有服务都可以共享存储库的单例实例,但它们会自动从域中选择正确的上下文。

你基本上有了这个自动请求上下文,然后你可以通过 em.transactional(...).

手动创建新的(嵌套的)上下文

https://mikro-orm.io/docs/transactions/#approach-2-explicitly