streszczenie
Czy autoryzacja w CQRS / DDD powinna być realizowana na polecenie / zapytanie, czy nie?
Po raz pierwszy opracowuję aplikację online, wykorzystującą mniej więcej ściśle wzorzec DDD CQRS. Wpadłem na jakiś problem, którego tak naprawdę nie mogę rozwiązać.
Aplikacja, którą tworzę, jest aplikacją do tworzenia ksiąg rachunkowych, umożliwiającą tworzenie ksiąg rachunkowych, a także umożliwiającą ich przeglądanie / edytowanie / usuwanie, np. Pracownikom. Twórca księgi powinien mieć możliwość edytowania praw dostępu do księgi, którą utworzył. Może nawet zmienić właściciela. Domena ma dwa agregaty TLedger i TUser .
Przeczytałem wiele postów ze słowem kluczowym DDD / CQRS dotyczących bezpieczeństwa, autoryzacji itp. Większość z nich stwierdziła, że autoryzacja jest poddomeną ogólną , chyba że budowana jest aplikacja zabezpieczająca.
W tym przypadku domeną podstawową jest z pewnością domena księgowa zainteresowana transakcjami, bilansowaniem i rachunkami. Wymagana jest także funkcjonalność umożliwiająca zarządzanie drobnym dostępem do ksiąg rachunkowych. Zastanawiam się, jak zaprojektować to w kategoriach DDD / CQRS.
W samouczkach DDD w całym miejscu stwierdzono, że polecenia są częścią wszechobecnego języka. Mają znaczenie. Są to konkretne działania, które reprezentują „rzeczywistość”.
Ponieważ wszystkie te polecenia i zapytania są faktycznymi działaniami, które użytkownicy wykonaliby w „prawdziwym życiu”, czy wdrożenie autoryzacji powinno być połączone ze wszystkimi tymi „poleceniami” i „zapytaniami”? Użytkownik miałby uprawnienia do wykonania TLedger.addTransaction (), ale nie na przykład TLedger.removeTransaction (). Lub użytkownik może wykonać zapytanie „getSummaries ()”, ale nie „getTransactions ()”.
Trójwymiarowe mapowanie istniałoby w formie polecenia-księgi użytkownika lub zapytania księgi użytkownika w celu ustalenia praw dostępu.
Lub w odsprzężony sposób nazwane „uprawnienia” zostaną zarejestrowane dla użytkownika. Uprawnienia, które byłyby następnie mapowane dla określonych poleceń. Na przykład pozwolenie „ManageTransactions” pozwoliłoby użytkownikowi na wykonanie polecenia „AddTransaction ()”, „RemoveTransaction ()” itp.
Użytkownik mapujący uprawnienia -> księga główna -> polecenie / zapytanie
Użytkownik mapujący uprawnienia -> księga główna -> pozwolenie -> polecenie / zapytanie
To pierwsza część pytania. Czy w skrócie, czy autoryzacja w CQRS / DDD powinna być realizowana dla polecenia lub zapytania? Czy też należy oddzielić autoryzację od poleceń?
Po drugie, dotyczy autoryzacji na podstawie uprawnień. Użytkownik powinien mieć możliwość zarządzania uprawnieniami do swoich ksiąg rachunkowych lub ksiąg, którymi może zarządzać.
- Komendy zarządzania autoryzacją występują w księdze głównej
Myślałem o dodaniu zdarzeń / poleceń / procedur obsługi do agregacji Księgi głównej, takich jak grantPermission (), revokePermission () itp. W takim przypadku wymuszenie tych reguł miałoby miejsce w procedurach obsługi komend. Wymagałoby to jednak, aby wszystkie polecenia zawierały identyfikator użytkownika, który je wydał. Następnie sprawdziłbym TLedger, czy istnieje uprawnienie dla tego użytkownika do wykonania tego polecenia.
Na przykład :
class TLedger{
function addTransactionCmdHandler(cmd){
if (!this.permissions.exist(user, 'addTransaction')
throw new Error('Not Authorized');
}
}
- Komendy zarządzania autoryzacją w Użytkowniku
Odwrotnym rozwiązaniem byłoby włączenie uprawnień do TUser. TUser miałby zestaw uprawnień. Następnie w modułach obsługi poleceń TLedger pobierałbym użytkownika i sprawdzał, czy ma on uprawnienia do wykonania polecenia. Wymagałoby to jednak pobrania agregatu TUser dla każdego polecenia 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);
})
}
- Kolejna domena z usługą
Inną możliwością byłoby całkowite modelowanie innej domeny autoryzacji. Ta domena byłaby zainteresowana prawami dostępu, autoryzacją itp. Subdomena księgowości użyłaby następnie usługi, aby uzyskać dostęp do tej domeny autoryzacji w formie 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);
})
}
Jaka decyzja byłaby najbardziej „DDD / CQRS”?