In der OOP-Community scheint es weit verbreitete Übereinstimmung zu geben, dass der Klassenkonstruktor ein Objekt nicht teilweise oder sogar vollständig nicht initialisiert lassen sollte.
Was meine ich mit "Initialisierung"? Grob gesagt der atomare Prozess, der ein neu erstelltes Objekt in einen Zustand bringt, in dem alle seine Klasseninvarianten gelten. Es sollte das erste sein, was mit einem Objekt passiert (es sollte nur einmal pro Objekt ausgeführt werden), und es sollte nichts erlaubt sein, an ein nicht initialisiertes Objekt zu gelangen. (Daher der häufige Rat, die Objektinitialisierung direkt im Klassenkonstruktor durchzuführen. Aus dem gleichen Grund werden
Initialize
Methoden häufig verpönt, da diese die Atomizität aufbrechen und es ermöglichen, ein Objekt zu erreichen und zu verwenden, das noch nicht vorhanden ist in einem genau definierten Zustand.)
Problem: Wenn CQRS mit Event Sourcing (CQRS + ES) kombiniert wird, bei dem alle Statusänderungen eines Objekts in einer geordneten Reihe von Ereignissen (Ereignisstrom) erfasst werden, frage ich mich, wann ein Objekt tatsächlich einen vollständig initialisierten Status erreicht: Am Ende des Klassenkonstruktors oder nachdem das allererste Ereignis auf das Objekt angewendet wurde?
Hinweis: Ich verzichte auf die Verwendung des Begriffs "Aggregatwurzel". Wenn Sie es vorziehen, ersetzen Sie es, wenn Sie "Objekt" lesen.
Diskussionsbeispiel: Angenommen, jedes Objekt wird durch einen undurchsichtigen Id
Wert eindeutig identifiziert (denken Sie an die GUID). Ein Ereignisstrom, der die Statusänderungen dieses Objekts darstellt, kann im Ereignisspeicher durch denselben Id
Wert identifiziert werden: (Machen wir uns keine Sorgen über die richtige Ereignisreihenfolge.)
interface IEventStore
{
IEnumerable<IEvent> GetEventsOfObject(Id objectId);
}
Angenommen, es gibt zwei Objekttypen Customer
und ShoppingCart
. Konzentrieren wir uns auf ShoppingCart
: Beim Erstellen sind Einkaufswagen leer und müssen genau einem Kunden zugeordnet werden. Das letzte Bit ist eine Klasseninvariante: Ein ShoppingCart
Objekt, das nicht mit a verknüpft ist, Customer
befindet sich in einem ungültigen Zustand.
In der traditionellen OOP könnte man dies im Konstruktor modellieren:
partial class ShoppingCart
{
public Id Id { get; private set; }
public Customer Customer { get; private set; }
public ShoppingCart(Id id, Customer customer)
{
this.Id = id;
this.Customer = customer;
}
}
Ich bin jedoch ratlos, wie ich dies in CQRS + ES modellieren kann, ohne dass es zu einer verzögerten Initialisierung kommt. Da diese einfache Initialisierung effektiv eine Statusänderung ist, müsste sie nicht als Ereignis modelliert werden?:
partial class CreatedEmptyShoppingCart
{
public ShoppingCartId { get; private set; }
public CustomerId { get; private set; }
}
// Note: `ShoppingCartId` is not actually required, since that Id must be
// known in advance in order to fetch the event stream from the event store.
Dies müsste offensichtlich das allererste Ereignis im Ereignisstrom eines ShoppingCart
Objekts sein, und dieses Objekt würde erst initialisiert, wenn das Ereignis darauf angewendet würde.
Wenn also die Initialisierung Teil der "Wiedergabe" des Ereignisstroms wird (was ein sehr allgemeiner Prozess ist, der wahrscheinlich genauso funktionieren würde, sei es für ein Customer
Objekt oder ein ShoppingCart
Objekt oder einen anderen Objekttyp)…
- Sollte der Konstruktor parameterlos sein und nichts tun und die gesamte Arbeit einer
void Apply(CreatedEmptyShoppingCart)
Methode überlassen (die der verpönten sehr ähnlich istInitialize()
)? - Oder sollte der Konstruktor einen Ereignisstrom empfangen und wiedergeben (was die Initialisierung wieder atomar macht, aber bedeutet, dass der Konstruktor jeder Klasse dieselbe generische "Wiedergabe & Anwenden" -Logik enthält, dh unerwünschte Codeduplizierung)?
- Oder sollte es sein , sowohl ein traditioneller OOP - Konstruktor (wie oben dargestellt) , dass das Objekt korrekt initialisiert, und dann alle Ereignisse , aber die ersten sind
void Apply(…)
-ied darauf?
Ich erwarte keine Antwort auf eine voll funktionsfähige Demo-Implementierung. Ich würde schon sehr freuen, wenn jemand erklären könnte , wo meine Argumentation fehlerhaft ist, oder ob Objektinitialisierung wirklich ist ein „Schmerzpunkt“ in den meisten CQRS + ES - Implementierungen.
Initialize
Methode) besetzt hätten. Das führt mich zu der Frage, wie eine solche Fabrik von Ihnen aussehen könnte.