Межсервисные транзакции Mikro-orm в NestJS

Я оцениваю Mikro-Orm для будущего проекта. Есть несколько вопросов, на которые я либо не нашел ответа в документации, либо не до конца их понял.

Позвольте мне описать минимально сложный пример (NestJS): у меня есть система обработки заказов с двумя объектами: Orders а также Invoicesа также таблица счетчиков для последовательных номеров счетов (юридическое требование). Важно отметить, что метод create OrderService не всегда вызывается контроллером, но также и через систему 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.

1 ответ

Решение

Я начну с плохих новостей, транзакции mongodb еще не поддерживаются в 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, но в будущем это, вероятно, вернет session объект в монго).

Также обратите внимание, что в драйвере mongo неявные транзакции не будут включены по умолчанию (хотя его можно будет настроить), поэтому вам нужно будет использовать явное разграничение транзакций через em.transactional(...).

В RequestContextпомощник работает автоматически. Вы просто регистрируете его как промежуточное ПО (выполняется автоматически в адаптере nestjs orm), а затем ваш обработчик запросов (метод маршрута / конечной точки / контроллера) запускается внутри домена, который разделяет контекст. Благодаря этому все службы в DI могут совместно использовать одноэлементные экземпляры репозиториев, но они автоматически выбирают правильный контекст из домена.

В основном у вас есть этот автоматический контекст запроса, а затем вы можете создавать новые (вложенные) контексты вручную с помощью em.transactional(...).

https://mikro-orm.io/docs/transactions/

Другие вопросы по тегам