DDD CQRS - Berechtigung pro Abfrage und Befehl


15

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.

  1. Berechtigungszuordnung Benutzer -> Ledger -> Befehl / Abfrage

  2. 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.

  1. 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');
    }
}
  1. 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);
    })

}
  1. 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?


1
Gute Frage - Ich habe versucht, ähnliche Probleme anzugehen, und keine der Literaturstellen scheint sich direkt damit zu befassen. Die zweite Hälfte Ihrer Frage hat mich ein wenig verwirrt. Es klang, als ob Sie sich gefragt hätten, wo Sie die Verwaltung von Berechtigungen (Hinzufügen oder Entfernen von Berechtigungen) platzieren sollen. Die gezeigten Beispiele beziehen sich jedoch auf das Hinzufügen einer Transaktion. In der zweiten Hälfte wird also eher gefragt, wie Berechtigungen abgefragt werden sollen. Können Sie diesen Teil bitte klarstellen?
Emragins

Jede Transaktion kann eine Ausführungsrichtlinie haben. Jeder Benutzer sollte zu einer oder mehreren Gruppen gehören. Jede Gruppe verfügt über ein Zugriffsprofil, das angibt, welche Transaktionen zulässig sind. Zur Laufzeit wird die Richtlinie vor dem Ausführen einer Transaktion mit den aggregierten Profilen für den ausführenden Benutzer verglichen. Das ist natürlich leichter gesagt als getan.
NoChance

Antworten:


5

Bei der ersten Frage habe ich mit etwas Ähnlichem zu kämpfen. Ich neige immer mehr zu einem dreiphasigen Genehmigungsschema:

1) Berechtigung auf Befehls- / Abfrageebene von "Hat dieser Benutzer jemals die Berechtigung, diesen Befehl auszuführen?" In einer MVC-App könnte dies wahrscheinlich auf Controller-Ebene erfolgen, aber ich wähle einen generischen Pre-Handler, der den Berechtigungsspeicher basierend auf dem aktuellen Benutzer und dem ausgeführten Befehl abfragt.

2) Die Autorisierung innerhalb des Anwendungsdienstes von "Hat dieser Benutzer" jemals * die Berechtigung, auf diese Entität zuzugreifen? "In meinem Fall handelt es sich wahrscheinlich um eine implizite Prüfung, die einfach durch Filter im Repository erfolgt. In meiner Domäne ist dies Im Grunde genommen eine TenantId mit etwas mehr Granularität der OrganizationId.

3) Die Autorisierung, die von vorübergehenden Eigenschaften Ihrer Entitäten abhängt (z. B. Status), würde innerhalb der Domäne erfolgen. (Bsp. "Nur bestimmte Personen können ein geschlossenes Hauptbuch ändern.") Ich entscheide mich dafür, das in die Domäne einzufügen, da es stark von der Domäne und der Geschäftslogik abhängt, und es ist mir nicht wirklich angenehm, das an anderen Stellen zu offenbaren.

Ich würde gerne die Reaktionen anderer auf diese Idee hören - reißen Sie sie in Stücke, wenn Sie wollen (geben Sie nur einige Alternativen an, wenn Sie dies tun :))


Ich denke, Sie haben einige gültige Punkte in Bezug auf verschiedene "Ebenen" der Autorisierung. Ein System, an dem ich arbeitete, hatte verschiedene Arten von Benutzern - registrierte Benutzer und Mitarbeiter. Die Berechtigungen für den Befehls- / Abfragehandler haben den Benutzertyp grundlegend überprüft. Wenn es Personal war, ging es immer durch. Wenn es sich um einen registrierten Benutzer handelte, war dies nur zulässig, wenn bestimmte Bedingungen erfüllt waren (z. B. Berechtigungen für ein Aggregat).
Magnus

0

Ich würde die Autorisierung als Teil Ihres Autorisierungs-BC implementieren, sie jedoch als Aktionsfilter in Ihrem Ledger-System bereitstellen. Auf diese Weise können sie logisch voneinander entkoppelt werden - Ihr Ledger-Code sollte den Autorisierungscode nicht aufrufen müssen -, aber Sie erhalten dennoch eine hochperformante In-Process-Autorisierung für jede eingehende Anforderung.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.