Підсумок
Чи має бути виконана авторизація в CQRS / DDD за командою / запитом чи ні?
Я вперше розробляю онлайн-додаток, використовуючи більш-менш строго схему DDD CQRS. Я зіткнувся з якоюсь проблемою, яку я не можу реально опустити.
Додаток, який я будую, - це головна програма, яка дозволяє людям створювати книги, а також дозволяє іншим людям їх переглядати / редагувати / видаляти, наприклад, працівникам. Творець книги повинен мати можливість редагувати права доступу до створеної ним книги. Може навіть змінити право власності. Домен має два агрегати TLedger і TUser .
Я читав багато публікацій із ключовим словом DDD / CQRS щодо безпеки, авторизації тощо. Більшість із них заявляли, що авторизація - це загальний субдомен , якщо тільки не створюється програма захисту.
У цьому випадку основний домен - це, безумовно, домен бухгалтерського обліку, зацікавлений у транзакціях, балансуванні та рахунках. Але також необхідна функціональність для управління дрібнозернистим доступом до книг. Мені цікаво, як спроектувати це в термінах DDD / CQRS.
У підручниках DDD по всьому місцю зазначено, що команди є частиною всюдисущої мови. Вони значущі. Вони конкретні дії, які представляють "реальну річ".
Оскільки всі ці команди та запити - це фактичні дії, які користувачі виконували б у реальному житті, чи має реалізація авторизації поєднуватися з усіма цими "командами" та "запитами"? Користувач мав би право на виконання TLedger.addTransaction (), але не TLedger.removeTransaction (), наприклад. Або користувачеві буде дозволено виконувати запит "getSummaries ()", але не "getTransaction ()".
Тривимірне відображення існувало б у формі користувальницької книги-команди або запиту користувача-книги для визначення прав доступу.
Або, нерозв'язаним способом, названі "дозволи" будуть зареєстровані для користувача. Дозволи, які потім будуть відображені для конкретних команд. Наприклад, дозвіл "ManageTransaction" дозволить користувачеві виконувати "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 мав би набір дозволів. Тоді в обробниках команд 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» способом?