2015-03-07 1 views
4

Dans mon application actuelle, j'ai l'appel MongoDB FindAndModify suivant au serveur MongoDBOù FindAndModify s'intègre-t-il dans CQRS?

public static TEntity PeekForRemoveSync<TEntity>(this MongoCollection<TEntity> collection, string reason) 
    where TEntity : class, IEntity, IUpdatesTrackable, ISyncable, IDeletable, IApprovable 
{ 
    if (reason == null) 
    { 
     throw new ArgumentNullException(nameof(reason)); 
    } 

    IMongoQuery query = Query.And(
     Query<TEntity>.EQ(e => e.SyncInfo.IsDirty, true), 
     Query<TEntity>.NE(e => e.Deletion, null), 
     Query<TEntity>.NE(e => e.Approval, null), 
     Query<TEntity>.EQ(e => e.SyncInfo.HumanInvestigateFlag, null), 
     Query.Or(
      Query<TEntity>.EQ(e => e.SyncInfo.IsUpdateInProgress, false), 
      Query<TEntity>.LT(e => e.SyncInfo.UpdateStartInfo.OccuredOn, DateTime.UtcNow.AddSeconds(-SyncConstants.PeekExpireTimeInSeconds)) 
     ), 
     Query<TEntity>.LTE(e => e.SyncInfo.PeekedCount, MaxPeekedCount) 
    ); 

    return PeekForSync(collection, query, reason); 
} 

private static TEntity PeekForSync<TEntity>(this MongoCollection<TEntity> collection, IMongoQuery query, string reason) 
    where TEntity : class, IEntity, IUpdatesTrackable, ISyncable, IDeletable 
{ 
    UpdateBuilder<TEntity> update = Update<TEntity> 
     .Inc(e => e.SyncInfo.PeekedCount, 1) 
     .Set(e => e.SyncInfo.UpdateStartInfo, new OccuranceWithReason(reason)) 
     .Set(e => e.SyncInfo.IsUpdateInProgress, true); 

    SortByBuilder<TEntity> sort = SortBy<TEntity>.Descending(e => e.LastUpdatedOn); 

    FindAndModifyArgs fmArgs = new FindAndModifyArgs 
    { 
     Query = query, 
     Update = update, 
     SortBy = sort, 
     VersionReturned = FindAndModifyDocumentVersion.Modified 
    }; 

    FindAndModifyResult result = collection.FindAndModify(fmArgs); 

    return result.ModifiedDocument != null 
     ? result.GetModifiedDocumentAs<TEntity>() 
     : null; 
} 

Am I reinventing yet another queue system with MongoDB? That's not my intention here but it's what it's. Just ignore the business logic there.

En CQRS, la requête et commande signifie ce qui suit (je crois):

Commande: Change l'état du système et ne renvoie aucune valeur.

Interrogation: Ne modifie pas l'état du système et ne renvoie aucune valeur.

Si tel est le cas, d'où mon appel FindAndModify ci-dessus (ou tout autre similaire comme cette requête) correspond?

Répondre

3

Cela va être opiniâtres, mais voici mon avis sur la question:

Un CQRS commandement doit généralement prendre des mesures spécifiques sur une seule entité globale.

Exemples de commandes:

  • ChangePersonName
  • PlaceOrder
  • DeleteAccount

Si les données sont nécessaires dans la mise en œuvre d'une mise en œuvre du gestionnaire de commandes, il doit provenir d'un document qui est trouvé strictement par son ID de document. (Ou ObjectId dans Mongo)

Dans certaines bases de données de documents, l'interrogation par quelque chose d'autre que l'ID est une opération éventuellement cohérente. Si vous comptez sur les résultats d'une requête pour décider sur quoi agir, il se peut que certains éléments soient manquants lorsqu'un index est obsolète. Comment sauriez-vous que la commande s'est vraiment terminée contre toutes les données? Tu ne le ferais pas. Si les résultats de la requête étaient en effet parfaitement cohérents (ce qui peut être le cas dans Mongo, je ne suis pas sûr à 100%), même alors vous avez le problème du timing. Vous avez peut-être eu l'intention d'agir sur toutes les données correspondant à votre requête, mais peut-être quelques millisecondes plus tard, de nouvelles données seront insérées qui correspondraient à la requête. Étant donné que la portée de l'opération n'était pas limitée à un document spécifique (ou à un ensemble de documents), vous n'auriez vraiment aucun moyen de savoir si vous étiez vraiment ou non.

