Gedanken über O/R Mapping und Domain-Driven Design – Teil 1

Warum das Entity Framework für Domain-Driven Design nicht geeignet ist

Ausgangssituation

In der aktuellen Ausgabe des Fachmagazins Visual Studio One ist der vierte Teil meiner Windows Azure Kolumne erschienen. Thematisch geht es um SQL Azure. Im Artikel geht es darum, dass Objektmodell der vorherigen Ausgabe an eine SQL Azure Datenbank anzubinden. Wie schon oft habe ich dabei auch das Thema O/R Mapper andiskutiert. Natürlich habe ich das Thema in den Kontext von Domain-Driven Design gesetzt. Ich habe angekündigt, in einem kritischen Blogbeitrag ausführlicher meine Sichtweise zum Einsatz des Entity Frameworks mit DDD-Ansätzen aufzuzeigen. Den Beitrag habe ich nun schon eine Weile in der Schublade, das heißt als Entwurf in WordPress J. Es ist an der Zeit, diesen nun zu vollenden und vorzustellen.

Häufig wird der Graben zwischen Objektmodell und der relationalen Datenbank als groß beschrieben, meistens eben auch als Problem. Schnell wird dann zu einem O/R Mapper gegriffen und damit sollte das Problem gelöst sein. Doch ist das wirklich so? Ich habe mich in den letzten Jahren in vielen verschiedenen Projekten mit der Thematik auseinandergesetzt und versucht, brauchbare und pragmatische Lösungen zu finden. In vielen Projekten ging es um die Erstellung von Unternehmenslösungen mit umfangreichen, komplexen Geschäftsprozessen. So habe ich vor ca. 5 Jahren begonnen, mich mit Domain-Driven Design zu beschäftigen und Wege der Anwendung dieser Prinzipien in Softwareprojekten im .NET Umfeld zu finden. …und dieser Weg war sehr steinig. Viele Erkenntnisse mussten hart erarbeitet werden. Domain-Driven Design ist inzwischen etwas zum Modewort geworden und wird manchmal leider fälschlich verwendet. Es existieren inzwischen auch sogenannte DDD Light Ansätze, die im Wesentlichen die technischen Aspekte von DDD verwenden.

In Projekten kommt sehr häufig die Frage auf: „Wie mappen wir unser Objektmodell auf die Datenbank?“ Schnell wird dann zu einem O/R Mapper gegriffen und Objekte aus den vorhandenen Datenbanktabellen erzeugt, im Glauben dann ein Domänenmodell zu besitzen. Nur ist das leider nicht der Fall. Entitäten im Sinne von DDD sind etwas anderes als Datenbank-Entitäten. Entitäten im DDD-Ansatz sind Domänenobjekte, die aus Verhalten und Struktur bestehen. Entitäten der Datenbank bestehen nur aus Struktur und natürlich Beziehungen zu anderen Objekten.

Die Mängel des Code First Ansatzes

Mit dem Entity Framework ist es seit einigen Versionen möglich, den Code-First Ansatz zu verfolgen. Dieser erlaubt es, Objekte mit Struktur und Verhalten zu modellieren und daraus Datenbanktabellen erzeugen zu lassen. Ich habe mich schon einige Male in Evaluationen mit diesem Vorgehen beschäftigt und kann mir nicht vorstellen, dass dieser Einsatz für echte Projekte und vor allem in Hinblick auf eine optimale Datenbankstruktur wirklich taugt. Das soll aber nicht Schwerpunkt dieser Ausgabe sein. Ich werde diese Diskussion zu einem späteren Zeitpunkt nochmals aufnehmen.

In dieser Ausgabe möchte ich eher auf die Probleme eingehen, die im Hinblick auf objektorientierte Prinzipien codeseitig entstehen.

Stellen wir uns vor, wir müssten das Domänenobjekt Ente mit verschiedenen Methoden (Verhalten) und Eigenschaften (Struktur) modellieren. Es muss möglich sein, sicher und konsistent neue Objekte zu erzeugen. Die Anforderung besteht darin, dass die Ente (Objektinstanz) eindeutig zu identifizieren ist (ID), einen Namen hat und durch eine Farbe charakterisiert ist. Die Ente soll fliegen können und jeder Flug soll gespeichert werden und über eine Liste der Flüge einsehbar sein. Dies bedeutet, es muss eine Möglichkeit bestehen, die Flüge zu sammeln und zu persistieren. Beim Erstellen einer neuen Objektinstanz soll eine Regel, nämlich die maximale Ausflugsstrecke in Kilometer festgelegt werden. Dieser Wert ist unveränderbar solange die Ente existiert, was bedeutet, dass der Wert nur bei der Erstellung (Anlegen) einer neuen Instanz benötigt wird, persistiert werden soll und dann nur intern in der Klasse selbst verwendet wird, um eine Validierung der Flugstrecke beim Aufruf der Methode „Fliegen“ vorzunehmen. Die Liste der Flüge darf im Übrigen nicht manipulierbar sein.

Daraus ergibt sich folgender Entwurf unter Einhaltung objektorientierter Prinzipien, Abbildung 1. Auch das in meinem Artikel zu Messaging und Entkoppelung erwähnte Geheimnisprinzip soll wieder Anwendung finden.


Abbildung 1 – Objektmodell für die Anforderungen

