Nos utilisateurs passent par plusieurs étapes de flux de travail - plus ils vont loin, plus nous créons d'objets. Nous permettons également aux utilisateurs de revenir à l'étape 1 et de modifier l'un des objets existants. Ce qui peut entraîner des incohérences, nous devons donc mettre à jour / supprimer certains des objets à l'étape 2. Je vois 2 options:
Mettez à jour / supprimez immédiatement les objets de l'étape 2. Cela mène à:
Correction de l'étape n ° 2 paresseusement - lorsque l'utilisateur s'y rend (notre approche actuelle). L'étape n ° 2 reconnaîtra que les objets sont incohérents et les corrigera. Ce qui conduit à un seul endroit où nous devons nous soucier, mais:
Quelqu'un connaît de meilleures approches? Ou peut-être des améliorations à ces deux?
Je n'ai pas trouvé de solution parfaite, mais nous avons finalement implémenté une version améliorée de # 1. Lors de la mise à jour de l'état à l'étape 1, nous définissons également un indicateur «besoin de reconstruire l'étape 2», lorsque l'interface utilisateur ouvre l'étape 2, il vérifie d'abord cet indicateur et émet un PUT pour reconstruire l'état, puis seulement il obtient l'étape 2.
Cela signifie toujours que l'état de la base de données est incohérent pendant un certain temps. Mais au moins, nous le saurons avec certitude grâce au drapeau dans DB. Et si nécessaire, nous pourrions écrire des migrations en tenant compte de cet indicateur. Cela permet également (si nécessaire à l'avenir) de créer un travail asynchrone pour corriger l'état.
3 Réponses :
Je pense qu'il est plus flexible de séparer l' état et le contexte où les objets sont stockés. Toute création d'un nouvel objet à n'importe quelle étape s'accompagne de la préservation de l'invariant et de la cohérence du contexte.
Il existe des règles d'états distinctes - ce sont des règles de transition de l'un à l'autre et des objets disponibles pour la création et des règles distinctes pour le contexte, des règles pour sa cohérence, qui est assurée à chaque fois qu'il change.
Je ne sais pas comment cela se rapporte. Dans votre cas, vous avez un contexte qui doit garder les choses cohérentes. Le problème reste le même - "invoquez-vous" le contexte tout de suite pour rendre les choses cohérentes (et compliquer les opérations PUT / PATCH) ou faites-vous cela paresseusement lors du prochain GET?
Je veux dire que la modification des données ne doit pas se produire lors de l'extraction de données, mais lors de la création
Qu'en est-il du nettoyage asynchrone des données sales?
En fonction de votre logique, 3) peut entraîner une erreur de l'utilisateur (par exemple, l'utilisateur devra répéter l'étape 2). Si le travailleur DataCleanup a suffisamment de ressources (c'est-à-dire qu'il traite la table DirtyData presque instantanément), cela ne devrait se produire qu'en de très rares occasions. Si ce n'est pas OK, vous pouvez opter pour la vérification des données sales à chaque extraction, mais cela pourrait être coûteux.
Les vérifications asynchrones sont excellentes si les données de l'étape 2 deviennent simplement obsolètes (nous ne les référencons pas) et sont faciles à déterminer à partir des colonnes de la base de données. Comme vous l'avez mentionné, cela devient un travail de nettoyage. Mais ma fonctionnalité est moins simple - par exemple, si quelque chose a été ajouté à l'étape 1, je devrais supprimer, mettre à jour ou ajouter des éléments à l'étape 2, et il est difficile de déterminer uniquement à partir des données de la base de données. Le travail de nettoyage n'est donc pas faisable. Exécuter des vérifications incorrectes à chaque extraction comme vous l'avez suggéré plus tard - est en fait une opération assez bon marché par rapport à d'autres choses à l'étape 2. C'est la raison pour laquelle nous l'avons choisi au départ.
Il semble que vous soyez familier avec la spécification HTTP concernant les requêtes GET, mais pour les futurs lecteurs:
Pour l'autre puce sous 2, nous n'avons probablement pas besoin d'une spécification pour convenir que la persistance de données valides est préférable à la persistance de données invalides.
Alors, que pouvons-nous faire pour les puces sous 1 pour éviter une logique de branchement complexe dans une étape particulière et également des dépendances circulaires? Ma suggestion est une conception événementielle. Lorsque l'étape 2 change, elle doit déclencher un événement de modification. Dans ce scénario, l'étape 2 n'a aucune connaissance du ou des écouteurs concrets susceptibles de recevoir ses événements, elle reste donc découplée de toute logique de traitement complexe.
Il n'y a probablement aucun moyen de garantir que vous n'oublierez rien à l'avenir; mais si chaque étape du flux de travail est définie comme un écouteur, cela vous oblige à prendre en compte les événements de changement dans une certaine mesure chaque fois que vous implémentez une nouvelle étape.
Une note latérale sur la granularité: si une étape comporte de nombreux changements, elle peut regrouper ses événements plutôt que de déclencher chacun d'eux individuellement. Vous pouvez ajuster la taille pour plus d'efficacité.
En résumé, je considérerais fortement le modèle de conception Observer .
Oui, Observer délie 2 fonctionnalités au moment de la compilation. Le seul inconvénient qui reste (et j'ai oublié de le mentionner) est que pour déterminer si l'étape n ° 2 a besoin de mises à jour, nous devons charger ses données. Cela signifie que peu importe si nous voulons réellement mettre à jour quelque chose à l'étape 2, nous devrons faire un travail supplémentaire qui ralentit toutes les opérations à l'étape 1. Eh bien, à moins que ces informations ne viennent de l'interface utilisateur ... Mais cela lie la fonctionnalité. À un niveau différent cependant.
Les mises à jour peuvent être asynchrones. Bien sûr, cela ajoute de la complexité en soi, mais il est possible d'éviter les performances affectées à l'étape 1.
La mise en cache est une autre solution au problème de performances et peut être plus simple que la logique asynchrone.
Une troisième option: vous pouvez exécuter un travail par lots pendant la nuit pour réconcilier l'état de tout flux de travail modifié. Ensuite, vous obtenez une sorte de cohérence éventuelle dans la base de données.
@ jaco0646, bien l'utilisateur peut passer à l'étape 2 tout de suite, donc même si nous allons avec le travail, nous devrons être en mesure de corriger l'état lors du prochain GET. Bien que nous puissions combiner les approches et garantir que les expériences sont dans un état cohérent même si l'utilisateur a quitté la maison sans passer à l'étape suivante.