NestJS中的Mikro orm服务间事务

NestJS中的Mikro orm服务间事务,nestjs,mikro-orm,Nestjs,Mikro Orm,我正在为未来的项目评估Mikro Orm。有几个问题我要么在文档中找不到答案,要么没有完全理解 让我描述一个最简单的复杂示例(NestJS):我有一个订单处理系统,它有两个实体:订单和发票,以及一个用于顺序发票编号的计数器表(法律要求)。值得一提的是,OrderService create方法并不总是由控制器调用,而是通过crobjob/queue系统调用。我的问题是关于创建新订单的用例: class OrderService { async createNewOrder(orderDt

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

让我描述一个最简单的复杂示例(NestJS):我有一个订单处理系统,它有两个实体:
订单
发票
,以及一个用于顺序发票编号的计数器表(法律要求)。值得一提的是,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()或随后调用的某个方法中抛出任何内容,则应回滚整个事务

  • Mikro Orm不允许InvoiceNumberService中显示的原子更新增量。我可以回到本地的mongo司机那里。但是如何确保对collection.findOneAndUpdate()的调用与Mikro Orm管理的实体共享相同的事务

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

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


  • 我将从坏消息开始,mongodb事务在MikroORM中还不受支持(尽管它们可能在几周内到达,已经实现了PoC)。您可以在此处订阅更新:

    但让我回答其余的问题,因为它将适用于:

    您可以使用
    const collection=(em作为EntityManager).getConnection().getCollection('counter')
    从内部mongo连接实例获取集合。您还可以使用
    orm.em.getTransactionContext()
    获取当前的事务上下文(当前仅在sql驱动程序中实现,但将来可能会在mongo中返回
    会话
    对象)

    还要注意的是,在mongo驱动程序中,默认情况下不会启用隐式事务(尽管它是可配置的),因此您需要通过
    em.transactional(…)
    使用显式事务划分

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

    您基本上拥有这个自动请求上下文,然后您可以通过
    em.transactional(…)
    手动创建新的(嵌套的)上下文


    我将从坏消息开始,mongodb事务在MikroORM中还不受支持(尽管它们可能会在几周内到达,已经实现了PoC)。您可以在此处订阅更新:

    但让我回答其余的问题,因为它将适用于:

    您可以使用
    const collection=(em作为EntityManager).getConnection().getCollection('counter')
    从内部mongo连接实例获取集合。您还可以使用
    orm.em.getTransactionContext()
    获取当前的事务上下文(当前仅在sql驱动程序中实现,但将来可能会在mongo中返回
    会话
    对象)

    还要注意的是,在mongo驱动程序中,默认情况下不会启用隐式事务(尽管它是可配置的),因此您需要通过
    em.transactional(…)
    使用显式事务划分

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

    您基本上拥有这个自动请求上下文,然后您可以通过
    em.transactional(…)
    手动创建新的(嵌套的)上下文


    哇,谢谢。关于请求上下文:我的服务方法不是由控制器调用的,而是通过cronjob。。。因此没有调用控制器,因此没有中间件。所以我假设,每当通过CronJob调用OrdersService.createNewOrder()时,我的服务将始终使用相同的IdentityMap…因此IdentityMap将无限增长。不是真的,如果您cron始终调用MikroORM.init(),那么您总是有新的上下文。您还可以使用em.fork()手动创建一个。哇,谢谢。关于请求上下文:我的服务方法不是由控制器调用的,而是通过cronjob。。。因此没有调用控制器,因此没有中间件。所以我假设,每当通过CronJob调用OrdersService.createNewOrder()时,我的服务将始终使用相同的IdentityMap…因此IdentityMap将无限增长。不是真的,如果您cron始终调用MikroORM.init(),那么您总是有新的上下文。您还可以使用em.fork()手动创建一个。