Die Klasse Ente sieht wie folgt aus, Abbildung 2. Der Konstruktor soll der sicheren Erzeugung neuer Objekte dienen und fungiert quasi als Factory. Die Wiederherstellung bereits persistierter Objekte soll dann das Entity Framework übernehmen. Für den Wert der maximal zulässigen Flugstrecke der Ente, verwendete ich ein readonly Feld, da der Wert nur interne Verwendung findet und nach der Erstellung nicht mehr verändert werden darf. Der Wert muss für Client Code nicht sichtbar sein. In Abbildung 3 ist die Klasse Flug zu sehen, die auch eine Factory-Methode (Konstruktor) nutzt, um konsistente Instanzen zu erzeugen.


Abbildung 2 Die Klasse Ente erfüllt die Anforderungen.


Abbildung 3 Die Klasse Flug.

Soweit so gut. Probieren wir nun den Code aus und schauen, ob sich alles wie gewünscht verhält. Dafür habe ich eine kleine Konsolenanwendung geschrieben, die eine Enteninstanz erstellt und die Ente zwei mal fliegen lässt und dann noch versucht, sie zu überfordern, Abbildung 4 und 5.


Abbildung 4 Aufrufe über ein kleines Programm


Abbildung 5 Die Ausgabe in der Konsole. Anwendung läuft und die Ente lässt sich nicht überfordern

Bis hierher haben wir objektorientiert gearbeitet und saubere Klassen entworfen. Wir haben uns auch nicht um Persistierung gekümmert, da wir dem Code-First Ansatz folgen. Diese Aufgabe übergeben wir nun dem Entity Framework und schauen uns nun an, was passiert. Dafür müssen wir nur eine Klasse erstellen, die DbContext implementiert, siehe Abbildung 6. Danach wird der DataContext in der Konsole verwendet, Abbildung 7.


Abbildung 6 Datenkontext hinzugefügt


Abbildung 7 Den Datenkontext nutzen und das Objekt speichern

Das Entity Framework erstellt die Datenbank und Tabellen im Hintergrund, sofern sie noch nicht vorhanden sind. Schauen wir uns das Ergebnis an, Abbildung 8. Es scheint alles zu funktionieren. Der Code sagt, er hätte eine Ente verspeichert. Prüfen wir das nun in der Datenbank.


Abbildung 8 Die Ausgabe verspricht Gutes

Werfen wir einen Blick in das SQL Server Management Studio. Cool, die Datenbank wurde erstellt. Aber: leider nur unvollständig. Das Feld maximaleFlugStrecke als auch die Flugauflistung fehlen, Abbildung 9.


Abbildung 9 Der Blick ins SQL Server Management Studio zeigt das nicht alle Datenfelder und die Referenz auf die Flüge nicht erstellt wurden

Grund dafür ist, dass das Entity Framework nicht in der Lage ist, Felder als Datenbankspalten zu speichern. Leider gibt es auch kein Attribut dafür. Damit kann ich notfalls leben und mache aus dem Feld maximaleFlugStrecke eine Eigenschaft, so dass die Spalte in der Datenbank erstellt wird.

Das zweite Problem finde ich gravierender. Das Entity Framework kann nicht mit einer Auflistung vom Typ IEnumerable<T> umgehen. Auflistungen müssen vom Typ Collection<T> , damit die Tabellenstruktur richtig erstellt wird. Hier wird aber ein grundlegendes Prinzip der Objektorientierung gebrochen. Aber schauen wir uns zunächst an, wie die Klasse Ente verändert werden muss, damit die Datenbank wie erwartet erstellt wird, Abbildung 10.


Abbildung 10 Notwendige Änderungen an der Klasse für Code First.

Damit das Entity Framework ein Objekt beim Zurücklesen aus der Datenbank wieder instanzieren kann, wird außerdem noch ein parameterloser Konstruktor benötigt. Der wurde in Abbildung 10 ergänzt. Das gefällt mir gar nicht! Denn: Das Erstellen inkonsistenter Objektinstanz ist nun auch grundsätzlich im Client Code möglich. Der Aufruf new Ente() ist nicht zu verhindern. Oje! Die Anforderung „Es muss möglich sein, neue Objekte sicher und konsistent zu erzeugen“ kann damit nicht mehr erfüllt werden.

[Korrektur, 9.8.2013]

Ein Kollege wies mich soeben darauf hin, dass man die für das EF erforderlichen parameterlosen Konstruktoren auch „private“ machen kann. Dann sieht das doch schon besser aus. 🙂 Die inkonsistente Erzeugung neuer Objekte ist damit nicht möglich. (Pluspunkt)

Die private List<Flug> und die öffentliche Auflistung IEnumerable<Flug> wurden entfernt und dafür eine öffentliche Collection<Flug> erstellt. MaximaleFlugStrecke ist nun eine öffentliche Eigenschaft. Die Erstellung der Tabellen in der Datenbank klappt damit reibungslos. Das Ergebnis ist wie erwartet, Abbildung 11.


Abbildung 11 Datenbank wurde wie erwartet erstellt

Das Problem

Welches Problem ergibt sich aber nun aus der geänderten Implementierung aus Abbildung 10?

Änderungen an Daten eines Objektes sollen nur über definierte Schnittstellen möglich sein (Geheimnisprinzip). Die Auflistung der getätigten Flüge darf laut Anforderung nicht manipulierbar sein. Um die Bedürfnisse eines Frameworks zu befriedigen, bin ich gezwungen objektorientierte Prinzipien zu brechen und kann Anforderungen nicht erfüllen. Die Liste der Flüge ist nun für Client-Code (siehe Abbildung 12) ohne weiteres manipulierbar. Ich kann nicht verhindern, dass ein Konsument der Klasse auf der Flüge-Collection Clear() aufruft und die Liste damit leert und speichert.


