DDD CQRS - autorizzazione per query e per comando

10

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.

  1. Associazione dei permessi utente - > ledger - > comando / interrogazione

  2. 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.

  1. 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');
    }
}
  1. 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);
    })

}
  1. 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"?

    
posta Ludovic C 02.11.2015 - 16:09
fonte

2 risposte

4

Per la prima domanda ho lottato con qualcosa di simile. Sempre più mi sto appoggiando a uno schema di autorizzazione in tre fasi:

1) Autorizzazione a livello di comando / query di "questo utente mai ha il permesso di eseguire questo comando?" In un'app MVC questo potrebbe probabilmente essere gestito a livello di controller, ma sto optando per un pre-gestore generico che eseguirà una query sull'archivio delle autorizzazioni in base all'utente corrente e al comando di esecuzione.

2) L'autorizzazione all'interno del servizio di applicazione di "questo utente" ha * mai il permesso di accedere a questa entità? "Nel mio caso questo probabilmente finirà per essere un controllo implicito semplicemente per mezzo di filtri nel repository - nel mio dominio questo è fondamentalmente un TenantId con un po 'più granularità di OrganizationId.

3) L'autorizzazione che si basa su proprietà transitorie delle tue entità (come Status) verrebbe gestita all'interno del dominio. (Es. "Solo alcune persone possono modificare un libro mastro chiuso.") Sto optando per metterlo all'interno del dominio perché fa molto affidamento sul dominio e sulla logica di business e non mi sento molto a mio agio esponendolo in altri luoghi.

Mi piacerebbe sentire le risposte degli altri a questa idea - strappala a brandelli se vuoi (offri solo alcune alternative se lo fai :))

    
risposta data 06.11.2015 - 20:01
fonte
0

Implementerei l'autorizzazione come parte della tua autorizzazione BC, ma la implementerò come filtro di azione nel tuo sistema di contabilità generale. In questo modo, possono essere disaccoppiati logicamente gli uni dagli altri - il tuo codice Ledger non dovrebbe dover chiamare il codice di autorizzazione - ma ottieni comunque un'autorizzazione in-process di elevate prestazioni per ogni richiesta in arrivo.

    
risposta data 30.11.2016 - 20:28
fonte

Leggi altre domande sui tag