Sommario
L'autorizzazione in CQRS / DDD dovrebbe essere implementata per comando / query o no?
Sto sviluppando per la prima volta un'applicazione online che usa più o meno rigorosamente il pattern DDD CQRS. Mi sono imbattuto in qualche problema, che non riesco davvero a capire.
L'applicazione che sto creando è un'applicazione di contabilità generale che consente alle persone di creare libri mastro, oltre a consentire ad altre persone di visualizzarle / modificarle / cancellarle, come i dipendenti. Il creatore di un libro mastro dovrebbe essere in grado di modificare i diritti di accesso del libro mastro che ha creato. Potrebbe anche cambiare la proprietà. Il dominio ha due aggregati TLedger e TUser .
Ho letto molti post con la parola chiave DDD / CQRS relativa a sicurezza, autorizzazione, ecc. La maggior parte di loro ha dichiarato che l'autorizzazione era un sottodominio generico , a meno che non si stesse costruendo un'applicazione di sicurezza.
In questo caso, il dominio principale è sicuramente un dominio contabile interessato alle transazioni, al bilanciamento e agli account. È richiesta anche la funzionalità di poter gestire l'accesso a grana fine ai libri mastri. Mi chiedo come progettarlo nei termini DDD / CQRS.
Nelle esercitazioni DDD è dichiarato che i comandi fanno parte del linguaggio ubiquitario. Sono significativi. Sono azioni concrete che rappresentano la "cosa reale".
Poiché tutti questi comandi e query sono azioni effettive che gli utenti eseguono nella "vita reale", se l'implementazione dell'autorizzazione fosse accoppiato con tutti questi "comandi" e "query"? Un utente avrebbe l'autorizzazione per eseguire TLedger.addTransaction () ma non TLedger.removeTransaction () per esempio. Oppure, un utente può eseguire la query "getSummaries ()" ma non "getTransactions ()".
Una mappatura tridimensionale esisterebbe sotto forma di user-ledger-command o user-ledger-query per determinare i diritti di accesso.
Oppure, in modo disaccoppiato, le "autorizzazioni" nominate sarebbero registrate per un utente. Autorizzazioni che verrebbero quindi mappate per comandi specifici. Ad esempio, l'autorizzazione "ManageTransactions" consentirebbe all'utente di eseguire "AddTransaction ()", "RemoveTransaction ()", ecc.
-
Associazione dei permessi utente - > ledger - > comando / interrogazione
-
Associazione dei permessi utente - > ledger - > permesso - > comando / interrogazione
Questa è la prima parte della domanda. O in breve, l'autorizzazione in CQRS / DDD dovrebbe essere implementata per comando o per query? Oppure, l'autorizzazione dovrebbe essere disaccoppiata dai comandi?
In secondo luogo, riguardo all'autorizzazione basata sulle autorizzazioni. Un utente dovrebbe essere in grado di gestire le autorizzazioni sui suoi libri o sui libri mastri che è stato autorizzato a gestire.
- I comandi di gestione delle autorizzazioni si verificano nel libro mastro
Ho pensato di aggiungere eventi / comandi / gestori nell'aggregato Ledger , come grantPermission (), revokePermission (), ecc. In questo caso, l'applicazione di tali regole si verificava nei gestori di comandi . Ma ciò richiederebbe che tutti i comandi includessero l'id dell'utente che ha emesso tale comando. Quindi verificherei nel TLedger se esiste il permesso per quell'utente di eseguire quel comando.
Ad esempio:
class TLedger{
function addTransactionCmdHandler(cmd){
if (!this.permissions.exist(user, 'addTransaction')
throw new Error('Not Authorized');
}
}
- Comandi di gestione delle autorizzazioni nell'utente
Il contrario sarebbe includere le autorizzazioni nel TUser. Un TUser avrebbe un set di permessi. Quindi, nei gestori di comandi TLedger, recupererei l'utente e controllerei se ha il permesso di eseguire il comando. Ma questo mi richiederebbe di recuperare l'aggregazione TUser per ogni comando 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 altro dominio con servizio
Un'altra possibilità sarebbe quella di modellare completamente un altro dominio di autorizzazione. Questo dominio sarebbe interessato ai diritti di accesso, all'autorizzazione, ecc. Il sottodominio contabile dovrebbe quindi utilizzare un servizio per accedere a questo dominio di autorizzazione sotto forma di 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);
})
}
Quale sarebbe la decisione più "DDD / CQRS"?