Abbildung 12 Es entstehen nun ungeahnte und ungewollte Möglichkeiten im Client Code

Öffentliche Auflistungen müssen aus meiner Sicht zwingend vom Typ IEnumerable<T> sein, damit Client-Code die Liste nicht selbstständig ändern kann. Sind Änderungen notwendig, so muss die Klasse entsprechende Methoden anbieten. Nur wegen eines Frameworks dieses Paradigma zu brechen, halte ich für absolut inakzeptabel.

Von soliden, objektorientierten Code haben wir uns mit dem Umbau in Abbildung 10 in jedem Fallen sehr weit entfernt.

Fazit und Ausblick

Ich glaube nicht, dass O/R Mapper im Zusammenhang mit Domain-Driven Design/ Objektorientierung das richtige Mittel der Wahl sind. Man sollte den Sinn eines Einsatz in Abhängigkeit von den Anforderungen genau prüfen. Pauschale Entscheidungen zugunsten oder auch dagegen halte ich für suboptimal. Generell halte ich die Verwendung des Code-First Ansatz dann für geeignet, wenn eine optimierte Datenbankstruktur nicht nötig ist und Anwendungen nur eine geringe Komplexität aufweisen. Ggf. genügen aber für solche Einsatzzwecke auch leichtgewichtigere DataTables. Objektorientierung Out-Of-The-Box sind mit O/R Mappern ohnehin nicht möglich, sondern eher das Gegenteil.

Im zweiten Teil der Reihe werde ich darauf eingehen, warum aus meiner Sicht das Problem die objektorientierte und die relationale Welt in Einklang zu bringen, nicht wirklich, sondern nur in unseren Köpfen, existiert.

Lose Koppelung durch Messaging und Domain Events – Teil 2

Teil 2 – Ein paar Gedanken zu objektorientierte Prinzipien und Implementierung des EventAggregator

Vorheriger Teil – Teil 1 – Aggregate mittels Domain Events entkoppeln und synchronisieren

Einleitung und ein paar Überlegungen

Im ersten Teil der Blogreihe habe ich bereits über Gründe, warum die Entkoppelung von Komponenten so wichtig ist, gesprochen. Anhand einer einfachen Anforderung habe ich gezeigt, wie schnell Komponenten ungünstigerweise miteinander verdrahtet sind. Mit wachsenden Anforderungen steigt die Komplexität einer Anwendung. Ein klar erklärtes Ziel von Domain-Driven Design ist aber die Reduzierung von Komplexität. Ein wirkliches Problem sind Abhängigkeiten. Je komplexer Anforderungen, desto mehr Abhängigkeiten scheint es zu geben. Doch welche Strategie ist die richtige, um Komplexität zu beherrschen. Leider gibt Eric Evans Standardwerk „Domain-Driven Design Tackling Complexity in the Heart of Software“ [DDD] nicht immer ausreichend Antworten dazu. Auch sind die Interpretationen, wie man Domain-Driven Design richtig anwendet, teilweise extrem verschieden. Vielleicht gibt es auch nicht nur den einen richtigen Weg?!

Aus meiner Sicht basiert die Implementierung von Domain-Driven Design auf objektorientierten Prinzipien. Ohne diese ist es nicht möglich, Domänenmodelle zu entwerfen und in lauffähigen Code zu übersetzen. In der Definition von Objektorientierung nach Alan Kay ist alles ein Objekt, wobei Objekte die Instanz einer Klasse sind. Klassen bestehen dabei aus Struktur und Verhalten. Der Zugriff auf die Daten eines Objektes erfolgt über Schnittstellen (damit sind Methoden gemeint), um inkonsistente Objektzustände durch Beeinflussung von außen zu vermeiden. Dahinter steckt im Übrigen das Prinzip der Kapselung (auch Geheimnisprinzip genannt).

Ein typisches Beispiel für die Verletzung des Geheimnisprinzips ist häufig die Verwendung von öffentlichen Settern, die ungewollte Manipulation von Daten eines Objektes zulassen. Im schlimmsten Fall ist es Clientcode möglich, die Identität eines Objekts zu ändern, siehe Abbildung 1.


Abbildung 1 Verletzung des Geheimnisprinzips – Client Code kann ungewollte Manipulierungen am Objekt durchführen

Das Problem, das in Abbildung 1 besteht, ist zum Beispiel folgendes: Client Code bezieht über einen Repository– Aufruf eine Produktinstanz aus der Datenbank. Durch einen Programmierfehler ändert der Client Code die ID des Produkts und gibt die Instanz an das Repository, um es zu speichern. Da es sich nun um eine neue Identität handelt, wird das Objekt in der Datenbank als neue Zeile eingefügt. Klar ist dieses Szenario hypothetisch; es könnte sicher weitere Prüfungen im darunterliegenden Code geben, die dieses Fehlverhalten verhindern etc. Jedoch sollte so ein Problem durch die konsequente Anwendung des Geheimnisprinzips von vornherein vermieden werden. Die Daten dürfen nur kontrolliert über Methoden änderbar sein. Daten die nach der Objektinitialisierung invariant sein müssen, dürfen gar nicht mehr änderbar sein. Diese sind als readonly Felder anzulegen, siehe Abbildung 2. In der Realität existiert Code wie in Abbildung 1 leider viel zu häufig.


Abbildung 2 Korrekte Implementierung – Manipulation der ID ist nicht mehr möglich

