Sommario
L'autorizzazione in CQRS / DDD dovrebbe essere implementata per comando / query o no?
Sto sviluppando per la prima volta un'applicazione online usando più o meno rigorosamente il modello DDD CQRS. Mi sono imbattuto in qualche problema, che non riesco davvero a capire.
L'applicazione che sto creando è un'applicazione di contabilità che consente alle persone di creare registri, oltre a consentire ad altre persone di visualizzarli / modificarli / eliminarli, 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 persino cambiare la proprietà. Il dominio ha due aggregati TLedger e TUser .
Ho letto molti post con la parola chiave DDD / CQRS riguardanti sicurezza, autorizzazione, ecc. La maggior parte di essi ha affermato che l'autorizzazione era un sottodominio generico , a meno che non si stesse creando un'applicazione di sicurezza.
In questo caso, il dominio principale è certamente un dominio contabile interessato alle transazioni, al bilanciamento e ai conti. Ma è anche richiesta la funzionalità di essere in grado di gestire un accesso fine ai registri. Mi chiedo come progettarlo in termini DDD / CQRS.
È spiegato nei tutorial DDD in tutto il luogo che i comandi fanno parte del linguaggio onnipresente. Sono significativi. Sono azioni concrete che rappresentano la "cosa reale".
Poiché tutti quei comandi e query sono azioni effettive che gli utenti eseguiranno nella "vita reale", l'implementazione dell'autorizzazione dovrebbe essere abbinata a tutti questi "comandi" e "query"? Un utente avrebbe l'autorizzazione per eseguire TLedger.addTransaction () ma non TLedger.removeTransaction () per esempio. In alternativa, un utente potrebbe eseguire la query "getSummaries ()" ma non "getTransactions ()".
Una mappatura tridimensionale esisterebbe sotto forma di comando-registro-utente o query di registro-utente per determinare i diritti di accesso.
Oppure, in modo disaccoppiato, i nomi di "autorizzazioni" verrebbero registrati per un utente. Autorizzazioni che verrebbero quindi mappate per comandi specifici. Ad esempio, l'autorizzazione "ManageTransactions" consentirebbe a un utente di eseguire "AddTransaction ()", "RemoveTransaction ()", ecc.
Autorizzazioni mapping utente -> libro mastro -> comando / query
Autorizzazioni mapping utente -> libro mastro -> permesso -> comando / query
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, per quanto riguarda l'autorizzazione basata su autorizzazioni. Un utente dovrebbe essere in grado di gestire le autorizzazioni sui suoi registri o sui registri che gli è stato permesso di 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 sarebbe avvenuta nei gestori di comandi. Ma ciò richiederebbe che tutti i comandi includano l'id dell'utente che ha emesso quel comando. Quindi verificherei nel TLedger se esiste l'autorizzazione per quell'utente per eseguire quel comando.
Per esempio :
class TLedger{
function addTransactionCmdHandler(cmd){
if (!this.permissions.exist(user, 'addTransaction')
throw new Error('Not Authorized');
}
}
- Comandi di gestione autorizzazioni nell'Utente
Il contrario sarebbe quello di includere le autorizzazioni nel TUser. Un TUser avrebbe una serie di autorizzazioni. Quindi, nei gestori dei comandi TLedger, vorrei recuperare l'utente e verificare se dispone dell'autorizzazione per eseguire il comando. Ma questo mi richiederebbe di recuperare l'aggregato 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 utilizza quindi 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 decisione sarebbe il modo più "DDD / CQRS"?