Resumen
¿Debería implementarse la autorización en CQRS / DDD por comando / consulta o no?
Estoy desarrollando por primera vez una aplicación en línea utilizando más o menos estrictamente el patrón DDD CQRS. Me topé con algún problema, que realmente no puedo entender.
La aplicación que estoy creando es una aplicación de libro mayor que permite a las personas crear libros mayores, así como también que otras personas puedan verlos, editarlos o eliminarlos, como los empleados. El creador de un libro mayor debería poder editar los derechos de acceso del libro mayor que creó. Incluso podría cambiar la propiedad. El dominio tiene dos agregados TLedger y TUser .
Leí muchas publicaciones con la palabra clave DDD / CQRS sobre seguridad, autorización, etc. La mayoría de ellas declararon que la autorización era un Subdominio genérico , a menos que uno estuviera creando una aplicación de seguridad.
En este caso, el dominio central es ciertamente un dominio contable interesado en transacciones, saldos y cuentas. Pero también se requiere la funcionalidad de poder administrar el acceso de grano fino a los libros de contabilidad. Me pregunto cómo diseñar esto en términos DDD / CQRS.
En los tutoriales de DDD se afirma que los comandos son parte del lenguaje omnipresente. Son significativos. Son acciones concretas que representan la "cosa real".
Debido a que todos esos comandos y consultas son acciones reales que los usuarios ejecutarían en la "vida real", ¿la implementación de la autorización debería estar asociada con todos estos "comandos" y "consultas"? Un usuario tendría autorización para ejecutar TLedger.addTransaction () pero no TLedger.removeTransaction (), por ejemplo. O, un usuario podría ejecutar la consulta "getSummaries ()" pero no "getTransactions ()".
Existiría un mapeo tridimensional en forma de user-ledger-command o user-ledger-query para determinar los derechos de acceso.
O, de forma desacoplada, los "permisos" denominados se registrarían para un usuario. Permisos que luego se asignarían para comandos específicos. Por ejemplo, el permiso "ManageTransactions" permitiría a un usuario ejecutar "AddTransaction ()", "RemoveTransaction ()", etc.
Usuario de mapeo de permisos -> libro mayor -> comando / consulta
Usuario de mapeo de permisos -> libro mayor -> permiso -> comando / consulta
Esa es la primera parte de la pregunta. O, en resumen, ¿se debe implementar la autorización en CQRS / DDD por comando o por consulta? O, ¿se debe desacoplar la autorización de los comandos?
En segundo lugar, en relación con la autorización basada en permisos. Un usuario debe poder administrar los permisos en sus Ledgers o en los Ledgers que se le ha permitido administrar.
- Los comandos de administración de autorización se producen en el Libro mayor
Pensé en agregar los eventos / comandos / controladores en el agregado de Ledger , como grantPermission (), revokePermission (), etc. En este caso, la aplicación de esas reglas sucedería en los controladores de comandos. Pero esto requeriría que todos los comandos incluyan la identificación del usuario que emitió ese comando. Luego, me registraría en el TLedger si existe el permiso para que ese usuario ejecute ese comando.
Por ejemplo :
class TLedger{
function addTransactionCmdHandler(cmd){
if (!this.permissions.exist(user, 'addTransaction')
throw new Error('Not Authorized');
}
}
- Comandos de gestión de autorizaciones en el Usuario
La otra forma sería incluir los permisos en el TUser. Un usuario de T tendría un conjunto de permisos. Luego, en los controladores de comandos de TLedger, recuperaría al usuario y comprobaría si tiene permiso para ejecutar el comando. Pero esto requeriría que busque el 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);
})
}
- Otro dominio con servicio
Otra posibilidad sería modelar completamente otro dominio de autorización. Este dominio estaría interesado en derechos de acceso, autorización, etc. El subdominio de contabilidad usaría un servicio para acceder a este dominio de autorización en 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);
})
}
¿Qué decisión sería la forma más "DDD / CQRS"?