Objekte kommunizieren nach Alan Kay immer über Nachrichten miteinander. All diese genannten Designprinzipien als auch die SOLID Prinzipien sind meiner Auffassung nach nötig, um Domain-Driven Design sauber zu implementieren. Ich werde mich in einer der nächsten Blogausgaben nochmals näher damit beschäftigen bzw. darauf eingehen, vor allem warum Domänenobjekte (Entities und Aggregate im Sinne von DDD) immer aus Daten und Verhalten bestehen. Das Erzeugen eines Entitymodells mit Hilfe eines O/R Mappers erzeugt keine Domänenobjekte, sondern lediglich um Datenzugriffsobjekte (DAO, vgl. auch [IDDD] S. 440-441); auch wenn es fälscherlicherweise häufig so gesehen wird, dass es sich bei Datenbanktabellen, die in Klassen übersetzt wurden, um Domänenobjekte handelt. Das ist aber nicht so, da die Klassen nur aus Struktur bestehen und damit nur Datenhalter sind. Domänenwissen ist aber auch Verhalten! Übrigens: Gerade weil O/R Mapper noch CRUD Funktionalität erzeugen, handelt es sich eher um das Table Module Konzept [Fowler], was sich grundsätzlich vom DDD Ansatz unterscheidet.

In der letzten Zeit hat sich in der DDD Community vor allem der Einsatz von Domain Events etabliert. Domain Events spielen eine wichtige Rolle, wenn es darum geht, Komponenten voneinander loszulösen. Nötig sind sie auch, wenn man CQRS anwenden möchte. Auf CQRS werde ich in einem weiteren Beitrag näher eingehen.

Die EventAggregator Implementierung

Nun aber zurück zum Thema Entkoppelung von Aggregaten und der versprochenen Implementierung des EventAggregator. Der EventAggregator soll als zentraler Messagebus innerhalb einer Anwendung agieren. Das Prinzip wie die losgelösten Aggregate miteinander kommunizieren sollen, habe ich in der vorherigen Ausgabe erläutert und schematisch dargestellt.

Der Anspruch dabei ist, dass keine zusätzlichen Systemkomponenten wie beispielsweise eine MSMQ o.ä. benötigt werden, da verteilte Szenarien nicht im Fokus stehen. Vaughn Vernon [IDDD] verwendet in seinen Beispielen eine statische EventPublisher Klasse, um Domain Events zu veröffentlichen. Ich möchte den EventAggregator allerdings gerne aus dem IoC-Container auflösen und per Konstruktorinjektion in betreffende Service- und Repository– Klassen injizieren. Deswegen zunächst die Schnittstellen-Definition. Der EventAggregator soll die beiden Methoden Publish und Subscribe bereitstellen, siehe Abbildung 3. Bei der Schnittstelle IDomainEvent handelt es sich um ein Markerinterface.


Abbildung 3 Die Schnittstellendefinition des EventAggregator

Um eine häufige Frage an dieser Stelle zu beantworten: Dependency Injection ergänzt den DDD Ansatz hervorragend. Alle Komponenten wie Services, Repositories oder Datenbankadapter sollten in einem Container registriert und daraus auflösbar sein. Domänenobjekte entspringen aber niemals dem IoC Container. Für die Instanzierung von Entitäten und Aggregaten sind Repositories zuständig. Benötigt also ein Aggregat eine Instanz des EventAggregator, so reicht das Repository seine injizierte Instanz der Klasse an den Konstruktor des Aggregates weiter.

Bei der Implementierung des EventAggregator habe ich mich stark an dieser Implementierung von Udi Dahan orientiert. Seine Implementierung der DomainEvents-Klasse ist statisch. Ich möchte aber soweit es geht alle Instanzen aus dem IoC-Container beziehen. Einen Grund, warum die Klasse statisch sein sollte, habe ich nicht direkt erkannt, sofern es im gesamten Anwendungskontext möglich ist, die Instanz des EventAggregator als Singleton im IoC-Container zu halten. Wird der Container allerdings immer Per-Call neu erstellt, so sollte bzw. muss der EventAggregator statisch sein.

In meinem Beispeil aus Teil 1 ging es darum, ein Domain Event zu veröffentlichen, wenn die Adresse eines Kunden geändert wurde. So muss zunächst eine Implementierung des KundenAdresseGeändert-Ereignisse erstellt werden, siehe Abbildung 4.


Abbildung 4 CustomerAddressChanged Domain Event Implementierung

Domain Event Objekte sollten nach ihrer Instanzierung unveränderbar sein. Deshalb sollten auch hier wieder private Setter verwendet werden. Domain Events dürfen im Übrigen nicht dazu dienen, ganze Objekte (also z. B. die Customer-Instanz) zu verschicken, sondern nur Identifier oder einfache Datentypen. Benötigt der Empfänger (der Observer) eine Instanz, so muss er sich diese selbst aus seinem Repository besorgen. Das ist wichtig, weil damit vermieden wird, das der Empfänger möglicherweise eine veraltet Instanz erhält. Außerdem müssen die Klassen in verschiedenen Zusammenhängen nicht immer gleich aussehen.

Wurde in der Customer- Klasse nun die Adresse erfolgreich geändert, so wird quasi nur noch ein Domain Event veröffentlicht. Im Sinne des Observer Musters ist die Customer- Klasse das Subjekt. Das Subjekt weiß nicht, wer sich alles dafür interessiert. Die Abhängigkeiten zu der MailSender Komponente und dem Sachbearbeiter (DeskOfficer-Klasse) werden wir mit der Verwendung der Domain Events und dem EventAggregator endlich los. Die Implementierung der Customer- Klasse sieht exemplarisch nun aus wie in Abbildung 5.


Abbildung 5 Customer Klasse besitzt keine Abhängigkeiten mehr zu anderen Aggregaten, nur noch zum EventAggregator

