User Tools

Site Tools


patterns:inheritancerolestragegyoverview:inheritance

Vererbung - Implementierung und Konsequenzen

Implementierung

Authentication BC

  • UserAccount und SystemAccount werden beide von Account abgeleitet,
  • In Account gibt es einen abstrakten Konstruktor der in den Kindklassen implementiert werden muss. Das Systemkonto wird z.B. eine Beschreibung erhalten1), und das Personenkonto eine PersonID,
  • Es gibt das abstrakte Ereignis AccountCreated, welches nur die ID des Kontos beinhaltet. UserAccount wird im Konstruktor das UserAccountCreated:AccountCreated Ereignis melden, welches neben der ID des Kontos die PersonID beinhalten wird. Entsprechendes gilt für SystemAccount oder neue Kontenarten,
  • Auf der Befehlsseite wird es den abstrakten Befehl CreateAccount geben. Für UserAccount wird es CreateUserAccount:CreateAccount geben, entsprechendes gilt für SystemAccount,
  • Im Command Handler wird Service Locator verwendet, um die korrekte Factory für die entsprechenden Implementierungen von CreateAccount zu ermitteln

Authentication Info BC

Der Authentication BC ist dafür da, die Domäne der Authentifizierung abzubilden. Es gibt dazu fast immer noch den Aspekt der Darstellung. Gemeinsame Eigenschaften des Kontos, die mit der Geschäftslogik nichts zu tun haben, und nicht vom Anwender eingegeben werden, sondern ermittelt werden müssen2) erhalten am besten einen eigenen Kontext. Es ist sinnvoll die Eigenschaften explizit zu machen, denn wenn sie implizit bleiben, sind wird schnell wieder bei der dritten Normalform3), etwas wovon wir mit DDD/CQRS/ES weg wollten4).

Es wurde also identifiziert, dass die Bezeichnung des Subjektes des Kontos im Authentication Info BC wichtig ist. Für ein Systemkonto möchte man gerne sehen, was das Konto repräsentiert, z.B. “Konto für die Outlook-Schnittstelle”. Für ein Benutzerkonto möchte man gerne sehen, wen das Konto repräsentiert, z.B. “Max Mustermann”.

Umsetzung

Es gibt das Aggregate AccountInfo im Authentication Info BC. Es gibt den Befehl SetAccountInfo { ID, SubjectDescription }. Es gibt das Ereignis AccountInfoSet { ID, SubjectDescription }.

Für jede Kontoart, die von Account abgeleitet wird, sollte ein Handler gebaut werden, der die AccountInfo aktuell hält. Beim SystemAccount ist das einfach. Ein handler lauscht auf SystemAccountAdded und SystemAccountRenamed und veröffentlicht einfach SetAccountInfo mit der ID des Kontos und der Beschreibung des Systemkontos.


Hinweis: Denormalisierer, die Informationen aus Account und AccountInfo kombinieren, müssen darauf achten, dass sie SystemAccountAdded und AccountInfoSet nicht zwingend in der erwarteten Reihenfolge erhalten. AccountInfoSet kann durchaus vor SystemAccountAdded behandelt werden. In dem Fall kann man entweder warten, bis SystemAccountAdded erhalten wurde, und erst dann das Konto denormalisieren, oder man schreibt schon mal einen Datensatz mit der Konto-ID und der Bezeichnung, alles andere bleibt leer. Die DTOs müssen nur darauf vorbereitet sein, leere Felder zu haben, oder aber die Abfragen liefern nur Konten mit allen Feldern, so dass die DTOs sicher sein können, dass sie immer vollständig gefüllt sind.


Wie fülle ich die Personeninformation beim Benutzerkonto? Das ist etwas schwieriger. Es muss einen Handler für Ereignisse aus dem Personenkontext und dem Authentifizierungskontext geben, und dieser Handler muss Daten cachen5), weil er nicht weiß, in welcher Reihenfolge die Ereignisse kommen. Der Handler muss folgende beiden Tabellen vorhalten6)

