-
Bisher in dieser Serie: DDD – Eine Einführung
Die zentrale Technik im Domain Driven Design ist die “Allgegenwärtige / Universelle Sprache”, engl. ubiquitous language. Um zu erklären, wobei es darum geht, möchte ich zunächst weg von der technischen Sprache. Wir alle kennen das Phänomen, das manche Filme und Bücher in der originalen Sprache einfach aussagekräftiger und präziser sind. Übersetzungen gehen oft soweit, das Film- und Buch-Titel einfach die Aussage des originalen Titels kaum noch widerspiegeln. Übersetzungen erzeugen schlicht kleine Fehler und Abweichungen, die in der Summe (bei schlechten Übersetzungen) einfach nicht mehr die selbe Aussage treffen.
Übertragen wir diese Film-Metapher auf die Software Entwicklung. In manchen starren Prozessen gibt es Entwickler, Architekten und Analysten. Letztere kommunizieren direkt mit dem Kunden, übersetzten die Anforderungen des Kunden in die Sprache der Architekten und Entwickler. Meist haben Entwickler und Architekten kaum noch die selbe Sicht und Sprache auf die Domäne wie der Kunde. Wie wir bereits gesehen haben, sind solche Übersetzungen nicht trivial und fehlerfrei. Im Extremfall kommunizieren die Team-Mitglieder in einer anderen Sprache und damit mit einem anderen Modell, als im Quelltext des Systems abgebildet ist. Sicherlich kennt fast jeder solche Projekte und Situationen.
Evans schlägt hier einen Ansatz vor, der zunächst manchem radikal erscheinen mag. Aber wie bei allen neuen Techniken ist ein wenig Dogmatik der Verbreitung sicherlich dienlich (siehe zum Beispiel TDD). Evans schlägt eine vereinheitlichte Sprache zwischen Kunden und Entwicklern vor. Diese allgegenwärtige, universelle Sprache bildet das Modell der Domäne ab. Diese Sprache manifestiert sich in der Kommunikation zwischen Team und Kunden, in Dokumenten und natürlich auch im Quelltext. Sie ist das Kernstück, das Rückgrat des Modells und muss unbedingt von allen Mitgliedern des Team kommuniziert werden.
Interessant zu diskutieren wäre hierbei das gebräuchliche Vorgehen, Quelltext in englischer Sprache zu halten während wir fast alle auf Deutsch kommunizieren. Denn dies ist bei deutschsprachigen Kunden schließlich auch eine Übersetzung. Auf der anderen Seite gehört es zum “guten Ton”, Quelltext auf Englisch zu schreiben – wohl auch weil die deutsche Sprache teilweise einfach mehr Wörter zum Ausdruck braucht.
Die Wahl der Dokumentation ist unbedingt dem Kontext anzupassen, denn die allgegenwärtige Sprache wird sich über einige Zeit weiterentwickeln und ständig Verändern, bis sie sich in ihrem Kern stabilisiert hat. Für manche Projekte machen hier sicher starre UML-Diagramme und Anforderungsdokumente Sinn, wenn das Projekt nicht mit dauernden Änderungen in der Domäne umgehen muss. Für Domänen mit häufigen, ständigen Änderungen empfehlen sich dann wohl eher temporäre Diagramme (Skizzen). Evans gibt hierbei zu bedenken, das gerade Diagramme eher nur Ausschnitte des Modells reflektieren sollten. Gerade UML-Diagramme tendieren dazu, sich extrem zu vermehren oder aber allumfassend zu werden. Solche Diagramme sind sicherlich kaum zur Kommunikation geeignet, weil es viel zu lange dauert sie zu lesen und zu ändern. Gerade in der Anfangsphase der Modellierung ist das Modell in der Regel noch nicht Stabil genug. Hier sind Skizzen und Ausschnitte unter Umständen viel schneller von Hand zu erstellen als jedes andere Diagramm. Zu bedenken ist auch, dass je mehr komplexe Dokumentation des Modells es gibt, desto mehr Artefakte müssen auch synchron mit der Entwicklung der Sprache gehalten werden. Aus der eigenen Erfahrung kann ich berichten, dass in manchen Projekten einfache digitale Mindmaps ausreichen, um Änderungen an der Sprache (und damit auch an der Domäne) zu dokumentieren. Dies hat unter Umständen aber den Nachteil, dass alle Dokumente fast nur inkrementelle Ausschnitte sind. Auf der anderen Seite sind solche Mindmaps schnell geschrieben, strukturiert und auch schnell verstanden.
Egal welcher Ansatz gewählt wurde: Die Sprache und damit auch das Modell der Domäne manifestiert sich vor allem in der Kommunikation zwischen Team und Kunde und vor allem auch innerhalb des Teams. Dies bedeutet automatisch, dass jedes Konzept in der Sprache seine Entsprechung im Quelltext finden muss. Alle Änderungen in der Sprache sind somit auch eine Änderung am Modell. Somit muss dann auch der Quelltext die Änderungen mittragen. Andersherum kann es auch Konzepte geben, die bisher nur implizit vorhanden – und vielleicht bisher kaum verstanden - sind. Dies manifestiert sich vielleicht erst in der Entwicklung des Systems. Solche impliziten Konzepte müssen dann explizit gemacht werden und auch Eingang in die Sprache finden. Erst dann sind sie schließlich mit den Experten in der Domäne diskutierbar.
Unsere allgegenwärtige, universelle Sprache ist während der Modellierung natürlich Änderungen unterworfen - auch wenn wir Big-Design-Up-Front arbeiten. Dann gibt es diese Änderungen hoffentlich nur während der Modellierungs-Phase (was ich aus meiner bisherigen Erfahrung eher selten glaube). Dies kann dazu führen, dass dass Modell der Domäne nicht mehr mit den neuen Anforderungen skaliert. Dann ist es Zeit, Teile des Modells zu entfernen und neu zu beginnen. Dies erreicht man im Prinzip nur durch Experimenten mit dem Modell und Diskussion mit den Experten der Domäne. Beispiele für solche Vorgänge sind in den Büchern von Eric Evans und Jimmy Nilsson in vielen Beispielen zu finden.
Einen Vergleich, den ich sehr interessant finde, gibt es zwischen der allgegenwärtigen Sprache des DDD und der System-Metapher des eXtreme Programming. Die Metapher wurde in den ursprünglichen XP-Büchern kaum behandelt (nur auf wenigen Seiten) und ist daher vielleicht eine der kaum praktizierten Methoden (jedenfalls hört und liest man eher selten davon). Man kann die allgegenwärtige Sprache im Domain Driven Design als ein Schlüssel zur Erstellung einer solchen System-Metapher auffassen.
Abschließend ein Ausblick:
In den folgenden Teilen der Serie stelle ich den von Evans eingeführten Sprach-Elemente vor, um aus der Kommunikation mit Experten der Domäne dann ein Modell zu strukturieren. Weitere Teile werden Techniken enthalten um Konzepte im Quelltext expliziter zu machen, das Modell reichhaltiger zu gestalten, seine Anstrengungen in der Modellierung zu fokussieren und ein Modell gegen unerwünschte Einflüsse zu schützen sowie mit anderen Modellen zu integrieren.
Zum Schluss: ein gutes Beispiel für schwierige Übersetzungen ist schon der Titel dieses Beitrags: “ubiquitous langauge” lässt sich übersetzten, verliert dann aber schnell an Bedeutung – jedenfalls in meinem sprachlichem Empfinden.
DDD Serie:
Tags: ddd, domain driven design, domainmodel, dotnet
Dies ist der Beginn einer Serie zum Thema Domain Driven Design (DDD). Ich werde versuchen, die Serie mit weiteren Beiträgen zu füllen, während ich an einem Vortrag zum Thema arbeite.
Domain Driven Design ist der Titel eines Buches von Eric Evans. Das Buch selbst ist nicht mehr “ganz neu” – trotzdem gab es (gerade auch in den Java und ALT.NET Gemeinschaften) einigen “Hype” darum. Dabei gibt es eigentlich nicht genau das DDD. Zunächst ist DDD eine Kombination aus “Model Driven Design” (nicht zu verwechseln mit MDA – Model Driven Architecture) und Prozessen rund um Kunden-Kommunikation, Integration und Modularisierung verschiedener Domänen, etc..
Nun stellt sich natürlich die Frage, wann DDD eingesetzt werden sollte. Zunächst ist die Ansatz der allgegenwärtigen Sprache sicherlich universell nutzbar. Um den vollen Nutzen zu erfahren ist aber sicherlich die Kopplung mit Model Driven Design empfehlenswert. Ausgangspunkt ist das Domain Model Pattern von Martin Fowler. Hierbei sollen die kompletten Fähigkeiten unserer OOP-Sprachen genutzt werden, um die Komplexität der Domäne in ein reichhaltiges Objektmodell zu übersetzten.
Damit dürfte klar sein, dass Model Driven Design, also OO-Modelle der Domäne, sich vor allem in OLTP-Szenarios einsetzten lassen. Derzeit ist Objekt-Orientierung einer der stärksten Ansätze, die wir kennen, um hohe Modularisierung und Reduzierung der Komplexität zu erreichen.
Dies bedeutet aber auch, dass OLAP-Szenarios ungeeignet für den Einsatz eines reichhaltigen Objektmodells sind – denn hierbei geht es primär um die Analyse von Daten und weniger um komplexe Prozesse und Regeln innerhalb von Transaktionen. Die Frage nach Ad-hoc Reporting wurde auf der DDD-Mailingliste schon mehrfach gestellt und auch beantwortet – mittlerweile gibt es dazu auch einen Artikel und eine Diskussions-Zusammenfassung auf der Webseite zu DDD. Ich möchte hier nicht direkt im Detail darauf eingehen - dies werde ich zu einem späteren Zeitpunkt noch tun. Zusammenfassend möchte ich aber sagen, das solche Objektmodelle und damit verbundene Datenbank-Schemata eigentlich nicht zum performanten Ad-hoc Reporting geeignet sind. Alternativen wären flache, spezielle Datenbank-Sichten oder eine Art Data-Warehouse-Lösung.
Ziel des Model Driven Design ist also die Erstellung eines reichhaltigen Objektmodells, das wir mit völliger Freiheit unter Ausnutzung aller Sprach-Features erstellen. Zu starke Kopplung an die Infrastruktur (etwa Basisklassen mit Transaktions- und Datenbank-Logik) sind hier hinderlich. Deshalb ist der Begriff “Persistence Ignorance” eng mit der Erstellung solcher Objektmodelle verbunden. Objekte ohne Einschränkung durch Infrastruktur oder Runtime-Container (wie zB EJB 2.0 - Java , CSLA - dotnet) werden auch POCOs genannt – Plain Old CLR Objects. Ich würde sogar noch ein Stück weiter gehen und von Infrastruktur-Ignoranz sprechen, da Persistenz nur ein Aspekt der Infrastruktur ist.
Natürlich bedeutet dies auch, dass wir gewisse Anforderungen an die Architektur solcher Systeme haben, auf der anderen Seite aber auch eine Menge gewinnen. Zunächst möchte ich auf die Gewinne eingehen. Solche Objektmodelle eigenen sich perfekt um Komplexität zu verstecken und Änderung zu handhaben. Die Arbeit mit solchen Modellen führt zu ausdrucksstarken Kombinationen aus bereits bestehenden ausdrucksstarken Teilen. Damit gewinnen wir als Lesbarkeit und auch Wartbarkeit. Objektmodelle ohne Abhängigkeit von der Infrastruktur bedeuten natürlich auch einfache Testbarkeit. Somit ist es möglich, die gesamte komplexe Logik in Objektmodelle mit automatisierten Tests ständig zu überprüfen. Unsere Architektur wird hier also testbar und wir werden nicht gehindert Tests zu entwickeln oder gar Test-first (TDD) zu arbeiten. Solche Objektmodelle erleichtern die Arbeit mit automatisierten Tests geradezu.
Es gibt aber auch einige Anforderungen an unsere Architekturen, um solche reichhaltigen Objektmodelle im Sinne von DDD zu entwickeln. Diese sind nicht unbedingt negativ (haben sogar positiven Einfluss auf das gesamte System), sind teilweise aber fast Voraussetzung. Zunächst einmal lassen sich Infrastruktur-Ignorante Objektmodelle im Prinzip nur entwickeln, wenn wir das Dependency Inversion Principle anwenden. Dies führt sofort zur Notwendigkeit eines Dependency-Injection-Containers zur Konfiguration der Abhängigkeiten. Natürlich ließe sich das DIP auch anders realisieren, pragmatisch gesehen nehmen uns solche Container (Castle Windsor, Spring.NET, StructureMap, Ninject) aber sehr viel Arbeit ab. Der Einsatz eines solchen IoC/DI-Containers hat darüber hinaus einen positiven Effekt auf die gesamte Anwendung: lose Kopplung ist schließlich eine Eigenschaft, die man sich generell (neben hoher Kohäsion) für seine Module wünscht.
Soll unser Objektmodell persistiert werden, brauchen wir auch einen starken, flexiblen O/R-Mapper. In letzter Zeit gab es viel Rumoren um das Entity Framework – um es kurz zu machen: derzeit sieht sowohl das EF-Team als auch die ALT.NET Community das EF nicht als eine Option an, wenn man DDD nutzt. Derzeit stärkstes Framework auf dem Markt ist damit NHibernate, eine ehemalige Portierung des Java-Frameworks Hibernate. NHibernate ist komplett in das dotnet-Ökosystem integriert und weit verbreitet.
Die Entwicklung reichhaltiger Objektmodelle erfordert zudem generell eine Leistungsfähige Entwickler-Struktur, denn ohne gute, OOP-erfahrene Entwickler ist es schwierig ein reichhaltiges Objektmodell zu erstellen. Viele Entwickler sehen sich zwar als OOP-erfahren, doch ist es noch ein großer Unterschied eine OOP-Sprache wie C# einzusetzen oder aber gute, leistungsfähige Objektmodelle zu entwickeln. Dies merkt eigentlich jeder, der bisher noch nie mit aller Kraft versucht hat, ein Modell noch reichhaltiger zu machen. Die ersten Versuche scheitern wohl in der Regel (und sind es auch bei mir). Das eingestehen und sehen der Fehler ist hier der Schlüssel um sich weiterzuentwickeln. Viele open-source Projekte nutzen reichhaltige Objektmodelle und sind optimal zum Lernen.
Zum Schluss noch einige Worte zum Aufwand. Ein Objektmodell zur Repräsentation der Domäne zu erstellen ist harte Arbeit und aufwendig – dem gegenüber steht aber die Leichtigkeit mit der später Komplexität gehandhabt werden kann (wenn das Modell gut genug ist). Als Alternativen gäbe es noch die Arbeit mit Transaction Scripts (Fowler) oder dem Table Module Pattern (Fowler). Letzteres erfreut sich gerade bei .NET-Entwicklern großer Beliebtheit wegen der guten IDE-Unterstützung in Visual Studio, dort bekannt als DataSets. Für einen genauen Vergleich möchte ich auf Martin Fowler’s Buch “Patterns of Enterprise Application Architecture” verweisen. Es sei aber gesagt, dass gerade das Table Module Pattern wahrscheinlich am wenigsten mit steigender Komplexität der Domäne skaliert, gefolgt von Transaction Scripts. Beide haben vor allem das Problem von Copy&Paste-Smells und Dopplung von Logik. Nach derzeitigem Stand ist ein objekt-orientieres Modell das einzige, das wirklich besser mit wachsender Komplexität skaliert. Problematisch ist allerdings, dass man Komplexität schlecht messen kann und keiner genau weiß, wo die Schwellwerte liegen. Letzten Endes ist hier die Erfahrung und Experimente / Prototypen gefragt.
Tags: ddd, domain driven design, domainmodel, dotnet
Objekt-Modellierung: Nomen? Verben? Beides? Keins?
0 Kommentare Eingestellt von Sebastian Jancke um 18:33Die allermeisten lernen wohl OOP – bis heute – in seiner einfachsten Form zunächst als “Finding the nouns”. Die Nomen finden – und dann daraus Objekte machen. Fehlen noch Aktionen, um daraus Methoden für ein Objekt zu erstellen – wie gut das es ja noch Verben gibt.
Vielen Entwicklern dürfte klar sein, dass diese Schema zu simpel ist und höchstens für die ersten OOP-Beispiele ausreicht. Fortgeschrittenere “Richtlinien” und Möglichkeiten zur Strukturierung bieten unter anderem der DDD-Prozess, und das Entity-Controller-Boundary Modell.
Trotzdem habe ich schon zu oft Entwickler gesehen, die krampfhaft versuchen, ein Objekt nach Nomen zu benennen, obwohl ein Verb viel aussagekräftiger wäre. Das Resultat sind die allerseits beliebten Konstrukte:
AnotherCarFinder.FindCar(myCarId);
Neben der Dopplung – die sich sehr holprig liest – ist das ganze auch kaum aussagekräftig. Deshalb wäre mein Ratschlag: Weg vom OO-Denken in Nomen, hin zu mehr Flexibilität und Kreativität.
Das obige Beispiel sieht so sicherlich viel lesbarer aus:
FindAnotherCar.By(myCarId)
Dies ist sicherlich noch nicht das non-plus-ultra und auch nicht das Ende der Möglichkeiten. Deshalb möchte ich alle Leser auffordern, mir ihre Beispiele und Ideen doch vielleicht zukommen zu lassen. Mich interessiert brennend, welche Techniken andere (unter anderem) nutzen, um die Lesbarkeit zu erhöhen.
Tags: domainmodel, dotnet, maintainability, readability
“ORMs für .NET im Vergleich” – Eine kleine Kritik
0 Kommentare Eingestellt von Sebastian Jancke um 23:44Nach Monaten ohne (öffentliche) Reaktionen und einer Diskussionen auf der ALT.NET.DE Mailing-Liste will ich versuchen, meine Kritik am Artikel “Objektrelationale Mapper für .NET im Vergleich” zu formulieren. Der Artikel wurde von Holger Schwichtenberg geschrieben und in dem Magazin dotnetpro (Ausgabe 4/2008) publiziert.
Da dieser Artikel sicherlich auch von Einsteigenden gelesen wird, oder von Menschen ohne Erfahrung speziell mit NHibernate, finde ich es wichtig, dass keine Halb-Wahrheiten stehen bleiben. Dieser Eintrag soll ein Versuch sein, mit einigen Fehlern und Missverständnisse im besagten Artikel aufzuräumen. Die Aufstellung ist sicherlich nicht komplett und ich maße mir auch keinerlei Wertung über den Artikel oder das Magazin oder den Autor an.
(Abschnitt: Henne oder Ei) Beim Forward Mapping machen NHibernate und das ADO.NET Entity Framework bislang nicht mit; sie können eine Datenbank nicht auf Basis von Geschäftsobjekten generieren.
Ich zitiere dazu (frei) aus der NHibernate Dokumentation, Abschnitt 3.5 “Optional configuration properties”: Die Eigenschaft “hibernate.hbm2ddl.auto” bestimmt, ob das DDL Schema automatisch bei Erzeugung der ISessionFactory exportiert werden soll. Mögliche Werte sind unter anderem “create” und “drop”. Mit “create-drop” wird das Datenbank-Schema zunächst erzeugt und beim Schließen der ISessionFactory dann gelöscht. Ich kann jedem nur empfehlen, diese Optionen auszuprobieren. Bisher habe ich sie auf Entwicklungs-Datenbanken erfolgreich nutzen können, um meine Produktivität in diesem Bereich zu steigern. Gleichzeitig halte ich eine Kopie der bisher ausgelieferten Datenbank vor. Mit Werkzeugen wie “Quest Comparison Suite” oder “RedGate SqlCompare” kann ich dann zum Ende einer Iteration die Änderungen automatisiert in ein Script übertragen lassen.
(Abschnitt: Geschäftsobjektklassen) Die Fachwelt spricht dann von Persistance Ignorance oder von Plain Old CLR Objects (POCOs). In diese Kategorie fällt NHibernate. NDO kann POCOs als Unterobjekte eines zu persistierenden Objekts verwenden.
Hierbei geht wohl die eigentliche Bedeutung des Wortes “POCO” verloren. Frei nach Fowler ist ein POCO ein “Plain Old CLR Object”. Also ein Objekt, das sonst keine Abhängigkeiten und Einschränkungen hat. Vererbung ist vielleicht die stärkste Form der Abhängigkeit. Durch Vererbung von einer zentralen Basisklasse meines Persistenz-Frameworks gehe ich somit eine sehr starke Abhängigkeit ein. Das Objekt ist somit kein POCO mehr. Die Begriffe POJO und POCO implizieren damit im Bezug auf Persistenz auch direkt einen Teil des Begriffes “Persistence Ignorance”. NDO ist somit nicht PI-fähig und die Geschäftsobjekte in diesem Zusammenhang sind keine POCOs.
(Abschnitt: Mapping und Treiber) Nicht alle Werkzeuge verwenden im Untergrund ADO.NET. VOA und NHibernate arbeiten mit JDBC (Java Database Connectivity), da es sich um Portierungen aus der Java-Welt handelt.
Dazu möchte ich wieder die NHibernate Dokumentation frei zitieren, im speziellen den Abschnitt 2 “Architecture”: NHibernate kann grundsätzlich auf zwei Arten eingesetzt werden: Entweder die Anwendung stellt NHibernate die entsprechenden ADO.NET Verbindungen zur Verfügung, oder aber NHibernate schirmt die Anwendung von der gesamten Datenbank-Kommunikation ab. Wie man in diesem Fall an der entsprechenden Grafik ("full cream" architecture) sehen kann, liegt auch dann ADO.NET (neben OLE DB und ODBC) zu Grunde. Noch deutlicher ist die Beschreibung des Interfaces ISession: “… kapselt eine ADO.NET Verbindung”.
Abschließend möchte ich noch einige Worte zum Fazit des Autors verlieren. Herr Schwichtenberg legt in seinem Vergleich anscheinend sehr viel Wert auf die Unterstützung mittels grafischer Werkzeuge. Ich kann diese Wahl der Kriterien nicht teilen und auch nicht unterstützen. Für Einsteigende mögen grafische Werkzeuge am Anfang sehr eingängig und leicht zu erlernen sein. Ich bezweifle allerdings, dass die “Dekoration” der Geschäftsobjekte mit Attributen schwieriger ist. Dies ist zwar kein “best practice” – da wir die Persistence Ignorate verlieren – ist aber für den Einstieg akzeptabel.
Schwerer wiegt für mich aber noch das Argument der Skalierbarkeit solcher grafischer Werkzeuge. Dazu reicht meist schon der Versuch, eine “mittelgroße” Anwendung (vielleicht 2-3 Mannmonate Aufwand?) in solch einem grafischen Werkzeug darzustellen. Hierbei scheitert derzeit vor allem der Entity Framework Designer – es werden schlicht alle Objekte auf der gesamten “Leinwand” dargestellt (Scott Allen - Visual Designers Don’t Scale). Auch mit Zoom-Funktion, Gruppierungen und Ausschnitte bleibt immer die Tatsache, das visuelle Werkzeuge in der Regel (bisher) einfach schlecht mit der Größe des Domänen-Modells skalieren.
Tags: dotnet, nhibernate, orm
Anlässlich der Diskussion auf der deutschen ALT.NET Mailing-Liste zum Thema: “Deutschsprachige Inhalte rund um ALT.NET” und der Tatsache, dass ich mich aus meiner Blog-Lethargie befreien möchte:
Ab jetzt ist hier alles in Deutsch geschrieben (naja fast alles) ;-)
Tags: dotnet