Question

Je suis toujours aux prises avec ce qui doit être les questions de base (et résolu) liés à l'architecture de style CQRS:

Comment mettre en œuvre des règles commerciales qui se fondent sur un ensemble Racines globales?

Prenez, à titre d'exemple, une application de réservation. Il peut vous permettre de réserver des billets pour un concert, des sièges pour un film ou une table dans un restaurant. Dans tous les cas, il va seulement être un nombre limité des « articles » à vendre.

Imaginons que l'événement ou le lieu est très populaire. Ouverture de la vente pour un nouveau créneau d'événements ou de temps, les réservations commencent à arriver très rapidement - peut-être beaucoup par seconde

.

Du côté de la demande, nous pouvons développer massivement, et les réservations sont mis sur une file d'attente à traiter de manière asynchrone par un composant autonome. Au début, quand nous nous retirons de commandes de réservation de la file d'attente, nous les accepterons, mais à un certain moment, nous devrons commencer rejeter le reste .

Comment savons-nous quand nous atteignons la limite?

Pour chaque commande la réservation, nous devrions interroger une sorte de magasin pour savoir si nous pouvons répondre à la demande. Cela signifie que nous aurons besoin de savoir combien de réserves que nous avons déjà reçu à ce moment-là.

Cependant, si le magasin de domaine est un ensemble de données non relationnelles stockent telles que par exemple Windows Azure Table de stockage, nous ne pouvons très bien faire un SELECT COUNT(*) FROM ...

L'une des options serait de garder un Racine séparée Aggregate qui conserve simplement une trace du nombre actuel, comme ceci:

  • AR: réservation (? Qui combien)
  • AR: emplacement de l'événement / heure / date (nombre total)

La seconde racine globale serait une agrégation dénormaliser de la première, mais lorsque le magasin de données sous-jacentes ne supporte pas les transactions, il est très probable que ceux-ci peuvent désynchronisés dans les scénarios de grand volume (ce qui est ce que nous tentent d'adresse en premier lieu).

Une solution possible est de serialize Gestion des commandes de réservation afin qu'un seul à la fois est manipulé, mais cela va à l'encontre de nos objectifs d'évolutivité (et redondance).

Ces scénarios me rappellent la norme « rupture de stock » scénarios, mais la différence est que nous ne pouvons pas très bien mettre la réservation en commande. Une fois qu'un événement est vendu, il est vendu, je ne peux pas voir ce que l'action de compensation serait.

Comment pouvons-nous gérer ces scénarios?

Était-ce utile?

La solution

Après avoir réfléchi à cela pendant un certain temps finalement compris que le problème sous-jacent est moins lié à CQRS que de la nature non trasactional des services disparates REST.

En réalité, il se résume à ce problème: si vous avez besoin de mettre à jour plusieurs ressources, comment assurez-vous la cohérence si la deuxième opération d'écriture échoue

Let imaginons que nous voulons écrire mises à jour de ressources A et B dans l'ordre.

  1. Ressource A est mis à jour avec succès
  2. La tentative de mise à jour des ressources B échoue

La première opération d'écriture ne peut pas facilement être annulée face à une exception, que pouvons-nous faire? La capture et la suppression de l'exception à effectuer une action contre la compensation des ressources A est pas une option viable. Tout d'abord il est complexe à mettre en œuvre, mais d'autre part, il est pas sûr: ce qui se passe si la première exception a eu lieu à cause d'une connexion réseau a échoué? Dans ce scénario, nous ne pouvons pas écrire une action contre la compensation des ressources A soit.

La clé réside dans explicitement idempotence . Alors que Windows Azure Queues ne garantissent pas exactement une fois sémantique , ils ne garantissent au moins une fois sémantique. Cela signifie que face à des exceptions intermittentes, le message sera ensuite rejouée .

Dans le scénario précédent, voici ce qui se passe alors:

  1. Ressource A est TENTATIVE mis à jour. Cependant, la reprise est détectée si l'état de n'est pas affectée. Cependant, l'opération 'écriture' réussit.
  2. Resource B est mis à jour avec succès.

Lorsque toutes les opérations d'écriture sont idempotent, cohérence éventuelle peut être réalisé avec replays message .

Autres conseils

Une question intéressante et celui-ci vous clouez un des points de la douleur dans CQRS.

La façon dont Amazon est la manipulation de c'est d'avoir le scénario d'affaires à faire face à un état d'erreur si les éléments demandés sont vendus. L'état d'erreur est simplement d'informer le client par e-mail que les éléments demandés actuellement en stock et le jour prévu pour la livraison.

Cependant -. Cela ne répond pas complètement à votre question

La pensée d'un scénario de vente de billets, je veillerais à dire au client que la demande qu'ils ont été Demande de réservation . Que la demande de réservation serait traitée se le plus tôt possible et qu'ils vont faire revivre la réponse finale dans un courrier plus tard. En ce alowing certains clients pourraient recevoir un courriel avec un refus à leur demande.

. Pouvons-nous faire de cette rejestion moins painfull? Certainement. En insérant une clé dans notre cache distribué avec le pourcentage ou le montant des articles en stock et décrémenter ce compteur si jamais un objet est vendu. De cette façon, nous pourrions avertir l'utilisateur avant que la demande de réservation est donnée, disons que si seulement 10% du nombre initial d'éléments est à gauche, que le client pourrait ne pas être en mesure d'obtenir l'article en question. Si le compteur est à zéro nous simplement refuser d'accepter toute demande de réservation plus.

Mon point étant:

1) laisser le savoir utilisateur que c'est une demande qu'ils font et que cela pourrait se refuser 2) informer l'utilisateur que les chances de succès pour obtenir l'élément en question est faible