Eine exemplarische Implementierung auf der Empfänger-Seite könnte beispielsweise so aussehen, Abbildung 6.


Abbildung 6 Implementierung des Subscriber Aggregates

Auch der Subscriber, in diesem Fall das DeskOfficer Aggregat, benötigt keine Abhängigkeit zum Versenden. Hier muss ebenfalls die EventAggregator Instanz verfügbar sein. Im Konstruktor wird der Nachrichtentyp registriert. Das erfolgt durch den Aufruf der Methode Subscribe<T> des EventAggregator. Als Parameter muss ein Delegat mit gegeben werden. Damit reagiert die DeskOfficer Instanz auf alle Nachrichten vom Typ CustomerAddressChanged, die auf dem EventAggregator veröffentlicht werden. Das Wissen, was mit der Nachricht zu machen ist, steckt in der OnCustomerAddressChanged-Methode des DeskOfficer Objektes. Das Geheimnisprinzip bleibt gewahrt. Kein anderes Objekt entscheidet, wie sich der DeskOfficer zu verhalten hat, wenn das Fachereignis „Kunde Adresse wurde geändert“ eingetreten ist.

Schauen wir uns zum Schluss noch die Implementierung des EventAggregator selbst an. Ich habe mich in diesem Beispiel auf die Verarbeitung der registrierten Delegate beschränkt, siehe Abbildung 7. Deswegen habe ich dieses Bespiel mit „einfache“ Implementierung betitelt. In der nächsten Ausgabe werde ich den EventAggregator so erweitern, dass er auch in der Lage ist, MessageHandler Implementierungen aus einem Unity-Container aufzulösen.


Abbildung 7 Einfache EventAggregator Implementierung

Ich hoffe, die ersten beiden Teile konnte die Vorteile von Messaging und der Entkoppelung von Komponenten nachvollziehbar aufzeigen. Über Feedbacks und Kommentare freue ich mich natürlich. Was haltet ihr von diesem Vorgehen?

Verwendete Literatur:

[IDDD] Vaughn Vernon, Implementing Domain-Driven Design, 2013, Addison-Wesley

[DDD] Eric Evans, Domain-Driven Design, 2003, Addison-Wesley

[Gamma] Erich Gamma, Design Patterns, 1994, Addison-Wesley

[Martin], Robert C. Martin, Clean Code, 2008, Prentice Hall

[Fowler], Martin Fowler, Patterns of Enterprise Application Architecture, 2003

Lose Koppelung durch Messaging und Domain Events – Teil 1

Teil 1 – Aggregate mittels Domain Events entkoppeln und synchronisieren

Ausgangssituation

Viele Prinzipien objektorientieren Software-Designs zielen auf Abstraktion und Entkoppelung hin. Ziel ist dabei unter anderem, dass Komponenten möglichst wenig Abhängigkeiten besitzen und somit leichter verständlich sowie einfacher weiter zu entwickeln sind. In der Realität sieht man oft das Gegenteil. Im Lebenszyklus eines Softwaresystems mehren sich die Abhängigkeiten zwischen Softwarekomponenten. Das macht den Code schwer wartbar, da Änderungen in einer Komponente dann zwangsläufig auch Änderungen in anderen Komponenten erfordern. Dies gilt es aber unbedingt zu vermeiden.

Bei der Anwendung von Domain-Driven Design (DDD) Prinzipien kommt schnell bei der Implementierung von Aggregaten im Entwicklerteam die berechtigte Frage auf, wie wird ein Aggregat von der Aktualisierung eines anderen benachrichtigt? Stellen wir uns vor, wir haben folgende fachliche Anforderung zu erfüllen:

„Ändert sich die Adresse eines Kunden, so muss der zuständige Sachbearbeiter per Email benachrichtigt werden.“

Gehen wir davon aus, dass wir Customer als auch DeskOfficer als Aggregate Root identifiziert haben.

Bei einer solch einfach anmutenden Anforderung werden in der Realität in der Regel jede Menge Design-Fehler begangen.

Übliche Implementierungen erzeugen zu viele Abhängigkeiten

In der folgenden Abbildung 1 sehen wir eine typische Implementierung, wie sie in der Realität häufig zu finden ist.


Abbildung 1 Typische Implementierung bricht das Single Responsibility Prinzip

Das erste Problem bei der Implementierung in Abbildung 1 besteht darin, dass das Aggregat Customer eine Abhängigkeit zu einer anderen Komponente, nämlich der MailSender Klasse, besitzt. Des Weiteren wird hier das Single Responsibility Principle (SRP) [Martin] verletzt. Die Methode „ChangeAddress“ tut nun etwas, was sie eigentlich nicht vorgibt zu tun. Sie ändert nämlich nicht nur die Adresse, sondern versendet auch eine Mail. Für Client-Code ist das aber nicht erkennbar.

Nun, man könnte die Methode umbenennen in ChangeAddressAndSendAddressChangeMessage o.ä. Diese würde das Problem nicht wirklich lösen. Käme eine weitere Anforderung hinzu, dass der zuständige Sachbearbeiter bei der Adressänderung auch direkt per „Notification“ in seiner Anwendung benachrichtigt werden soll, so würde der Methodenname wieder angepasst werden müssen (z.B. ChangeAddressAndSendAddressChangeMessageAndNotifyDeskOfficer). Methodenname als auch der Code der Methode würden im länger und unleserlicher werden.

