Sommaire
L'autorisation dans CQRS / DDD doit-elle être implémentée par commande / requête ou non?
Je développe pour la première fois une application en ligne utilisant plus ou moins strictement le modèle DDD CQRS. J'ai rencontré un problème que je n'arrive pas vraiment à comprendre.
L'application que je construis est une application de grand livre permettant aux gens de créer des grands livres, ainsi qu'à d'autres personnes de les afficher / modifier / supprimer, comme les employés. Le créateur d'un livre doit pouvoir modifier les droits d'accès du livre qu'il a créé. Pourrait même changer de propriétaire. Le domaine a deux agrégats TLedger et TUser .
J'ai lu beaucoup de messages avec le mot-clé DDD / CQRS concernant la sécurité, l'autorisation, etc. La plupart d'entre eux ont déclaré que l'autorisation était un sous- domaine générique , à moins que l'on ne crée une application de sécurité.
Dans ce cas, le domaine principal est certainement un domaine comptable intéressé par les transactions, l'équilibre et les comptes. Mais la fonctionnalité de pouvoir gérer un accès fin aux registres est également requise. Je me demande comment concevoir cela en termes DDD / CQRS.
Il est indiqué dans les didacticiels DDD partout dans le monde que les commandes font partie du langage omniprésent. Ils sont significatifs. Ce sont des actions concrètes qui représentent la «vraie chose».
Étant donné que toutes ces commandes et requêtes sont des actions réelles que les utilisateurs exécuteraient dans la «vie réelle», la mise en œuvre de l'autorisation devrait-elle être associée à toutes ces «commandes» et «requêtes»? Un utilisateur serait autorisé à exécuter TLedger.addTransaction () mais pas TLedger.removeTransaction () par exemple. Ou, un utilisateur serait autorisé à exécuter la requête "getSummaries ()" mais pas "getTransactions ()".
Un mappage tridimensionnel existerait sous la forme d'une commande de registre utilisateur ou d'une requête de registre utilisateur pour déterminer les droits d'accès.
Ou, de manière découplée, des «autorisations» nommées seraient enregistrées pour un utilisateur. Autorisations qui seraient ensuite mappées pour des commandes spécifiques. Par exemple, l'autorisation "ManageTransactions" permettrait à un utilisateur d'exécuter "AddTransaction ()", "RemoveTransaction ()", etc.
Utilisateur de mappage des autorisations -> ledger -> commande / requête
Utilisateur de mappage des autorisations -> registre -> autorisation -> commande / requête
C'est la première partie de la question. Ou en bref, l'autorisation dans CQRS / DDD doit-elle être implémentée par commande ou par requête? Ou, l'autorisation doit-elle être découplée des commandes?
Deuxièmement, concernant l'autorisation basée sur les autorisations. Un utilisateur doit être en mesure de gérer les autorisations sur ses livres ou sur les livres qu'il a été autorisé à gérer.
- Les commandes de gestion des autorisations se produisent dans le Ledger
J'ai pensé ajouter les événements / commandes / gestionnaires dans l' agrégat Ledger , tels que grantPermission (), revokePermission (), etc. Dans ce cas, l'application de ces règles se produirait dans les gestionnaires de commandes. Mais cela nécessiterait que toutes les commandes incluent l'ID de l'utilisateur qui a émis cette commande. Ensuite, je vérifierais dans le TLedger si l'autorisation existe pour cet utilisateur d'exécuter cette commande.
Par exemple :
class TLedger{
function addTransactionCmdHandler(cmd){
if (!this.permissions.exist(user, 'addTransaction')
throw new Error('Not Authorized');
}
}
- Commandes de gestion des autorisations dans l'utilisateur
L'autre solution consisterait à inclure les autorisations dans le TUser. Un TUser aurait un ensemble d'autorisations. Ensuite, dans les gestionnaires de commandes TLedger, je récupérais l'utilisateur et vérifiais s'il était autorisé à exécuter la commande. Mais cela m'obligerait à récupérer l'agrégat TUser pour chaque commande TLedger.
class TAddTransactionCmdHandler(cmd) {
this.userRepository.find(cmd.userId)
.then(function(user){
if (!user.can(cmd)){
throw new Error('Not authorized');
}
return this.ledgerRepository.find(cmd.ledgerId);
})
.then(function(ledger){
ledger.addTransaction(cmd);
})
}
- Un autre domaine avec service
Une autre possibilité serait de modéliser complètement un autre domaine d'autorisation. Ce domaine serait intéressé par les droits d'accès, l'autorisation etc. Le sous-domaine comptable utiliserait alors un service pour accéder à ce domaine d'autorisation sous la forme de AuthorizationService.isAuthorized(user, command)
.
class TAddTransactionCmdHandler(cmd) {
authService.isAuthorized(cmd)
.then(function(authorized){
if (!authorized) throw new Error('Not authorized');
return this.ledgerRepository.find(cmd.ledgerId)
})
.then(function(){
ledger.addTransaction(cmd);
})
}
Quelle serait la décision la plus "DDD / CQRS"?
la source
Réponses:
Pour la première question, j'ai eu du mal avec quelque chose de similaire. De plus en plus, je penche pour un régime d'autorisation en trois phases:
1) Autorisation au niveau commande / requête "cet utilisateur a- t-il déjà la permission d'exécuter cette commande?" Dans une application MVC, cela pourrait probablement être géré au niveau du contrôleur, mais j'opte pour un pré-gestionnaire générique qui interrogera le magasin d'autorisations en fonction de l'utilisateur actuel et de la commande d'exécution.
2) L'autorisation au sein du service d'application de "est-ce que cet utilisateur" a * déjà la permission d'accéder à cette entité? "Dans mon cas, cela finira probablement par être une vérification implicite simplement au moyen de filtres sur le référentiel - dans mon domaine, c'est essentiellement un TenantId avec un peu plus de granularité de OrganizationId.
3) L'autorisation qui repose sur les propriétés transitoires de vos entités (telles que le statut) serait gérée à l'intérieur du domaine. (Ex. "Seules certaines personnes peuvent modifier un grand livre fermé.") Je choisis de mettre cela à l'intérieur du domaine car il dépend fortement du domaine et de la logique métier et je ne suis pas vraiment à l'aise d'exposer cela à d'autres endroits.
J'adorerais entendre les réponses des autres à cette idée - déchirez-la si vous le souhaitez (fournissez simplement des alternatives si vous le faites :))
la source
Je voudrais implémenter l'autorisation dans le cadre de votre autorisation BC, mais la déployer en tant que filtre d'action dans votre système Ledger. De cette façon, ils peuvent être découplés logiquement les uns des autres - votre code Ledger ne devrait pas avoir à appeler le code d'autorisation - mais vous obtenez toujours une autorisation en cours de haute performance pour chaque demande entrante.
la source