En général, je dirais que l'exécution d'une requête (sur autre chose que l'ID de document) à partir d'une commande CQRS n'est pas une bonne idée.

FindAndModify semble qu'il est utilisé pour la correction. Vous pouvez en effet avoir besoin de le faire de temps en temps, mais cela ne correspondrait pas aux modèles CQRS.Je le réserverais pour des tâches de maintenance - en dehors de la partie principale du système CQRS.

+0

Point intéressant. Je vois que vous ne devriez pas lancer une * requête * contre votre 'cache de lecture', car cela pourrait être incohérent, mais pour reconstruire l'état, je devrais interroger * quelque chose *? Je suppose que c'est ce que vous voulez dire par "chargé par id", mais quelle autre méthode de chargement existe-t-il (ORMs, ODMs et similaire hellspawn?) Dans MongoDB, la valeur par défaut est read from the primary autorise explicitement les lectures périmées des secondaires. – mnemosyn

+1

Désolé, ma terminologie [RavenDB] (http://ravendb.net) me gêne. Mais l'idée est toujours valable pour Mongo et autres db's. Mise à jour ma réponse –

+0

thx! * "vous n'auriez vraiment aucun moyen de savoir si vous étiez vraiment fait ou pas" * 'FindAndModify' dans le pilote MongoDB appelle le [' db.collection.findAndModify'] (http://docs.mongodb.org/manual/ reference/method/db.collection.findAndModify /) qui est atomique. – tugberk

1

Je ne suis pas sûr que je suis bien compris votre question, voici donc la réponse à ce que je compris:

J'aime voir CQRS comme une combinaison d'un modèle d'accès concurrentiel et un guide de modèle de données . La ligne directrice du modèle de données est la plus évidente, après tout, vous utilisez maintenant plus d'un modèle. Dans un non-CQRS-scénario, on pourrait avoir l'objet suivant pour une commande (inachevée) ou panier:

{ items : [ { 'sku' : '235423', 'qty' : 3, 'price' : 4.34, ... }, ... ], 
    orderStatus : 'in_cart', 
    customerId : null, 
    ... 
} 

Maintenant, mise à jour de ce modèle en lui-même serait facile, mais CQRS vous dit de ne pas. Supposons que je souhaite ajouter le produit '1234' à ce panier. Je pourrais poster le nouveau chariot entier, bien sûr. Mais c'est à la fois inefficace (tout ce dont j'ai besoin c'est l'info 'je veux ajouter 1 produit 1234 au panier X'), et irritant, car le prix total par exemple doit être ignoré par le serveur quand même.

En son cœur, cependant, je CQRS est principalement un schéma de concordance - au moins en combinaison avec Event Sourcing. En gardant des enregistrements de qui a changé quoi, nous pouvons centraliser le code qui est responsable de l'application des changements, pour gérer les conflits, permettre d'annuler/refaire et de conserver un journal d'audit en même temps. Bien sûr, cela se fait au détriment de la cohérence éventuelle, mais la notion est que an object that has many concurrent writers doesn't have a well-defined 'current' state anyway (c'est comme ça que je l'interprète, au moins). De plus, en regardant ce chariot à nouveau, peut-être mettre à jour le modèle n'est pas trivial après tout, car le panier probablement représente un verrou sur les objets dans le panier (au moins pour les sites avec une forte probabilité de conflits, par exemple ventes flash - si le produit est dans le panier, je voudrais savoir que je peux réellement l'acheter, disons pendant 10 minutes). Sans objets de commande, il faudrait analyser comment le document est passé de l'état 1 à l'état 2 et ce qui a changé exactement pour ne pas libérer de verrous existants, mais peut-être en acquérir un nouveau (ou en libérer un nouveau).

Au lieu de cela, vous pouvez envoyer un AddToCartCommand:

{ item : { 'sku' : '4242342', 'qty' : 4 }, 
    cartId : 53543 } 

Si ce produit a été vendu trop rapidement, il ne fait pas mal, puisque nous ne sommes pas abandonner le panier - ajouter à la commande panier échoué, mais l'état de la charrette est resté complètement inchangé. Si le site est soumis à une charge élevée et que l'acheteur a ajouté un autre produit, cette commande peut aboutir. Avec les modèles de demande/réponse traditionnels, cela n'aurait pas été facile, voire impossible.Ceci n'est probablement pas le meilleur exemple, mais supposons que le panier a été rempli par plusieurs utilisateurs simultanément, alors la simple 'dernière écriture gagne' la concurrence (pas de verrou) ou un look optimiste (erreur: quelqu'un d'autre a changé le document entre les deux) serait problématique. Dans CQRS, puisque nous n'effectuons pas de mises à jour immédiates, nous pouvons éviter à la fois RPC (un appel de méthode qui me dit ce qui s'est passé immédiatement) et trop d'interfaces spécifiques au client. En outre, contrairement à RPC vs REST, CQRS va au moins une couche plus profonde. Bien que cela affecte beaucoup le code client, cela a des implications énormes sur la façon dont le serveur traite les informations, de manière asynchrone et potentiellement distribuée, pouvant même impliquer des banques de données complètement différentes (puisque les modèles sont différents de toute façon). En tout cas, je pense que le code que vous avez affiché pourrait être le point où les deux modèles communiquent, c'est-à-dire où les changements doivent être appliqués, ce qui peut être difficile. D'une part, cela nécessite une transaction (distribuée), donc beaucoup de verrouillage en cours et le verrouillage nécessite des mises à jour atomiques, de sorte que le findAndModify semble être une nécessité dans ce cas. Je ne suis pas sûr de ce que le piaulement fait, mais pour moi, votre code semble essayer d'obtenir un court verrou sur le document (à partir des critères IsDirty : true et IsUpdateInProgress : false). En un mot, je dirais que CQRS est l'async du web.

+0

thx! * "La ligne directrice du modèle de données est la plus évidente, après tout, vous utilisez maintenant plus d'un modèle" * Pouvez-vous préciser ce que vous entendez par là? – tugberk

0

Les opérations FindAndModify se trouvent généralement dans les applications centrées sur les données. Il y a cependant quelques façons de trouver «FindAndModify» dans une application CQRS. FindAndModify peut être une commande légitime, si mal nommée, dans votre domaine. Vous acheminez une commande vers un agrégat unique qui exécute le 'FindAndModify'. Juste pour être clair, c'est un seul agrégat. Je suppose également que 'FindAndModify' signifie quelque chose dans votre domaine. L'autre emplacement pourrait se trouver dans un gestionnaire d'événements. Il n'est pas difficile d'imaginer une forme d'événement de type NameChanged. Cela pourrait déclencher une réponse de style FindAndModify. Je suppose que vous avez déjà une bonne connaissance de CQRS et de l'approvisionnement d'événements. Si ce n'est pas le cas et que vous cherchez un bon aperçu de la façon dont une application typique de CQRS ES s'intègre ensemble, jetez un oeil à mon blog: CQRS – A Step-by-Step Guide to the Flow of a typical Application

J'espère que c'est utile.