In diesem Fall würden sogar noch eine Abhängigkeit zum Aggregat DeskOfficer hinzukommen, um auf diesem die Methode Notify aufzurufen, siehe Abbildung 2. Das Problem von Abhängigkeiten wäre aber ohnehin nicht gelöst. Single Responsibility wird damit auf keinen Fall erreicht.


Abbildung 2 Noch mehr Abhängigkeiten

In Abbildung 3 ist zu sehen, dass das Customer-Aggregat von den anderen beiden Aggregaten direkt abhängig ist. Zwar schreibt Eric Evans [DDD], dass ein anderes Aggregate außerhalb der Aggregatsgrenze referenziert werden kann, falls es nötig ist. Dies führt aber meiner Meinung nach zu großen, unübersichtlichen Objektgraphen und unnötigen Abhängigkeiten. Vaughn Vernon [IDDD] schlägt vor, dass Aggregate ausschließlich durch „Identity References“ –also IDs- auf einander verweisen sollten. D. h. beispielsweise, dass unser Customer Aggregat nur die ID zum verantwortlichen Sachbearbeiter (ResponsibleDeskOfficer) hält, aber keine Objektinstanz. Damit können zudem auch mögliche Performance-Probleme, die bei der Objektinstanzierung auftreten könnten, gelöst werden. Gibt es Änderungen am Customer-Objekt, die auch für den verantwortlichen Sachbearbeiter interessant sind, so muss die betreffende Instanz durch ein Domain Event (Fachereignis) benachrichtigt werden. Um zwei Aggregate, die nichts voneinander wissen, zu synchronisieren, werden demnach Domain Events benötigt. Mit dieser Technik erreicht man die gewünschte Entkoppelung der Komponenten.


Abbildung 3 Das Customer Aggregat muss andere Aggregate referenzieren, um seine Aufgaben zu erfüllen.

Bevor ich weiter auf Domain Events eingehe, hier noch ein weiterer ungünstiger -aber oft verwendeter- Lösungsansatz, bei dem die Aufrufe in eine darüberliegende Dienstklasse, z. B. einem CustomerModifyDomainService verlagert werden, siehe Abbildung 4. Das ist aus meiner Sicht nicht wirklich besser, da das Problem der Abhängigkeiten und Methoden-Benennung nur eine andere Ebene verlagert werden würde.


Abbildung 4 Auch keine Lösung, nur eine Verlagerung des Problems…

Domain Events

Schauen wir durch die DDD-Brille, so können wir aus der o.g. Anforderung ein Domain Event, sprich ein fachliches Ereignis, herauslesen. Im Grunde kann man auf einer abstrakten Ebene sagen: Wenn das Ereignis „Kundenadresse wurde geändert“ eingetreten ist, so muss in der Folge eine Email versendet werden und der Sachbearbeiter direkt im System benachrichtigt werden. Das fachliche Ereignis löst also weitere Aktionen aus. In einem Fachmodell sollte nun ein Ereignis „CustomerAddressChanged“ existieren. Zu beachten ist, dass Domain Events immer in der Vergangenheit formuliert werden. Diese Ereignis tritt immer dann ein, wenn die Adresse eines Kunde verändert wurde. Den Befehl (Command), der das Ereignis auslöst, besitzt das Aggregat Customer in Form der Methode ChangeAddress. Wie andere Aggregate auf dieses Ereignis reagieren müssen, liegt nicht im Verantwortungsbereich des Customer-Aggregates (Seperation of Concerns).

Reden Fachexperten über ihre Fachdomäne, so formulieren sie häufige Ereignisse in der Form „Wenn dies passiert ist, dann muss danach das und das passieren/ informiert werden…“ o.ä.

Domain Events sind Teil der Ubquitious Language [DDD] und müssen sich deshalb auch im Domänenmodell bzw. im Code wiederfinden. Im Gespräch mit den Fachexperten muss herausgefunden werden, wer sich für welches Ereignis interessiert und wie derjenige darauf reagieren muss.

Domain Events sind im Grunde nichts anderes Nachrichten, die zwischen Komponenten eines Systems ausgetauscht werden. Dies entspricht dem Observer Muster [Gamma]. Ein Subjekt veröffentlicht eine Nachricht und ein oder mehrere Beobachter (Observer) abonnieren diesen Nachrichtentyp. Dieses Muster ist auch als Publish-/ Subscribe Muster bekannt. Der Vorteil ist, dass die Komponenten sich nun nicht mehr kennen bzw. miteinander verdrahtet sein müssen.

Für unseren Fall ergibt sich folgende Struktur, Abbildung 5. Die Kommunikation zwischen den Komponenten erfolgt nur noch in Form von Nachrichten. Die Aggregate Root
Customer referenziert nun keine Instanz der Aggregate Root DeskOfficer mehr, sondern hat nur noch eine Identity Reference [IDDD], sprich eine ID, siehe auch Abbildung 6. Ändert sich die Kundenadresse, erfolgt dies durch den Aufruf des ChangeAddress– Commands (Methode) auf einer Customer-Instanz. Dieser Command löst das Domain Event CustomerAddressChanged aus, welches auf einem Message Bus (EventAggregator –Klasse) veröffentlich wird. Alle Aggregate, die diesen Nachrichtentyp abonniert haben, reagieren darauf und können ihre Aufgabe erfüllen. So kann die betreffende DeskOfficer Instanz eine Notification erstellen etc. Für das Versenden der Email würde ich einen spezifischen MessageHandler erstellen, der verantwortlich ist, die richtige Email zu erstellen. Dieser Handler kann dann die MailSender-Komponente referenzieren, die den eigentlichen Versand übernimmt oder die Mail-Nachricht besser zunächst in einer Queue ablegt etc.

 
 


