This shows you the differences between two versions of the page.
technology:domainmodel:secondaryindex [2013/02/14 18:45] rtavassoli |
technology:domainmodel:secondaryindex [2013/02/18 18:23] (current) rtavassoli |
||
---|---|---|---|
Line 1: | Line 1: | ||
- | ====== Hinweis ====== | + | ====== Mengenindex - Set Index ====== |
- | Diese Besprechung scheint ziemlich sinnfrei zu sein und wird wahrscheinlich demnächst gelöscht. Das ist viel sauberer und einfacher über Domain Services zu implementieren. Die Domäne wird durch die Sekundärindezes lediglich verkompliziert ohne dass wirkliche Vorteile entstehen. | + | In DDD heißt es, es gäbe keine transaktional einzuhaltenden, Mengenbasierten Regeln, die mehrere Aggregate umfassen. Ich behaupte, dass die explizite Verwendung von Mengenindizes eine pragmatische Abweichung von der reinen Anwendung von DDD ist. Ein Mengenindex ist im Grunde eine IoC((Inversion of Control)) von Entitäten und Aggregates, indem aus Entitäten Aggregates werden, und die Aggregates auf den Mengenindex((das ursprüngliche Aggregate)) zeigen. Durch Mengenindizes kann man den komplizierten Einsatz von Domain Services, SAGAs, Reservation Patterns und Compensating Actions umgehen. Vor allem aber kann man dieselbe Funktionalität von größeren Aggregates haben, und die Aggregates trotzdem klein halten. Man sollte damit natürlich vorsichtig sein, wobei der übermäßige Einsatz auch nicht schadet, weil ein Mengenindex immer relativ einfach eben durch einen anderen Prozess ersetzt werden kann, der Eventual Consistency verwendet, bzw. sofortige Konsistenz mit dem Reservation Pattern. |
- | ====== Sekundärindex - Secondary Index ====== | + | |
- | In DDD heißt es, es gäbe keine secondary indices. Ich behaupte, dass die explizite Verwendung von Zweitindizes eine pragmatische Abweichung von der reinen Anwendung von DDD ist. Durch Zweitindizes kann man den komplizierten Einsatz von Domain Services, SAGAs, Reservation Patterns und Compensating Actions umgehen. Man sollte damit natürlich vorsichtig sein, wobei der übermäßige Einsatz auch nicht schadet, weil ein Zweitindex immer relativ einfach eben durch einen anderen Prozess ersetzt werden kann, der Eventual Consistency verwendet, bzw. sofortige Konsistenz mit dem Reservation Pattern. | + | |
\\ \\ | \\ \\ | ||
- | Wenn man sich relativ sicher sein kann, dass ein Aggregte immer gemeinsam mit dem Zweitindex in derselben Partition liegen wird, dann sollte der Einsatz eines Zweit- oder auch Drittindexes auch eine langfristige Lösung darstellen. Die Umsetzung ist relativ einfach - ein paar technische Voraussetzungen müssen eingebaut werden. Dann können Zweitindizes sogar so gebaut werden, dass sie sehr gut in DDD passen, denn dann sind sie nichts anderes als weitere Zweit- und Dritt-Aggregates, die die Ereignisse von anderen Aggregates verwenden. | + | Wenn man sich relativ sicher sein kann, dass ein Aggregte immer gemeinsam mit dem Mengenindex in derselben Partition liegen wird, dann sollte der Einsatz eines Mengenindexes auch eine langfristige Lösung darstellen. Die Umsetzung ist relativ einfach - ein paar technische Voraussetzungen müssen eingebaut werden. Dann können Mengenindizes sogar so gebaut werden, dass sie sehr gut in DDD passen, denn dann sind sie nichts anderes als weitere Zweit- und Dritt-Aggregates, die die Ereignisse von anderen Aggregates verwenden. |
+ | >To-Do: Sekundärindex noch in Mengenindex ändern - nur bis hier erledigt | ||
===== Beispiel Zeitdatensätze ===== | ===== Beispiel Zeitdatensätze ===== | ||
Angenommen die Regeln((Invariants)) sind folgende | Angenommen die Regeln((Invariants)) sind folgende | ||
Line 99: | Line 98: | ||
> | > | ||
> Ist das zu restriktiv? Mitnichten. Die Lösung als ein großes Kalender-Aggregate ist ja noch restriktiver - alles würde über ein einziges Aggregate laufen, und somit auch in einem EventStore liegen müssen. D.h. dass wir uns mit einem Sekundärindex nicht stärker einschränken als ohne. Im Gegenteil. Die Ereignisse müssen zwar weiterhin im selben Event Store gespeichert werden, aber die Aggregates werden kleiner und somit flexibler, und nur bestimmte Index-betreffende Ereignisse müssen Aggregate übergreifend behandelt werden. | > Ist das zu restriktiv? Mitnichten. Die Lösung als ein großes Kalender-Aggregate ist ja noch restriktiver - alles würde über ein einziges Aggregate laufen, und somit auch in einem EventStore liegen müssen. D.h. dass wir uns mit einem Sekundärindex nicht stärker einschränken als ohne. Im Gegenteil. Die Ereignisse müssen zwar weiterhin im selben Event Store gespeichert werden, aber die Aggregates werden kleiner und somit flexibler, und nur bestimmte Index-betreffende Ereignisse müssen Aggregate übergreifend behandelt werden. | ||
- | |||
===== Und zum Abschluss ===== | ===== Und zum Abschluss ===== | ||
Eine schließliche Konsistenz im verteilten Mitarbeiterkalender((also ohne Sekundärindex)) herzustellen ist nicht so ohne. Wie stelle ich sie her? E-Mail an Mitarbeiter, dass es eine Überschneidung gibt? Zeitdatensätze nach einer Neuterminierung automatisch in einen Status //wird geprüft// versetzen, und dann einen Event-Handler bauen, der die Prüfungen serialisiert, und ihn auf //geprüft// oder //Konflikt// setzt - nach der Regel, wer zuerst kam, wird zuerst auf //geprüft// gesetzt. | Eine schließliche Konsistenz im verteilten Mitarbeiterkalender((also ohne Sekundärindex)) herzustellen ist nicht so ohne. Wie stelle ich sie her? E-Mail an Mitarbeiter, dass es eine Überschneidung gibt? Zeitdatensätze nach einer Neuterminierung automatisch in einen Status //wird geprüft// versetzen, und dann einen Event-Handler bauen, der die Prüfungen serialisiert, und ihn auf //geprüft// oder //Konflikt// setzt - nach der Regel, wer zuerst kam, wird zuerst auf //geprüft// gesetzt. | ||
\\ \\ | \\ \\ | ||
Alternativ erlaubt man die Möglichkeit einer Überschneidung ohne speziellen Status, und die nachgelagerten Aktionen prüfen auf Überschneidungen. Z.B. würde das Drucken von Reports, das Erstellen von Monatsabschlüssen und das Abrechnen von Zeiten verhindert werden, wenn es Konflikte gibt. Dann gibt es einfach eine Meldung, und der Mitarbeiter muss den Konflikt lösen bevor die Zeiten weiter verarbeitet werden können. | Alternativ erlaubt man die Möglichkeit einer Überschneidung ohne speziellen Status, und die nachgelagerten Aktionen prüfen auf Überschneidungen. Z.B. würde das Drucken von Reports, das Erstellen von Monatsabschlüssen und das Abrechnen von Zeiten verhindert werden, wenn es Konflikte gibt. Dann gibt es einfach eine Meldung, und der Mitarbeiter muss den Konflikt lösen bevor die Zeiten weiter verarbeitet werden können. | ||
+ | ==== Und noch was ==== | ||
+ | Ein Sekundärindex, wie hier beschrieben, dient eigentlich nur dazu, ein Aggregate, das eines sein sollte, aufzubrechen in mehrere Aggregates. Das bedeutet, dass der Index im Grunde fest auf die //eigentliche Aggregate Id// geht, im Beispiel des Mitarbeiterkalenders auf die Mitarbeiter-Id. Das bedeutet auch, dass die Id, auf die sich der Index bezieht, unveränderlich((immutable)) sein muss, ansonsten hat das Ganze wenig Sinn. Die Aufteilung ist //künstlich//, weil die Regel den gesamten Kalender betrifft, aber ebenso //natürlich// und //echt//, weil man die Einträge einzeln betrachtet, und nicht den gesamten Kalender samt aller Einträge bemühen muss, wenn man in einem Eintrag nur den Kommentar ändert. | ||
+ | \\ \\ | ||
+ | Gegen eine Aufteilung des Aggregates in ein Kalender-Aggregate, das nur den Zeitraum des Termins betrifft, und ein ein Detail-Aggregate, das alle weiteren Angaben betrifft, spricht, dass diese Aufteilung unnatürlich ist. Ein Zeitdatensatz wird z.B. als Ganzes abgerechnet. Wenn der Zeitdatensatz einen Status hat, der eine Bearbeitung nicht mehr erlaubt, dann gilt das für den Datensatz im Kalender, und auch für die Details des Datensatzes. | ||
+ | \\ \\ | ||
+ | Ein Domain Service könnte natürlich eine Statusänderung immer auf beide Teile anwenden. Was, wenn es aber eine Abweichung gibt, und der Zeitdatensatz den Status //erstellt// hat, der terminierte Datensatz im Kalender den Status //abgelehnt//? Was anzeigen, welche Statusänderungsrechte prüfen? | ||
+ | \\ \\ | ||
+ | Alternativ könnte jede Änderung im terminierten Teil, also im Mitarbeiterkalender, über einen Domain Service laufen. Der Domain Service würde den Eintrag im Kalender verschieben, und im Zeitdatensatz selbst nur den Befehl AllowRescheduling(von-bis) ausführen. Der Zeitdatensatz müsste dann das Ereignis ReschedulingAllowed(id, von-bis) veröffentlichen, damit alle Prüfungen innerhalb des Domain Service Aufrufs über beide Aggregates geschützt sind. | ||
+ | \\ \\ | ||
+ | Die Lösung in diesem Fall laut //Blue Book// ist, auf eine transaktionale Konsistenz zu verzichten, und die Regel, dass es keine Zeitüberschneidung geben darf, nur schließlich konsistent einzuhalten. Oder man macht aus dem gesamten Kalender samt aller Zeitdatensätze und Details ein (potenziell) riesiges Aggregate. Ich suche Wege, das zu verhindern. Solch ein Sekundärindex ist ein Weg, der mir fast natürlicher erscheint als den Zeitdatensatz in zwei Aggregates aufzuteilen und einen Domain Service zu verwenden, der in diversen Fällen beide anfassen muss. Was ist ein Zeitdatensatz ohne Zeitangaben, und was ist ein terminierter Datensatz ohne dazu gehörige Details? | ||
+ | \\ \\ | ||
+ | Also halte ich an dem Sekundärindex fest, und ja, er soll auch im Aggregate gesetzt werden, und nicht versteckt im Event Handler, denn die Regel ist ja fester Bestandteil der Domäne, und nicht was rauf geflaschtes. |