PersonDescription
PersonID Description
3F2504E0-4F89-11D3-9A0C-0305E82C3301 Max Mustermann
UserAccountPersonMap
UserAccountID PersonID
64RT31G0-34UI-186E-Z896-RH84F8D7 3F2504E0-4F89-11D3-9A0C-0305E82C3301

Der Handler verarbeitet die drei Ereignisse UserAccountAdded, PersonAdded und PersonRenamed7). Die Workflows sind nun folgende8):

Behandlung von UserAccountAdded

Behandlung von PersonAdded/PersonRenamed

Besprechung

  • Die AccountInfoSet Befehle müssen zwingend erfolgreich sein. Dafür sollte man ein Systemkonto einrichten, das die entsprechenden Rechte hat, und keine Logik in dem AccountInfo Aggregate haben. Bei Fehlern wird der Handler von oben nicht weiter machen, und der Administrator sollte informiert werden, damit der Fehler behoben wird,
  • Wenn ein neues Konto dazu kommt, sollte es einen Handler mitbringen, der die AccountInfo füllt. Ansonsten wäre die Info im UI immer leer9). Man kann den Handler aber auch selbst dazu bauen, wenn man entsprechende Kenntnisse von den verwendeten BCs hat,
  • Das Open-Closed Prinzip wird eingehalten. Der Authentifizierungskontext ist geschlossen was Änderungen angeht, und offen für Erweiterungen. Durch den Authentication Info BC wird auch die Projektionsseite bedient,
  • Wenn die AccountInfoSet Befehle falsch erzeugt wurden, muss der fehlerhafte Handler korrigiert werden, und es wird kompensiert - in dem Fall werden einfach neue SetAccountInfo Befehle gesendet,
  • Wenn es zu einer Person viele Konten geben kann10), dann ist der Prozess Speicher- und Zeitintensiv. Für eine Änderung an der Personenbezeichnung müssten N Befehle gesendet werden, diese werden alle gespeichert und es werden N Domänenereignisse generiert. Zudem werden alle Personen und Konten im Handler gecacht. Es kann auch dauern, bis alle N Befehle durchgelaufen sind. Man muss abwägen, ob man in bestimmten Fällen die Info nicht direkt in die Haupt-Domäne schreibt, und Sichten auf aktuell verknüpfte Daten dann tatsächlich über Referenzen ermittelt. Wenn die Domäne aber erweiterbar ist, ist das mit den Referenzen nicht so einfach, und Optimierungen mit z.B. geteilten Datenbank Views würden bedeuten, dass man sich an anderer Stelle einschränkt. Es ist somit eine Fall-zu-Fall Entscheidung,
  • Man darf sich nicht darauf verlassen, dass man nach AccountAdded auch AccountInfoSet erhält. Das sind Ereignisse aus 2 Domänen, die durch die Applikationsschicht verknüpft werden,
  • AccountAdded11) sollte den Typ des Kontos beinhalten12). So kann dann jedes DTO den Typ beinhalten, z.B. PROM.Authentication.UserAccount. Anhand dieses Typs kann nun überall über Service Locator entsprechend mit dem Konto umgegangen werden. Das UI kann zum Bearbeiten den entsprechenden Controller laden, ebenso kann ein Übersetzer gefunden werden, der aus der Typenbezeichnung eine Bezeichnung in der Sprache des angemeldeten Users macht.

Doppelt gemoppelt? Gefahr widersprüchlicher Daten?

