2016-12-06 1 views
1

je la tâche suivante:CQRS et commandes debouncing

  1. architecture actuelle SPA Web avec CQRS et MVVM. Nous avons commandes, requêtes et SingnalR comme bus de message.
  2. Les utilisateurs peuvent sélectionner, déplacer, redimensionner des divs sur le même espace de travail à partir de plusieurs navigateurs Web simultanément.
  3. Chaque div est lié au ViewModel approprié. Chaque ViewModel a sa propre requête à actualiser. Chaque ViewModel s'est abonné à des événements métier et a actualisé l'état entier après.

Lets image utilisateur fait les étapes suivantes:

  1. Sélectionnez div (SelectWidgetCommand envoyé)
  2. Déplacer div à x = 10. (ChangePositionCommand envoyé)
  3. Déplacer div vers x = 100. (ChangePositionCommand envoyé)

Le problème est que les commandes sont fire-and-forget et que l'utilisateur peut recevoir l'événement WidgetSelectedEvent au cours de l'étape 3, mais que ChangePositionCommand n'est peut-être pas encore géré. Ainsi, l'utilisateur recevra l'ancienne position x et la div ira à l'ancienne position.

Quelle est la meilleure pratique pour gérer ce genre de problèmes?

Ce que nous faisons maintenant est de diviser le DivViewModel en deux divs: SelectionViewModel, PositionViewModel. Chaque ViewModel a sa propre requête à actualiser et différents événements à gérer. Nous considérons également l'utilisation de Debounce et de Rolling Buffer pour la gestion des commandes.

+0

Votre solution semble être une bonne solution. Séparer en modèles plus petits, chacun synchronisé à travers un sous-ensemble spécifique d'événements. Une autre solution que je pourrais envisager serait d'avoir un seul modèle, mais créer des mappages d'attributs d'événements (par exemple mapper Map ViewModel.position à ChangePositionCommand.position). Tant que vos événements ne sont pas reçus dans le désordre, cela devrait fonctionner. Au fait, il est étrange d'avoir une commande selecton gérée côté serveur. L'état de sélection ne peut-il pas exister uniquement sur le client? – plalx

+0

@plalx Nous avons la même sélection pour tous les utilisateurs qui travaillent sur un espace de travail –

+0

N'est-ce pas créer un conflit inutile qui empêche presque la collaboration? Que faire si la sélection est modifiée par l'utilisateur B alors que l'utilisateur A essayait d'exécuter une commande sur sa sélection d'origine? – plalx

Répondre

1

Vous voudrez peut-être passer en revue la conférence de Greg Young au occasionally connected systems.

Le problème est que les commandes sont le feu et oublier

Comment comprenez-vous "fire-and-forget"? Si le modèle n'est pas autorisé à rejeter les commandes qui lui sont envoyées, les messages que vous envoyez sont événements, pas les commandes.

L'implémentation habituelle du modèle d'écriture dans CQRS est que les commandes sont linéarisées (traitées une à la fois). Les commandements doivent s'assurer que leurs conditions préalables sont remplies.

Compare-And-Set, plutôt que Set.

Dans sa forme la plus simple, les commandes spécifient la version initiale de l'agrégat qu'elles vont modifier, à peu près de la même manière que les requêtes conditionnelles dans HTTP spécifient preconditions on the resource. Dans le cas d'une condition de concurrence, avec plusieurs commandes essayant de changer la même partie du modèle, une commande gagnerait, et le perdant serait trivialement rejeté. Mieux (mais plus de travail à implémenter) serait d'implémenter une sorte de seconde chance pour la commande perdante - construire dans le modèle une compréhension suffisante qu'elle peut déterminer si les changements induits par les deux commandes sont en conflit. Si ce n'est pas le cas, alors vous les enchaînez.

Une approche alternative consiste à autoriser les deux écritures et à accepter l'existence d'un conflit. Pensez à la façon dont un système de contrôle de source fonctionne: J'ai engagé l'histoire 1-2-X, vous avez engagé l'histoire 1-2-Y, et maintenant il y a deux alternatives jusqu'à ce que quelqu'un les réconcilie avec une fusion.

Cette approche est à peu près en alignement avec le point central d'Udi Dahan dans son essai Race Conditions Don't Exist. Une différence de temps d'une microseconde ne devrait pas faire de différence dans les principaux comportements professionnels. Si vos vues sont construites à partir de listes d'événements (comme vous le décrivez ici), alors un endroit pour commencer avec une CRDT d'une liste. Mark Klepmann décrit un JSON data type(hacker new commentary). Cela ne vous permet pas de "pas de conflits", mais vous obtenez la propriété que deux utilisateurs qui voient les mêmes événements individuels les voient nécessairement dans le même ordre (s'il y a un problème, alors tout le monde voit le même problème). Mais l'approche de Greg est probablement plus simple à implémenter - les auteurs travaillent avec une copie locale du modèle partagé et interagissent avec lui, mais ce modèle partagé est une approximation, et les commandes qui ne peuvent pas être réconciliées par le livre d'enregistrement sont renvoyées. à l'auteur pour l'atténuation manuelle.

+0

le problème est que mes événements ne contiennent pas de données et je ne crée pas de ViewModels à partir d'événements. Lorsque ViewModel reçoit l'événement, il envoie la requête pour actualiser l'état entier de ViewModel. –