Sumário
A autorização no CQRS / DDD deve ser implementada por comando / consulta ou não?
Estou desenvolvendo pela primeira vez um aplicativo online usando mais ou menos estritamente o padrão DDD CQRS. Eu me deparei com um problema que eu realmente não consigo entender.
O aplicativo que estou criando é um aplicativo de contabilidade que permite que as pessoas criem livros de contabilidade, além de permitir que outras pessoas os visualizem / editem / excluam, como funcionários. O criador de um razão deve poder editar os direitos de acesso do razão que ele criou. Pode até mudar de propriedade. O domínio possui dois agregados TLedger e TUser .
Li muitas postagens com a palavra-chave DDD / CQRS sobre segurança, autorização etc. A maioria delas afirmou que a autorização era um subdomínio genérico , a menos que alguém estivesse criando um aplicativo de segurança.
Nesse caso, o domínio principal é certamente um domínio contábil interessado em transações, balanços e contas. Mas a funcionalidade de poder gerenciar o acesso granular aos livros contábeis também é necessária. Eu estou querendo saber como projetar isso em termos de DDD / CQRS.
É declarado nos tutoriais do DDD em todo o lugar que os comandos fazem parte da linguagem onipresente. Eles são significativos. São ações concretas que representam a "coisa real".
Como todos esses comandos e consultas são ações reais que os usuários executariam na "vida real", a implementação da autorização deve ser associada a todos esses "comandos" e "consultas"? Um usuário teria autorização para executar TLedger.addTransaction (), mas não TLedger.removeTransaction (), por exemplo. Ou, um usuário poderia executar a consulta "getSummaries ()", mas não "getTransactions ()".
Um mapeamento tridimensional existiria na forma de comando de razão do usuário ou consulta de razão do usuário para determinar os direitos de acesso.
Ou, de maneira dissociada, o nome "permissões" seria registrado para um usuário. Permissões que seriam mapeadas para comandos específicos. Por exemplo, a permissão "ManageTransactions" permitiria ao usuário executar "AddTransaction ()", "RemoveTransaction ()", etc.
Usuário de mapeamento de permissões -> razão -> comando / consulta
Usuário de mapeamento de permissões -> razão -> permissão -> comando / consulta
Essa é a primeira parte da pergunta. Ou, resumidamente, a autorização no CQRS / DDD deve ser implementada por comando ou por consulta? Ou, a autorização deve ser dissociada dos comandos?
Em segundo lugar, referente à autorização baseada em permissões. Um usuário deve poder gerenciar as permissões em seus livros ou nos livros que ele tem permissão para gerenciar.
- Os comandos de gerenciamento de autorização acontecem no Ledger
Pensei em adicionar os eventos / comandos / manipuladores ao agregado Ledger , como grantPermission (), revokePermission (), etc. Nesse caso, a aplicação dessas regras aconteceria nos manipuladores de comando. Mas isso exigiria que todos os comandos incluíssem o ID do usuário que emitiu esse comando. Depois, verificaria no TLedger se existe permissão para esse usuário executar esse comando.
Por exemplo :
class TLedger{
function addTransactionCmdHandler(cmd){
if (!this.permissions.exist(user, 'addTransaction')
throw new Error('Not Authorized');
}
}
- Comandos de gerenciamento de autorização no Usuário
O contrário seria incluir as permissões no TUser. Um TUser teria um conjunto de permissões. Em seguida, nos manipuladores de comando do TLedger, eu recuperaria o usuário e verificaria se ele tem permissão para executar o comando. Mas isso exigiria que eu buscasse o agregado TUser para cada 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);
})
}
- Outro domínio com serviço
Outra possibilidade seria modelar outro domínio de autorização completamente. Este domínio estaria interessado em direitos de acesso, autorização etc. O subdomínio contábil usaria um serviço para acessar esse domínio de autorização na forma 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);
})
}
Qual decisão seria a maneira mais "DDD / CQRS"?