Ist das nicht alles doppelt gemoppelt? Ich habe doch die Personenbezeichnung im Personenkontext, wozu nochmal in den AccountInfo Kontext schreiben? Aus mehreren Gründen

  • Die Bounded Contexts sollen einigermaßen autark sein. Wenn ich was im Authentifizierungskontext mache, möchte ich dort alles haben, was ihn betrifft13).
  • Die Koppelung der Kontexte soll auf ein Minimum reduziert sein. Wenn die Referenzen nicht explizit aufgelöst werden, sondern implizit gehalten und jedesmal ermittelt werden müssen, sind die Systeme stark aneinander gebunden,
  • Man könnte eine Sicht inklusive der Bezeichnung des Subjektes14) auf alle Konten haben, die dann von Handlern wie oben aufgezeigt gefüllt werden. Man bräuchte dazu keinen zusätzlichen BC und kein AccountInfo Aggregate. In dem Fall würde die Projektion15) von allen weiteren Projektionen, die sich für die Bezeichnung interessieren, geteilt werden. Wenn es keine weiteren Projektionen gibt, wäre das ein einfacher und sinnvoller Weg. Wenn es aber mehrere Projektionen gibt, die den Bezeichner benötigen, und wenn man davon ausgehen kann, das weitere dazu kommen werden, ist eine Basisprojektion nicht mehr sinnvoll. Die anderen müssten Zugriff auf sie erhalten, sie ständig abfragen, und Änderungen an der Basisprojektion müssten über Projektionsereignisse-/Meldungen mitgeteilt werden. In dem Fall sollte man sich dazu entscheiden, das Ganze explizit zu machen und einen eigenen Kontext mit einer echten Domäne dafür zu bauen.

Was ist mit der single-source-of-truth? Die wahre Personenbezeichnung der Person eines Benutzerkontos steht doch in den Personenereignissen. Wenn wir ihn jetzt nochmal in die Ereignisse des Authentication Info BCs schreiben, haben wir zwei mögliche unterschiedliche Bezeichnungen für ein und dieselbe Person. Dazu folgendes:

  • wir haben nicht zwei unterschiedliche Bezeichnungen für eine Person. Wir haben die Bezeichnung der Person im Personenkontext, und wir haben eine Repräsentation des Kontoinhabers im Benutzerkonto. Wie diese Repräsentation aufgebaut sein soll entscheidet das Benutzerkonto, nicht der Personenkontext. Dort kann alles mögliche stehen, es bedeutet nicht, dass die Person eine falsche Bezeichnung hat, sondern dass die Sicht des Benutzerkontos auf die Person eine andere ist,
  • die Applikation stellt sicher, dass die Bezeichnung im Konto schließlich mit der Personenbezeichnung übereinstimmt. Das ist die Eventual Consistency, von der gesprochen wird. Das gilt natürlich nur, wenn die Systeme fehlerfrei laufen, aber auch Fehlerbehebung ist ein Teil von Eventual Consistency.

Was ist mit Event Enrichment. Man könnte die Ereignisse bereichern16) anstatt eine neue Domäne zu bauen mit eigenen Ereignissen. Ich finde eine zweite Domäne wesentlich einfacher als Ereignisse zu bereichern. Das Abfragen der bereicherten/dekorierten Ereignisse nach ihrem Typ und den internen Eigenschaften der Basisklassen ist komplexer als eigene, zusätzliche Ereignisse. Zudem ist eine nachträgliche Bereicherung bereits vorhandener Ereignisse unmöglich. Info-Kontexte nachträglich dazu zu bauen und mit historischen Ereignissen zu befüllen ist aber möglich - und sehr einfach.

1) z.B. “Outlook-Schnittstellenkonto”
2) eventuell aus anderen BCs
3) 3NF
4) Man müsste Eigenschaften referenzieren, eventuell sogar aus anderen BCs
5) in einer Datenbank
6) OK, wir sind hier bei 3NF. An irgend einer Stelle muss ja die Beziehung vom Benutzerkonto zur Person gehalten werden. Getrieben wird das aber von Ereignissen
7) wenn der Person BC mit Event Sourcing arbeitet. Wenn nicht, läuft das ähnlich, nur dass der Handler regelmäßig die Personenbezeichungen abfragt
8) dran denken, der Handler serialisiert die Ereignisse, weil er ein singleton ist
9) oder der Datensatz wird dort nicht angezeigt
10) oder zu einem Produkt viele Bestellungen
11) und alle erweiterbaren Typen
12) das sollte im Grunde automatisch passieren - wird in das Framework eingebaut
13) eventuell gemeinsam mit den diversen Info-Kontexten…
14) was oder wen das Konto repräsentiert
15) Sicht
16) z.B. mit dem Decorator Pattern
patterns/inheritancerolestragegyoverview/inheritance.txt · Last modified: 2013/01/08 16:29 by rtavassoli