User Tools

Site Tools


patterns:inheritancerolestragegyoverview:inheritance

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

patterns:inheritancerolestragegyoverview:inheritance [2013/01/08 15:55]
rtavassoli [Authentication Info BC]
patterns:inheritancerolestragegyoverview:inheritance [2013/01/08 16:29] (current)
rtavassoli
Line 35: Line 35:
 === Besprechung === === 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,   * 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, dass die AccountInfo füllt. Ansonsten wäre die Info im UI immer leer((oder der Datensatz wird dort nicht angezeigt)). Man kann den Handler aber auch selbst dazu bauen, wenn man entsprechende Kenntnisse von den verwendeten BCs hat,+  * Wenn ein neues Konto dazu kommt, sollte es einen Handler mitbringen, der die AccountInfo füllt. Ansonsten wäre die Info im UI immer leer((oder der Datensatz wird dort nicht angezeigt)). 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,   * 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 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 kann((oder zu einem Produkt viele Bestellungen)), 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,   * Wenn es zu einer Person viele Konten geben kann((oder zu einem Produkt viele Bestellungen)), 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. +  * 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, 
- +  * AccountAdded((und alle erweiterbaren Typen)) sollte den Typ des Kontos beinhalten((das sollte im Grunde automatisch passieren - wird in das Framework eingebaut))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 werdenDas UI kann zum Bearbeiten den entsprechenden Controller ladenebenso kann ein Übersetzer gefunden werden, der aus der Typenbezeichnung eine Bezeichnung in der Sprache des angemeldeten Users macht
-==== Wie wäre es ohne explizites Vorhandensein der gemeinsamen Aspekte ==== +=== Doppelt gemoppelt? Gefahr widersprüchlicher Daten? === 
-Wen oder was ein Konto repräsentiert ist ein wichtiger Aspekt eines Kontos. Ein Systemkonto repräsentiert ein System, bzw. Dinge, die das Konto tun können soll, z.B. ein "Konto für die Outlook Schnittstelle"Ein Benutzerkonto repräsentiert eine Person, z.B. "Max Mustermann". Die Bezeichnung für das Systemkonto steht aber in SystemAccount, die der Person in der Person in einem anderen BC, repräsentiert lediglich durch die PersonID in UserAccount. Somit ist die Bezeichnung des //Subjektes// des Kontos nur implizit ermittelbar über das Systemkonto oder über das Benutzerkonto, das dann noch die Person dafür benötigt. +Ist das nicht alles doppelt gemoppelt? Ich habe doch die Personenbezeichnung im Personenkontextwozu nochmal in den AccountInfo Kontext schreiben? Aus mehreren Gründen 
-\\ \\ +  * Die Bounded Contexts sollen einigermaßen autark sein. Wenn ich was im Authentifizierungskontext machemöchte ich dort alles haben, was ihn betrifft((eventuell gemeinsam mit den diversen Info-Kontexten...)). 
-Was, wenn ein neuer Kontotyp dazu kommt? Man kann im Vorfeld nicht wissen, wie die Bezeichnung des Subjekts zu ermitteln ist. Man könnte sowas wie //Projektionsereignisse// definieren, wie z.B. AccountSubjectRenamed(Guid AccountId, string SubjectName), und von den unterschiedlichen Konto Implementierungen verlangendiese Ereignisse auszulösen. Was aber ist ein //Projektionsereignis//? Die Domänenereignisse sollen doch alles, was die Domäne betrifft, erfassen. Und was, wenn die Projektionen falsch sind und korrigiert werden müssen? Die Projektionsereignisse einfach neu erzeugen((damit ist gemeintdass ein Adapter aus einem PersonRenamed Ereignis AccountSubjectRenamed Ereignisse für alle Konten erzeugt und veröffentlicht, die zu dieser Person gehören))? Das wäre nicht richtig, weil dann historische *Ereignisse* verändert würden+  * Die Koppelung der Kontexte soll auf ein Minimum reduziert seinWenn die Referenzen nicht explizit aufgelöst werden, sondern implizit gehalten und jedesmal ermittelt werden müssensind die Systeme stark aneinander gebunden
-==== Gemeinsamkeiten explizit darstellen ==== +  * Man könnte eine Sicht inklusive der Bezeichnung des Subjektes((was oder wen das Konto repräsentiert)) auf alle Konten habendie 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 Projektion((Sicht)) von allen weiteren Projektionen, die sich für die Bezeichnung interessierengeteilt werdenWenn es keine weiteren Projektionen gibtwäre das ein einfacher und sinnvoller WegWenn es aber mehrere Projektionen gibt, die den Bezeichner benötigen, und wenn man davon ausgehen kanndas weitere dazu kommen werdenist 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 werdenIn dem Fall sollte man sich dazu entscheidendas Ganze explizit zu machen und einen eigenen Kontext mit einer echten Domäne dafür zu bauen. 
-Gemeinsame Aspekte müssen explizit gemacht werden. Das ist SpeicherintensiverDaten werden in der ersten Normalform((1NF)) doppelt gehalten, können zeitweise abweichen, werden aber eventuell schlüssig sein((Eventual Consistency)), uswDer Vorteil ist aber derdass das, was danach kommt((Zusammenarbeit mit anderen Objekten und Kontexten, und vor allem die Systemwartbarkeit)) wesentlich einfacher sein wirdWenn ich weiß, dass es einen Bezeichner des Subjektes im Konto gibt, egal ob es ein Systemkonto, ein Benutzerkonto oder sonst eine Kontoart ist, dann ist das Arbeiten mit dieser Bezeichnung direkt und einfach. Es vollendet auch den BC als solches, denn was ist ein Benutzerkonto mit einer PersonenId von 3F2504E0-4F89-11D3-9A0C-0305E82C3301, wem gehört es? Wenn im Konto steht, dass es "Max Mustermann gehört, der eine Person mit der Identität 3F2504E0-4F89-11D3-9A0C-0305E82C3301 ist", hat der Kontext alles, was er benötigt.  +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 schreibenhaben wir zwei mögliche unterschiedliche Bezeichnungen für ein und dieselbe Person. Dazu folgendes: 
- +  * wir haben nicht zwei unterschiedliche Bezeichnungen für eine PersonWir haben die Bezeichnung der Person im Personenkontextund wir haben eine //Repräsentation// des Kontoinhabers im BenutzerkontoWie diese Repräsentation aufgebaut sein soll entscheidet das Benutzerkontonicht der PersonenkontextDort kann alles mögliche stehenes bedeutet nichtdass die Person eine falsche Bezeichnung hatsondern dass die Sicht des Benutzerkontos auf die Person eine andere ist, 
- +  * die Applikation stellt sicherdass 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 laufenaber auch Fehlerbehebung ist ein Teil von Eventual Consistency
-  * Die //Projektionsseite// ist nun tatsächlich ein wenig komplexerProjektionen werden dadurch erzeugt, dass Ereignisse behandelt werden, und aus diesen Ereignissen Sichten erstellt werden, die abgefragt werden. Die Sichten werden dann in Data Transfer Objects((DTO)) gewandelt, die dann an andere Systeme((vorwiegend an das UI)) gesendet werden können. Die anderen Systeme müssen nun mit den DTOs arbeiten können. Die Komplikation liegt darindass jede neue Kontoart neue Ereignisse, DTOs und Anforderungen and die Sichten einführt. Das wird am besten hier besprochen: +Was ist mit //Event Enrichment//. Man könnte die Ereignisse bereichern((z.B. mit dem //Decorator Pattern//)) anstatt eine neue Domäne zu bauen mit eigenen EreignissenIch 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öglichInfo-Kontexte nachträglich dazu zu bauen und mit historischen Ereignissen zu befüllen ist aber möglich - und sehr einfach.
-===== Konsequenzen ===== +
-//Ich//((ein Event Handler)) bin an der Erstellung von Konten interessiertwill wissen, wann ein Konto erstellt wird. Ich abonniere somit das AccountCreated Ereignis. Da UserAccountCreated und SystemAccountCreated von AccountCreated abgeleitet sind, würden beide von mir behandelt werden. Wenn eine neue Kontoart mit einem neuen Erzeugungsereignis dazu kommt, wird mir das auch automatisch zum Behandeln übergeben. +
-\\ \\ +
-In diesem Fall sollte ich nicht fest auf die vorhandenen [User/Sytem]AccountCreated reagieren, sondern auf die Basisklasse AccountCreated. Ansonsten würde ich Ereignisse eines neu dazu gekommenen Kontos nicht behandeln, und die Systemerweiterbarkeit ist nicht gegeben. Angenommen, ich möchte eine Liste aller Konten darstellen, mit der Bezeichnung der Kontoart und der Kontobezeichnung. Für Systemkonten ist das "Systemkonto"((wenn die Liste in Deutsch dargestellt werden soll)) plus die Bezeichnung des Kontosz.B. "Outlook-Schnittstellenkonto"für Personenkonten "Benutzerkonto" samt der PersonenbezeichnungDas ist schon nicht ganz so einfachweil die Kontoart in die Sprache des Anwenders übersetzt werden sollte, und weil UserAccount nur die PersonID hältnicht aber die Personenbezeichnung kennt. Und was, wenn neue Kontenarten dazu kommen+
-\\ \\ +
-Wir haben somit identifiziertwelche Gemeinsamkeiten die unterschiedlichen Kontoarten besitzen sollen. Die Bezeichnung der Kontoart plus die Bezeichnung des KontoinhabersWelche Ereignisse führen nun dazu, dass sich die Bezeichnung des Kontoinhabers ändert? SystemAccountDescriptionChanged wäre eins. Was ist mit der Personenbezeichnung? Die Person eines UserAccounts ändert sich nie, aber die Bezeichnung kann sich ändernentweder weil sie korrigiert wurde, oder weil die Person ihren Namen geändert hat((z.Bdurch Heirat)). Muss ich also wissendass ich auch auf PersonNameChanged Ereignisse lauschen muss? Das ginge ja noch, aber bei neuen Konten wäre das nicht mehr möglich. +
-==== Push==== +
-//Zusammenfassende Ereignisse für Projektionen von abstrakten Klassen//. Als erstes muss identifiziert werden, welche Gemeinsamkeiten für abstrakte Klassen existieren. Dann werden Ereignisse eingeführt, die Änderungen an diesen Gemeinsamkeiten melden. Diese Ereignisse werden ausgelöst durch +
-  * andere Ereignisse. Z.B. wird ein handler von PersonNameChanged eingebaut, der den neuen Namen meldetDieser handler behandelt ebenfalls UserAccountCreatedum zu wissenzu welchem Benutzerkonto die Person gehört. Beim behandeln von PersonNameChanged wird also der neue Personenname gespeichertdas Konto((bzw. die Konten)) der Person werden ermittelt, und für jedes wird ein AccountDescriptionChanged(Guid AccountID, string Description) Ereignis ausgelöst. Keine Ahnung wie dieses Muster heißt, es ist aber einfach zu verstehen und genauso einfach zu implementieren+
-  * regelmäßige Abfragen. Wenn Namensänderungen z.B. nicht über Ereignisse gemeldet werdenwürde man die Personenbezeichnungen regelmäßig abfragen. Wenn sich eine ändert, wird wie im ersten Fall dafür ein AccountDescriptionChanged Ereignis ausgelöst. +
-Es können beliebig tiefe Ereignisketten gebildet werden. AccountDescriptionChanged könnte z.B. von einem weitern handler in ein weiteres Ereignis gewandelt werden, usw. +
-=== Das Push-Problem === +
-Das Problem bei solche einer Push Lösung ist die, dass man es nur //per Konvention// von der Implementierung einer weiteren Account Klasse verlangen kann. Das ist aber immer noch besser als die Notwendigkeit, vorhandene Klassen für einen neue Account Klasse ändern zu müssen. Es geht ja darum, ein System erweitern zu können ohne vorhandene Systeme zu (zer)stören. Und genau das wäre damit erreicht. Zudem kann man die Konvention per Pull ein wenig formalisieren+
-==== Pull ==== +
-Account hat eine abstrakte Methode //GetSubjectDescription()//. Die muss von der geerbten Kindklasse implementiert werden; wie, ist der Kindklasse überlassen. SystemAccount würde einfach den Wert des Feldes //Description// raus gebenUserAccount würde entweder eine Projektion von der Person fragen, oder die Person direkt befragen, wenn sie im selben Prozess laufen((In dem Beisiel ist UserAccount das Query-Objekt, was entweder gemeinsam mit dem Command-Objekt existiert, oder getrennt, wenn CQRS umgesetzt wird))+
-<code csharp> +
-public class UserAccount: Account +
-+
-    ... +
-    private IPersonQuery PersonQuery { get; set; } +
-    public Guid PersonId { get; private set; } +
-    ... +
-    public UserAccount(IPersonQuery personQuery) +
-    { +
-        PersonQuery = personQuery; +
-    } +
-    ... +
-    public string GetDescription() +
-    { +
-        return personQuery.Get(PersonId).GetDescription(); +
-    } +
-    ...    +
-+
-</code> +
-IPersonQuery kann ein Repository<Person> dahinter liegen haben, eine Abfrage, etc. +
-=== Das Pull-Problem === +
-Immer wenn die Kontobezeichnung benötigt wird, kann das Konto direkt befragt werden. Das Problem besteht darin, wann danach gefragt werden soll. Vor jeder Abfrage auf Daten, die die Kontobezeichnung benötigen? Wenn es 1.000 Konten gibt, und man eine Abfrage auf sie startet, müßten dann erst 1.000 Pullaktionen durchgeführt werden? Sinnvoller wäre ein regelmäßiges Abfragen, oder eine Push-Pull Kombilösung. +
-=== Push-Pull Kombilösung mit Event Enrichment - Beispiel UserAccount === +
-  - Es gibt einen Denormalisierer im Personen Kontext, der die Personenbezeichnung aktualisiert und dann eine PersonNameChanged(Guid id, string newName) //Nachricht//((kein Ereignis, und auch kein Befehl. Es ist lediglich eine ....AHHHHHHHHHHHHHHHHHHHH das sind ja gar keine Ereignisse. Und was wenn doch? AccountSubjectDescriptionChanged oder PersonNameChanged als Ereignisse einführen? Übel!!!   sendetDie Reihenfolge ist hierbei äußerst wichtig, +
-  - Es gibt einen Dienst im Personen Kontext, der nach der Personenbezeichnung für eine PersonID gefragt werden kann. Dieser Dienst bezieht sich auf die Denormalisierung aus 1. +
-  - Es gibt im UserAccount Kontext einen Handler, der auf PersonNameChanged Ereignisse lauscht, sowie auf UserAccountCreatedWenn UserAccountCreated behandelt wird, zieht sich der Handler die Personenbezeichnung aus dem Dienst aus Punkt 2((es kann sein, dass die Person in der Projektion noch gar nicht existiert, also muss auch ein null-Bezeichner möglihch sein)). Darüber wird dann ein AccountSubjectDescriptionChanged Ereignis  +
-  Es gibt im UserAccount Kontext einen Handler, der eine SAGA startet sobald ein UserAccount erzeugt wurde. Die Saga lauscht auf AccountSubjectDescriptionChanged und auf PersonNameChanged. Derselbe Handler lädt dann aus dem Abfragedienst aus Punkt 2. die Personenbezeichung und meldet UserAccount +
-  - Es gibt im UserAccount Kontext einen Denormalisierer, der auf UserAccountCreated Ereignisse lauscht, sowie auf die PersonNameChanged Ereignisse aus 1.  +
-  -  +
-Es muss **einen** Denormalisierer geben, der auf Ereignisse die Personenbezeichnung betreffend wartet, sowie auf Ereignisse, die ein UserAccount anlegen. Wird ein UserAccount angelegt, wird die PersonID gespeichert, damit der Denormalisierer weiß, dass die Bezeichnung dieser Person benötigt wird. Dann wird geguckt, ob die Personenbezeichnung bereits vorhanden ist. Wenn ja, wird gleich hinterher ein AccountSubjectRenamed mit der Bezeichnung hinterher geschickt.  +
-\\ \\ +
-Der Handler für UserAccountCreated kann jetzt  +
-==== Service Locator ==== +
-Die Kontoartbezeichnung sollte einen Default haben, z.B. in Deutsch. Jedes DTO für Account wird zudem den Typ beinhalten, also 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.+
patterns/inheritancerolestragegyoverview/inheritance.1357656934.txt.gz · Last modified: 2013/01/08 15:55 by rtavassoli