Abbildung 5 Entkoppelte Aggregate


Abbildung 6 Adresse ändern und Domain Event veröffentlichen

In diesem ersten Teil meiner neuen Blogreihe zum Thema „Lose Koppelung durch Messaging“ habe ich eine grundsätzliche Idee geliefert, wie Komponenten entkoppelt werden können. Im nächsten Teil zeige ich, wie die benötigten technischen Komponenten wie EventAggregator und MessageHandler in C# realisiert werden und wie wenig Coding-Aufwand dafür eigentlich notwendig ist.

Als weiterführende Literatur zur Thematik möchte ich vor allem Vaughn Vernons „Implementing Domain-Driven Design“ empfehlen.

 
 

Verwendete Literatur:

[IDDD] Vaughn Vernon, Implementing Domain-Driven Design, 2013, Addison-Wesley

[DDD] Eric Evans, Domain-Driven Design, 2003, Addison-Wesley

[Gamma] Erich Gamma, Design Patterns, 1994, Addison-Wesley

[Martin], Robert C. Martin, Clean Code, 2008, Prentice Hall

Fehler bei Gated CheckIns mit VS2010 und TFS Online

Wir nutzen aktuell in einem Projekt den TFS Online (visualstudio.com) zusammen mit Visual Studio 2010. Als Build-Definition soll Gated Check-In verwendet werden, Standard ohne Änderungen.

Versucht man nun einzuchecken, erscheint die folgende Fehlermeldung:

Bild

Interessant dabei ist allerdings, wenn man den Check-in über die Source Control von VS 2012 macht, dass es dann funktioniert.

Ich habe nach dem Problem im Netz recherchiert. Es scheint auch bei anderen aufzutreten. Lösung konnte ich aber nicht finden.

Hat jemand eine Idee?

Windows Azure Kolumne im Fachmagazin Visual Studio One

In der aktuellen Ausgabe der Visual Studio One ist bereits dritte Teil meiner Windows Azure Kolumne zum Thema WCF unter Windows Azure erschienen. Inhaltlich beschäftigt sich die Artikelreihe vor allem mit der Entwicklung von Softwaresystemen (Geschäftsanwendungen), die auf Windows Azure laufen sollen. Grundsätze der Objektorientierung, die Anwendung von Domain-Driven Design und die Erstellung von sauberen Code beziehe ich jeweils in meine Betrachtungen ein.

Einen Artikel Preview findet man hier: http://www.visualstudio1.de/images/Previews/032013-Fritzsche.aspx

EWS via Powershell aktivieren – Exchange Online (O365)

Möchte man die Exchange Webservices (EWS) nutzen, um beispielsweise Postfächer des Exchange Online in eigene Anwendungen zu integrieren, wird man schnell auf das Problem stoßen, dass für alle Nicht-Administratoren-Konten der Zugriff via EWS deaktiviert sind. Die EWS sind zwar standardmäßig auf O365 aktiviert, jedoch nur für Administratorenkonten.

Prüfen kann man das, in dem man die URL zum Dienst im Browser eingibt: https://%5Bo365-server-url%5D/ews/exchange.asmx

Hier wird eine Authentifizierung verlangt. Gibt man dann seine Admin-Credentials an, erhält man den Zugriff auf die WSDL. Als Nutzername muss hier der Userprincipalname (UPN) angegeben werden, mit dem man sich auch am Office 365 Portal anmelden würde. Also zum Beispiel: max.mustermann@contoso.com

Versucht man nun das Gleiche mit einem normalen Nutzeraccount, schlägt die Aktion mit dem Fehler 403 -Forbidden fehl. Ursache dafür ist, dass für Accounts, die nicht der Rolle Administrator zugeordnet sind, EWS abgeschalten ist. Nun möchte man natürlich nicht jedem Nutzer der Organisation Adminrechte auf dem O365-Portal erteilen. Im Portal findet sich allerdings keine Option die Exchange Webservices für einzelne Nutzer ein- und auszuschalten. Dafür ist der Zugriff auf Office 365 mit Hilfe der Windows Powershell notwendig. Der Artikel beschreibt nun im Folgenden das Vorgehen.

Zunächst ist es notwendig, die Cmdlets für O365 zu installieren. Microsoft bietet dafür einen sogenannten O365 SignIn Assistenten zum Download an. Ausserdem müssen die Microsoft Online Services Module installiert werden. Beide Links zu den Downloads findet man hier: http://onlinehelp.microsoft.com/en-us/office365-enterprises/hh124998.aspx#BKMK_connect

Nach der erfolgreichen Installation muss die Powershell für O365 als Administrator gestartet werden. Der Aufruf befindet sich im Startmenü. Nun muss man sich mit seinen O365 Credentials anmelden.

Dies geschieht mit dem Befehl: $LiveCred = Get-Credential

Es folgt ein Dialog zur Eingabe der Credentials. Danach muss die Session erstellt werden. Dazu ist folgender Befehl notwendig:

$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://ps.outlook.com/powershell/ -Credential $LiveCred -Authentication Basic -AllowRedirection

Danach muss die Session noch importiert werden. Der Befehl dafür: Import-PSSession $Session

Hat alles geklappt, sieht die Konsole so aus:

Nun können wir mit der eigentlich Aufgabe beginnen.

Zunächst prüfen wir, ob das Postfach für Exchange Webservices freigegeben wurde. Dafür ist das folgende Cmdlet zuständig:

Get-CasMailbox -Identity max.mustermann@contoso.com |FL EwsEnabled

