This is an old revision of the document!
Ich4) bin an der Erstellung von Konten interessiert, will 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”5) plus die Bezeichnung des Kontos, z.B. “Outlook-Schnittstellenkonto”, für Personenkonten “Benutzerkonto” samt der Personenbezeichnung. Das ist schon nicht ganz so einfach, weil die Kontoart in die Sprache des Anwenders übersetzt werden sollte, und weil UserAccount nur die PersonID hält, nicht aber die Personenbezeichnung kennt. Und was, wenn neue Kontenarten dazu kommen?
Wir haben somit identifiziert, welche Gemeinsamkeiten die unterschiedlichen Kontoarten besitzen sollen. Die Bezeichnung der Kontoart plus die Bezeichnung des Kontoinhabers. Welche 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 ändern, entweder weil sie korrigiert wurde, oder weil die Person ihren Namen geändert hat6). Muss ich also wissen, dass ich auch auf PersonNameChanged Ereignisse lauschen muss? Das ginge ja noch, aber bei neuen Konten wäre das nicht mehr möglich.
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
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 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.
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 geben. UserAccount würde entweder eine Projektion von der Person fragen, oder die Person direkt befragen, wenn sie im selben Prozess laufen8):
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(); } ... }
IPersonQuery kann ein Repository<Person> dahinter liegen haben, eine Abfrage, etc.
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.