Резюме
Должна ли авторизация в CQRS / DDD реализовываться для каждой команды / запроса или нет?
Я впервые разрабатываю онлайн-приложение, использующее более или менее строго шаблон DDD CQRS. Я столкнулся с некоторой проблемой, которую я не могу понять.
Приложение, которое я создаю, представляет собой приложение главной книги, которое позволяет людям создавать книги, а также позволяет другим людям просматривать / редактировать / удалять их, например сотрудников. Создатель бухгалтерской книги должен иметь возможность редактировать права доступа к созданной бухгалтерской книге. Могли даже поменять владельца. Домен имеет две совокупности TLedger и TUser .
Я прочитал много постов с ключевым словом DDD / CQRS, касающихся безопасности, авторизации и т. Д. Большинство из них заявили, что авторизация была общим субдоменом , если только не создавалось приложение безопасности.
В этом случае основной домен - это, безусловно, учетная область, заинтересованная в транзакциях, балансировании и счетах. Но также требуется возможность управлять мелкозернистым доступом к бухгалтерским книгам. Мне интересно, как спроектировать это в терминах DDD / CQRS.
В учебниках DDD повсюду говорится, что команды являются частью вездесущего языка. Они значимы. Это конкретные действия, которые представляют «реальную вещь».
Поскольку все эти команды и запросы являются действительными действиями, которые пользователи будут выполнять в «реальной жизни», должна ли реализация авторизации сочетаться со всеми этими «командами» и «запросами»? Пользователь будет иметь полномочия для выполнения TLedger.addTransaction (), но не TLedger.removeTransaction (), например. Или пользователю будет разрешено выполнить запрос "getSummaries ()", но не "getTransactions ()".
Для определения прав доступа должно существовать трехмерное отображение в виде команды-пользовательской книги или запроса-главной книги.
Или, в отрыве, именованные «разрешения» будут зарегистрированы для пользователя. Разрешения, которые затем будут отображаться для конкретных команд. Например, разрешение «ManageTransactions» позволит пользователю выполнять «AddTransaction ()», «RemoveTransaction ()» и т. Д.
Разрешение сопоставления пользователя -> регистр -> команда / запрос
Разрешение сопоставления пользователя -> регистр -> разрешение -> команда / запрос
Это первая часть вопроса. Или вкратце, следует ли осуществлять авторизацию в CQRS / DDD для каждой команды или для запроса? Или авторизация должна быть отделена от команд?
Во-вторых, в отношении авторизации на основе разрешений. Пользователь должен иметь возможность управлять разрешениями в своих книгах или в тех книгах, которыми ему разрешено управлять.
- Команды управления авторизацией происходит в Главной книге
Я думал о добавлении событий / команд / обработчиков в агрегат Ledger , таких как grantPermission (), revokePermission () и т. Д. В этом случае применение этих правил будет происходить в обработчиках команд. Но для этого необходимо, чтобы все команды включали идентификатор пользователя, который выполнил эту команду. Затем я бы проверил в TLedger, существует ли разрешение для этого пользователя на выполнение этой команды.
Например :
class TLedger{
function addTransactionCmdHandler(cmd){
if (!this.permissions.exist(user, 'addTransaction')
throw new Error('Not Authorized');
}
}
- Команды управления авторизацией в Пользователе
Другой способ - включить разрешения в TUser. TUser будет иметь набор разрешений. Затем в обработчиках команд TLedger я получал пользователя и проверял, есть ли у него разрешение на выполнение команды. Но это потребовало бы от меня получения агрегата TUser для каждой команды 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);
})
}
- Еще один домен с сервисом
Другая возможность - полностью смоделировать другой домен авторизации. Этот домен будет интересоваться правами доступа, авторизацией и т. Д. Субдомен учета будет использовать службу для доступа к этому домену авторизации в форме 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);
})
}
Какое решение будет наиболее "DDD / CQRS"?