Zusammenfassung
Sollte die Autorisierung in CQRS / DDD per Befehl / Abfrage implementiert werden oder nicht?
Ich entwickle zum ersten Mal eine Online-Bewerbung unter mehr oder weniger strikter Verwendung des DDD-CQRS-Musters. Ich bin auf ein Problem gestoßen, mit dem ich mich nicht wirklich auskennen kann.
Die Anwendung, die ich erstelle, ist eine Hauptbuchanwendung, die es Benutzern ermöglicht, Hauptbücher zu erstellen und anderen Benutzern zu ermöglichen, diese anzuzeigen, zu bearbeiten oder zu löschen, z. B. Mitarbeitern. Der Ersteller eines Ledgers sollte in der Lage sein, die Zugriffsrechte des von ihm erstellten Ledgers zu bearbeiten. Könnte sogar den Besitzer wechseln. Die Domain hat zwei Aggregate TLedger und TUser .
Ich habe viele Posts mit dem DDD / CQRS-Schlüsselwort zu Sicherheit, Autorisierung usw. gelesen. Die meisten gaben an, dass Autorisierung eine generische Subdomäne ist , es sei denn, man erstellt eine Sicherheitsanwendung.
In diesem Fall ist die Kerndomäne sicherlich eine Buchhaltungsdomäne, die sich für Transaktionen, Bilanzierung und Konten interessiert. Es ist jedoch auch die Funktionalität erforderlich, den differenzierten Zugriff auf die Hauptbücher verwalten zu können. Ich frage mich, wie ich dies in DDD / CQRS-Begriffen gestalten soll.
In den DDD-Tutorials heißt es überall, dass die Befehle Teil der allgegenwärtigen Sprache sind. Sie sind sinnvoll. Es sind konkrete Aktionen, die die "reale Sache" darstellen.
Da all diese Befehle und Abfragen tatsächliche Aktionen sind, die Benutzer im "echten Leben" ausführen würden, sollte die Implementierung der Berechtigung mit all diesen "Befehlen" und "Abfragen" gekoppelt werden? Ein Benutzer hätte beispielsweise die Berechtigung, TLedger.addTransaction () auszuführen, nicht jedoch TLedger.removeTransaction (). Oder ein Benutzer darf die Abfrage "getSummaries ()" ausführen, nicht jedoch "getTransactions ()".
Eine dreidimensionale Abbildung würde in Form eines Benutzer-Hauptbuch-Befehls oder einer Benutzer-Hauptbuch-Abfrage existieren, um die Zugriffsrechte zu bestimmen.
Oder in entkoppelter Weise werden benannte "Berechtigungen" für einen Benutzer registriert. Berechtigungen, die dann für bestimmte Befehle zugeordnet würden. Mit der Berechtigung "ManageTransactions" kann ein Benutzer beispielsweise "AddTransaction ()", "RemoveTransaction ()" usw. ausführen.
Berechtigungszuordnung Benutzer -> Ledger -> Befehl / Abfrage
Berechtigungszuordnung Benutzer -> Hauptbuch -> Berechtigung -> Befehl / Abfrage
Das ist der erste Teil der Frage. Oder kurz gesagt, sollte die Autorisierung in CQRS / DDD pro Befehl oder pro Abfrage implementiert werden? Oder sollte die Autorisierung von den Befehlen entkoppelt werden?
Zweitens in Bezug auf die Autorisierung basierend auf Berechtigungen. Ein Benutzer sollte in der Lage sein, die Berechtigungen für seine Hauptbücher oder für die Hauptbücher, die er verwalten durfte, zu verwalten.
- Befehle zur Berechtigungsverwaltung werden im Ledger ausgeführt
Ich dachte an das Hinzufügen der Ereignisse / Befehle / Handler zum Ledger- Aggregat, z. B. grantPermission (), revokePermission () usw. In diesem Fall würde die Durchsetzung dieser Regeln in den Befehlshandlern erfolgen. Dies würde jedoch erfordern, dass alle Befehle die ID des Benutzers enthalten, der diesen Befehl ausgegeben hat. Dann würde ich in den TLedger einchecken, wenn die Berechtigung für diesen Benutzer besteht, diesen Befehl auszuführen.
Zum Beispiel :
class TLedger{
function addTransactionCmdHandler(cmd){
if (!this.permissions.exist(user, 'addTransaction')
throw new Error('Not Authorized');
}
}
- Berechtigungsverwaltungsbefehle im Benutzer
Der andere Weg wäre, die Berechtigungen in den TUser aufzunehmen. Ein TUser hätte eine Reihe von Berechtigungen. Dann würde ich in den TLedger-Befehlshandlern den Benutzer abrufen und prüfen, ob er die Berechtigung hat, den Befehl auszuführen. Dafür müsste ich jedoch das TUser-Aggregat für jeden TLedger-Befehl abrufen.
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);
})
}
- Eine andere Domain mit Service
Eine andere Möglichkeit wäre, eine andere Berechtigungsdomäne vollständig zu modellieren. Diese Domain würde sich für Zugriffsrechte, Autorisierung usw. interessieren. Die Subdomain Accounting würde dann einen Dienst verwenden, um auf diese Autorisierungsdomain in Form von zuzugreifen 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);
})
}
Welche Entscheidung wäre die "DDD / CQRS" -Methode?