NestJS 中的 Mikro-orm 服务间事务
Mikro-orm inter-service transactions in NestJS
我正在为未来的项目评估 Mikro-Orm。有几个问题我要么在文档中找不到答案,要么没有完全理解它们。
让我描述一个最简单的复杂示例 (NestJS):我有一个包含两个实体的订单处理系统:Orders
和 Invoices
以及用于顺序发票的计数器 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() 或随后调用的方法之一中抛出任何内容,则应回滚整个事务。
Mikro-Orm 不允许 InvoiceNumberService 中显示的原子更新增量。我可以回退到本机 mongo 驱动程序。但是我如何确保对 collection.findOneAndUpdate() 的调用与 Mikro-Orm 管理的实体共享相同的事务?
Mikro-Orm 需要一个唯一的请求上下文。在 NestJS 的示例中,这个独特的上下文是在控制器级别创建的。在上面的示例中,服务方法不一定由控制器调用。因此,每次调用 OrderService.createNewOrder() 都需要一个新的上下文,该上下文的生命周期限定为函数调用,对吗?我怎样才能做到这一点?
如何在服务之间共享相同的请求上下文?在上面的示例中,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
我正在为未来的项目评估 Mikro-Orm。有几个问题我要么在文档中找不到答案,要么没有完全理解它们。
让我描述一个最简单的复杂示例 (NestJS):我有一个包含两个实体的订单处理系统:Orders
和 Invoices
以及用于顺序发票的计数器 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() 或随后调用的方法之一中抛出任何内容,则应回滚整个事务。
Mikro-Orm 不允许 InvoiceNumberService 中显示的原子更新增量。我可以回退到本机 mongo 驱动程序。但是我如何确保对 collection.findOneAndUpdate() 的调用与 Mikro-Orm 管理的实体共享相同的事务?
Mikro-Orm 需要一个唯一的请求上下文。在 NestJS 的示例中,这个独特的上下文是在控制器级别创建的。在上面的示例中,服务方法不一定由控制器调用。因此,每次调用 OrderService.createNewOrder() 都需要一个新的上下文,该上下文的生命周期限定为函数调用,对吗?我怎样才能做到这一点?
如何在服务之间共享相同的请求上下文?在上面的示例中,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