Die Konsole gibt nun die Settings der Mailbox aus.

Steht der Wert für EwsEnabled auf FALSE, muss folgender Befehl ausgeführt werden, damit die Mailbox über EWS erreichbar ist:

Set-CasMailbox -Identity max.mustermann@contoso.com -EwsEnabled:$true

Danach sollte die o.g. URL für das aktivierte Konto erreichbar sein. Damit steht dem Zugriff auf das Postfach über eine .NET Applikation nichts mehr im Wege.

Möchte man aus irgendeinem Grund, den EWS Zugriff wieder deaktivieren, dann geht dies wie folgt:

Set-CasMailbox -Identity max.mustermann@contoso.com -EwsEnabled:$false

Views müssen für Volltextsuche schemagebunden sein

Sicher ein alter Hut für DBA´s! Aber irgendwie stoße ich von Zeit zu Zeit auf dieses kleine Problem. Und um anderen die Suche nach der Lösung zu vereinfachen, schreibe ich es mal auf.

Problemstellung:

Ich möchte über die View einer MS SQL Server Datenbank einen Volltextindex legen.

Tue ich dies, bekomme ich mein Vorhaben mit dem Fehler quittiert, dass eine eindeutige Spalte auf der View vorhanden sein muss, sofern die noch nicht indiziert wurde.

Fehlermeldung: „A unique column must be defined on this table/view.“

In diesem Fall besteht die Lösung darin, der View einen Index zu verpassen, was aber daran scheitert, dass die View nicht schemagebunden ist.

Möchte ich eine vorhandene View an ein Schema binden, kann das mit dem folgenden Statement gemacht werden:

ALTER VIEW [Profile].[MyView]

WITH SCHEMABINDING AS SELECT  

Danach ist es notwendig, einen „Clustered Unique Index“ anzulegen. Dafür bietet sich natürlich eine ID-Spalte an.

Hat das funktioniert, kann ich nun der View den gewünschten Volltext Index spendieren und dann die Volltextsuche ausführen.

Screencast Serie zu Windows 8 App Development

Meine Kollegen von Visual World haben eine interessante und hilfreiche Videoserie zum Thema Windows-8-App-Development gestartet. Mit Hilfe dieser kostenfreien Serie rund um dieses Thema findet man recht schnell den Einstieg in die Spezifika der App-Entwicklung. Einfach mal reinschauen und gerne auch Feedback geben, ob es hilfreich war und gefallen hat.

Die erste Folge hat meine Kollege Geraud erarbeitet: http://www.youtube.com/watch?v=m7iF2-ucDpE&feature=plcp

Viel Spaß!

Volltextsuche mit Problem bei Office 2010-Dateiformaten im SQL Server 2008

Soeben stieß ich auf ein Problem in einer bestehenden Lösung, bei der Office-Dokumente als Binaries in einer varbinary(max) -Spalte im SQL Server gespeichert werden. In der Applikation soll es möglich sein, in der Datenbank gespeicherte Dokumente zu durchsuchen. Dafür wurde für die Binärdaten-Spalte in der Datenbank ein Fulltext-Index/Katalog wie folgt angelegt.

CREATE  FULLTEXT CATALOG [DefaultCatalog] WITH ACCENT_SENSITIVITY=ON

AS  DEFAULT

AUTHORIZATION [dbo]

Danach wird auf der Spalte „Content“, welche die Dokumente als Binary enthält, der Volltext-Index angelegt. Da es sich- wie bereits gesagt- um eine Binärdatenspalte handelt, muss eine weitere Spalte angegeben werden, die die Dateierweiterung enthält. Dort steht im Grunde die Dateiendung, also doc, docx, xls usw. drin.

CREATE FULLTEXT INDEX ON dbo.Documents

(Content TYPECOLUMN Extension)

KEY INDEX Files_PK

ON DefaultCatalog

WITH CHANGE_TRACKING AUTO;

Nun ist es bereits möglich, die Inhalte gespeicherter Dokumente zu durchsuchen. Das folgende Statement gibt uns alle Namen der Dokumente aus, bei denen im Text das Word „Branchenschwerpunkt“ vorkommt.

SELECT Name FROM dbo.Documents  WHERE Freetext (Content, ‚Branchenschwerpunkt‘)

Das funktioniert soweit gut. Nun zum Problem. Zufälligerweise bemerkte ich, dass Worddokumente mit der Endung docx nicht gefunden wurden. Nach etwas Recherche kam ich der Ursache auf die Spur.

Man kann mit dem folgenden Befehl abfragen, welche Documenttypen registiert sind und damit in der Volltextsuche berücksichtigt werden.

SELECT  * FROM sys.fulltext_document_types;

Findet man in dieser Liste die Office 2007/10 Dateitypen (docx, xlsx usw.) nicht, ist folgendes zu tun:

Microsoft bietet sogenannte Filter-Packs an. Der Download für die Microsoft Office 2010-Filterpacks ist hier zu finden:

http://www.microsoft.com/en-us/download/details.aspx?id=17062

Dieses Paket muss auf der Maschine, auf welcher der SQL Server läuft, installiert werden. Danach erfolgt die Registrierung mit folgendem Befehl:

sp_fulltext_service ‚load_os_resources‘, 1

Der SQL Server -Dienst muss anschließend zwingend neu gestartet werden.

Fragt man danach die registrierten Typen erneut ab (SELECT  * FROM sys.fulltext_document_types;), sollten die gewünschten Dateitypen im Resultset erscheinen.

Damit ist das Durchsuchen von Office 2007/2010 -Dokumenten über die Volltextsuche möglich.