Pas exactement une réponse précise à votre question, mais voici comment je gérer un scénario comme celui-ci lorsqu'ils traitent avec CQRS.

Le eTag permet l'accès concurrentiel optimiste que vous pouvez utiliser à la place de verrouillage transactionnel de mettre à jour un document et gérer en toute sécurité les conditions de course possibles. Voir remarque ici http://msdn.microsoft.com/en-us/library/ dd179427.aspx pour plus d'informations.

histoire pourrait aller quelque chose comme ceci: L'utilisateur A crée un événement E avec des billets maximum de 2, eTag est 123. En raison de la forte demande 3 utilisateurs tentent d'acheter les billets à peu près en même temps. L'utilisateur B crée une demande de réservation B. L'utilisateur C crée une demande de réservation C. L'utilisateur D crée une demande de réservation D.

Système S reçoit une demande de réservation B, lit événement ETAG 123 et modifie l'événement pour avoir 1 billet restant, S présente la mise à jour, y compris eTag 123 qui correspond à eTag d'origine pour la mise à jour réussit. ETag est maintenant 456. Demande de réservation est approuvée et l'utilisateur est informé a réussi.

Un autre système S2 reçoit de demande de réservation C en même temps que système S traitement de la demande a été B, il lit également l'événement avec l'événement eTag 123 changements qu'il reste à 1 ticket et tente de mettre à jour le document. Cependant, cette fois eTag 123 ne correspond pas à la mise à jour échoue avec une exception. S2 tentatives système de retenter l'opération en relisant le document qui a maintenant eTag 456 et un nombre de 1 donc il décrémente ce à 0 et resoumet ETAG 456.

Malheureusement pour l'utilisateur C utilisateur, système S a commencé à traiter la demande de D'd'utilisateur immédiatement après l'utilisateur B et lire le document ETAG 456, mais parce que le système S est plus rapide que le système S2, il a été en mesure de mettre à jour l'événement avec eTag 456 avant que le système S2 si utilisateur D a également réservé son billet avec succès. eTag est maintenant 789

système S2 échoue à nouveau, il donne encore une autre tentative, mais cette fois-ci quand il lit l'événement avec eTag 789 il voit qu'il n'y a pas de billets disponibles et refuse donc la demande de réservation de l'utilisateur C.

Comment vous avertir les utilisateurs que leurs demandes de réservation ont réussi ou non à vous. Vous pouvez simplement interroger le serveur toutes les quelques secondes et attendre l'état de réservation à jour.

Let regard à la perspective d'affaires (je traite des choses semblables - rendez-vous sur réservation emplacements libres) ...

La première chose dans votre analyse qui me frappe comme étant hors est qu'il n'y a aucune notion d'un billet reservable / siège / table. Ce sont les ressources qui sont réservées.

Dans le cas d'être transactionnel, vous pouvez utiliser une certaine forme d'unicité pour garantir qu'une double réservation ne se produit pas pour le même billet / siège / table (plus d'infos sur http://seabites.wordpress.com/2010/11/11/consistent-indexes-constraints ). Ce scénario exige le traitement de commande synchrone (mais toujours en même temps).

Dans le cas de ne pas être transactionnel, vous pouvez surveiller rétroactivement le flux d'événements et de compenser la commande. Vous pouvez même donner à l'utilisateur final une expérience d'attendre la confirmation de réservation jusqu'à ce que le système sait que - soit après l'analyse de flux d'événements - que la commande terminée et a été ou n'a pas été compensé (ce qui revient à « a été la réservation faite? Oui ou non?"). En d'autres termes la rémunération pourrait faire partie du cycle de confirmation.

dos étape Let un peu plus ...

Lors de la facturation est également impliqué (par exemple la vente de billets en ligne), je pense que ce scénario évolue dans une saga de toute façon (billet + réserve de billets de facture). Même sans facturation, vous auriez une saga pour rendre l'expérience crédible (tableau réserve + confirmation de réservation). Ainsi, même si vous ne zoomant sur un seul aspect de la réservation d'un billet / table / siège (c.-à-est-il toujours disponible), la transaction « longue durée » n'est pas complète jusqu'à ce que je payé pour ou jusqu'à ce que je l'a confirmé . L'indemnisation se de toute façon, libérant ainsi le ticket à nouveau quand j'interrompra la transaction pour quelle que soit la raison. La partie intéressante est maintenant comment l'entreprise veut faire face à ceci: peut-être un autre client aurait terminé l'opération lui avait donné nous / elle le même billet. Dans ce cas, le remboursement pourrait devenir plus intéressante lorsque vous double réservation d'un billet / siège / Table - même offrant un rabais sur un prochain événement / similaire pour compenser la gêne occasionnée. La réponse se trouve dans le modèle d'affaires, pas le modèle technique.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top