J'évalue Mikro-Orm pour un futur projet. Il y a plusieurs questions auxquelles je n'ai pas pu trouver de réponse dans les documents ou je ne les ai pas bien comprises.

Permettez-moi de décrire un exemple complexe minimal (NestJS) : j'ai un système de traitement des commandes avec deux entités : Orders et Invoices ainsi qu'une table de compteur pour les numéros de facture séquentiels (exigence légale). Il est important de mentionner que la méthode de création OrderService n'est pas toujours appelée par un contrôleur, mais également via le système crobjob/queue. Mes questions concernent le cas d'utilisation de la création d'une nouvelle commande :

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 } });
  }
}

L'ensemble du cas d'utilisation de la création d'une nouvelle commande avec tous les appels de service ultérieurs devrait se produire dans une transaction Mikro-Orm. Donc, si quelque chose est lancé dans OrderService.createNewOrder() ou l'une des méthodes appelées par la suite, toute la transaction doit être annulée.

  1. Mikro-Orm n'autorise pas l'incrément de mise à jour atomique affiché dans InvoiceNumberService. Je peux me rabattre sur le pilote mongo natif. Mais comment m'assurer que l'appel à collection.findOneAndUpdate() partage la même transaction que les entités gérées par Mikro-Orm ?

  2. Mikro-Orm a besoin d'un contexte de requête unique. Dans les exemples pour NestJS, ce contexte unique est créé au niveau du contrôleur. Dans l'exemple ci-dessus, les méthodes de service ne sont pas nécessairement appelées par un contrôleur. J'aurais donc besoin d'un nouveau contexte pour chaque appel à OrderService.createNewOrder() qui a une durée de vie limitée à l'appel de fonction, n'est-ce pas ? Comment puis-je y parvenir ?

  3. Comment puis-je partager le même contexte de requête entre les services ? Dans l'exemple ci-dessus, InvoiceService et InvoiceNumberService auraient besoin du même contexte que OrderService pour que Mikro-Orm fonctionne correctement.

2
florian norbert bepunkt 25 févr. 2020 à 13:18

1 réponse

Meilleure réponse

Je vais commencer par la mauvaise nouvelle, les transactions mongodb ne sont pas encore prises en charge dans MikroORM (bien qu'elles atterriront probablement dans quelques semaines, le PoC a déjà été mis en œuvre). Vous pouvez vous abonner ici pour les mises à jour : https://github.com/mikro-orm /mikro-orm/issues/34

Mais permettez-moi de répondre au reste car il s'appliquera alors :

Vous pouvez utiliser const collection = (em as EntityManager<MongoDriver>).getConnection().getCollection('counter'); pour obtenir la collection à partir de l'instance de connexion mongo interne. Vous pouvez également utiliser orm.em.getTransactionContext() pour obtenir le contexte de transaction actuel (actuellement implémenté uniquement dans les pilotes SQL, mais à l'avenir, cela renverra probablement l'objet session dans mongo).

Notez également que dans le pilote mongo, les transactions implicites ne seront pas activées par défaut (elles seront cependant configurables), vous devrez donc utiliser une démarcation de transaction explicite via em.transactional(...).

L'assistant RequestContext fonctionne automatiquement. Vous l'enregistrez simplement en tant que middleware (fait automatiquement dans l'adaptateur nestjs orm), puis votre gestionnaire de requêtes (méthode route/point de terminaison/contrôleur) est exécuté dans un domaine qui partage le contexte. Grâce à cela, tous les services de la DI peuvent partager des instances singleton de référentiels, mais ils choisiront automatiquement le bon contexte dans le domaine.

Vous avez essentiellement ce contexte de demande automatique, puis vous pouvez créer de nouveaux contextes (imbriqués) manuellement via em.transactional(...).

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

0
Martin Adámek 25 févr. 2020 à 11:05