Sunday, October 28, 2007

Patterns 4 - Domain Driven Design

Im vorläufig letzten Beitrag zu Patterns möchte ich einige Patterns aus dem Buch Domain Driven Design von Evans auflisten. Leider sind die Patterns nicht Online verfügbar, doch gehört dieses Buch sowieso zur Pflichtlektüre wenn mit Domain Model's gearbeitet wird. Wer es weniger theorietisch mag und sehe möchte wie sich das Arbeiten mit DDD 'anfühlt' dem sei Applying Domain-Driven-Design and Pattern von Nilson empfohlen.

Supple Design
Unter dieser Überschrift sind Patterns enthalten welche die Absicht und Wirkung von Code möglichst transparent darstellen lassen sollen. Idee ist dass die Struktur des Code die Funktionalität möglich klar darstellt.
  • Side-Effect-Free-Functions: Mit diesem Pattern können, auch in Kombination mit unveränderbaren 'Value Objects', einfach verständliche und testbare Funktionen realisiert werden. Für das Verständnis und das Testen müssen nur Inputparameter und der Resultatwert der Methode berücksichtigt werden da keine Seiteneffekte vorhanden sind. Weiter wird empfohlen Commands (Methoden welche beobachtbaren Zustand verändern) in sehr einfache Operationen zu gliedern welche keine Domäneninformationen retournieren.
  • Assertions: Im Code sollen Pre-/Postcondition und Invarianzen definiert werden. Damit sollen auch Seiteneffekte explizit spezifiziert werden damit für das Verständnis einer Methode nicht alle Ausführungspfade analysiert werden müssen.
  • Intention-Revealing Interfaces: Hier gehts es darum dass Klassen- und Methodenamen gefunden werden welche die Funktionalität und Verantwortlichkeit klar ausdrücken. Es sollte nicht nötigt sein dass daneben noch dokumentiert werden muss, unter welchen Umständen eine Klasse und Methode wie funktioniert. Auch sollten im Namen Begriffe aus der 'Ubiquitous Language' auftauchen.
  • Standalone Classes: Gelingt es eine völlig losgelöste Klasse, ohne Abhängigkeiten auf andere Klassen zu entwickeln, reduziert man die konzeptionelle Komplexität massgeblich. Lose Kopplung kann eingesetzt werden um die Komplexität von Code zu reduzieren.
  • Closure of Operations: Dieses Konzept hält uns an Operationen wie mathematische Operationen aufzubauen da diese ein einfaches Konzept ist. Dabei sollen Eingabeparameter und Rückgabwert vom gleichen Typ sein. Ein solches Interface ermöglicht das Zusammenbauen von komplexeren Operationen aus einfacheren Primitiven.
  • Specification: Ein Value Object welches als Predicate bestimmt ob ein anderes Objekt ein bestimmtes Kriterium erfüllt.
  • Ubiquitous Language: Dabei sollen Begriff und Konzepte aus der Problemdomäne von den Entwicklern übernommen werden. Diese Begriff und Konzepte haben ein starken Einfluss auf den Design und sind einer der wichtigsten Treiber für den Design.

Building Blocks
Hier haben wir Patterns welche unter DDD häufig vorkommen.

  • Layered Architecture: Schlägt vor im allg. eine Applikation in Presentation, Application, Domain und Infrastruktur Layer zu schichten. Dies ermöglicht einen ausdrucksstarken Domainlayer zu erhalten da dieser nicht mit Anzeige-, Speicher-oder Berechtigungsaufgaben usw. überladen wird.
  • Entities und Value Objects: Wichtige Konzepte zum Unterscheiden von Klassen mit Identitäten (z.B: eine Person mit einer ID) und Klassen deren Identität durch die Werte der Klasse bestimmt ist (z.B eine Farbe identifizierbar durch ihre RGB-Werten). Es wird empfohlen Value Objects als unveränderbare Klassen zu designen.
  • Modules: Gibt Anleitunng nach welchen Aspekten Code modularisiert werden sollten. Dabei sollte auf geringe Kopplung und hohe Kohäsion geachtet werden. Modulnamen sollten Teil der Ubiquitous Language werden. "If your model is telling a story, the Modules are chapters".
  • Services: Im allg. sollte für jeden Service gelten: 1.) Die Operation auf dem Service ist Teil des Domänenkonzept welches aber nicht als Entitiy oder Value Object ausgedrückt werden kann. 2.) Die Service-Schnittstelle nimmt Bezug auf das Domänenmodell. 3.) Die Operation ist zustandslos. Desweitern kann ein Service in verschiedenen Schichten realisiert werden (Application-, Domain-, Infrastructure-Service) wobei sich die Verantwortlichkeiten je nach Schicht stark unterscheiden können.
  • Factories: Erzeugt in einer Operation ein konsistentes Objekt oder Aggregate (Invarianzen müssen erfüllt sein). Dieses Pattern wird wichtig sobald ein Objekt nicht mehr mit einem einfachen Konstruktoraufruf erzeugt und in einen konsistenen Zustand überführt werden kann.
  • Repositories: Lädt Objekte oder Aggregates von einem tieferen Infrastrukturlayer in den Speicher (z.B von der Datenbank) und erzeugt die Illusion dass die Objekte immer im Speicher vorhanden sind. Repositories werden spezifisch für Klassen von Objekten und Aggregates realisiert und zeigen auf ihren Schnittstellen die Designentscheidung bezüglich Objektzugriff.
  • Aggregates: Damit werden eine Anzahl von Entities oder Value Objects logisch gruppiert um die Kopplung zwischen Teilen des Modells zu reduzieren. Pro Aggregate wird eine Klasse als Aggregate-Root definiert und der Zugriff auf das Aggregate darf nur noch über diesen Root erfolgen.
Strategic Design
Hier sind Patterns aufgelistet welche helfen in einem grösseren, komplexen System das Domänenmodell und verschiedene Teile davon herauszubilden.
  • Core Domain: Definiert die wichtigste Domain in einem Projekt. Für diese wird mit grosser Sorgfalt das Modell gefunden und ständig verfeinert. Jegliche Art von Entscheidungen können nun so gefällt werden dass es zuerst der Core Domain zu Gute kommt.
  • Generic Subdomain: Eine Subdomain ist nicht der Hauptgrund weshalb eine Software entwickelt wird, doch ist die Subdomain (z.B ein Zeitzonen-Modell) eine wichtige Unterstützung der Core Domain. Solche Domänen sollten aus der Core Domain gelöst werden um diese nicht unnötig komplex zu machen. Auch können Generic Subdomain häufiger als Core Domains wiederverwendet oder auch eingekauft werden. Vor und Nachteile beim Einkaufen von Sumdomänen sind in diesem Kapitel auch erläutert.
  • Domain Vision Statement: Schlägt vor dass die Coredomäne und ihr Wert zu Projektbeginn beschrieben wird um dem Entwicklungsteam eine gemeinsame Richtung zu geben.
  • Segregated Core: Beschreibt den Vorgang wie aus einem System ohne Core Domain eine solche gebildet werden kann.

Large-Scale Structure ist ein Kapitel in welche einige Patterns enthalten sind welche helfen ein Struktur für grosse Systeme zu finden.

Maintaining Model Integrity beschreibt Ansätze wie Modelle zwischen unterschiedlichen Systemen synchron gehalten werden oder wie mit den Unterschieden umgegangen werden kann.

Thursday, October 18, 2007

Patterns 3 - Enterprise Application Architecture

Wie der Titel dem geneigten Leser andeutet möchte ich in diesem Beitrag das Standardwerk Patterns of Enterprise Application Architecture, kurz EAP, von Martin Fowler zitieren. Im Gegensatz zu den GoF-Patterns aus meinem ersten Beitrag zu Patterns sind die EAP-Patterns recht spezifisch mit einer Anwendung verbunden. Im folgenden werde ich eine Auswahl von Patterns aus dem Buch auflisten indem ich die Zusammenfassung aus dem Buch pro Pattern zitieren und wo es für mein Verständis wichtig war habe ich noch Kommentare hinzugefügt. Für Details sei an das Buch verwiesen. Nicht erwähnte Patterns sind nicht weniger wichtig, doch haben sie in meinem Umfeld weniger Bedeutung da die Problemstellung nicht vorhanden ist oder ich diese bis heute durch ein Framework lösen lies.

Domain Logic Patterns
Domain Model: 'An object model of the domain that incorporates both behavior and data.' Wegen Domain Driven Design ist dieses Pattern für mich ein Muss.

Service Layer: 'Defines an application's boundary with a layer of services that establishes a set of available operations and coordinates the application's response in each operation.' Für den Service Layer gibt es die Variation 'Domain Facade' und 'Operation Script'. Da ich den Domain Model-Ansatz bevorzuge habe ich hier auch eine Bevorzugung für die 'Domain Facade'. Die Eigenschaften eines einzelnen Service hat Evans auch hier beschrieben.

Data Source Architectural Patterns
Active Record: 'An object that wraps a row in a database table or view, encapsulates the database access, and adds domain logic on that data.' Dieses Pattern kann zu einer Vereinfachung in der Implementation eines Domain Model führen indem die Datenbankstruktur und die Domain Model-Stuktur gleich gehalten werden.

Data Mapper: 'A layer of Mappers that moves data between objects and a database while keeping them independent of each other and the mapper itself.' Im Gegensatz zum Active Record wird das Mapping hier in den Data Mapper ausgelagert. Der Data Mapper wird in der Regel nicht selbst implementiert da dies ein komplexes Unterfangen ist (z.B kann für ein objekte-relationales Mapping NHibernate verwendet werden). Auch wird ein Data Mapper oft zusammen mit einem Domain Model verwendet. (siehe auch Mapper)

OR Behavioral Patterns
Unit of Work: 'Maintains a list of objects affected by a business transaction and coordinates the writing out of changes and the resolution of concurrency problems.' Unit of Work sollte in der Regel zusammen mit einem Data Mapper implementiert werden.

Identity Map: 'Ensures that each object gets loaded only once by keeping every loaded object in a map. Looks up objects using the map when referring to them. ' Identity Map muss im Zusammenhang mit einem Data Mapper implementiert werden.

Lazy Load: 'An object that doesn't contain all of the data you need but knows how to get it.' Wichtiges Pattern in Zusammenhang mit Data Mapper und Domain Model um zu steuern wann welche Daten von einer Datenquelle geladen werden.

OR Metadata Mapping Patterns
Query Object: 'An object that represents a database query.' Das Specification Pattern ist mit Query Object verwandt. Der Unterschied ist dass das Query Object auf technischer Ebene ein Query beschreibt. Das Specification Pattern selbst kann Begriffe aus der ubiquitous Language verwenden und bilden und ist Teil der Geschäftslogik.

Repository: 'Mediates between the domain and data mapping layers using a collection-like interface for accessing domain objects'. Ich war hier immer unsicher was der Unterschied zwischen einem Repository und einer Factory ist. Aber in Domain Driven Design von Evans wird klar definiert: 'The Factory makes new objects; the Repository finds old objects' und in Applying Domain-Driven Design and Patterns wird dargestellt dass das Repository für die Persistierung von neu erzeugten Objekten zuständig ist. Ein Repository befindet sich auf einer höheren Ebene als ein Data Mapper indem es Query Object oder Specifications auswertet und Objekte mit Hilfe eines Data Mapper erzeugt.

Web Presentation Patterns
Model View Controller: 'Splits user interface interaction into three distinct roles. (View, Controller, Model).' Siehe auch MVP unter welche das Pattern weiter verfeinert dargstellt wird.
Page Controller: 'An object that handles a request for a specific page or action on a Web site'. Ich sehe im Page Controller eine Anpassung des Model View Controller an das Web-Umfeld wo es die View einmal auf dem Client als HTML-Seite und einmal auf dem Server als Server-Seite gibt. Dies ist auf jeden Fall der Grund weshalb im Unterschied zum Model View Controller die View keine Abhängigkeit auf den Page Controller hat. Die clientseitige HTML-Seite hat nämlich eine Abhängigkeit auf den Page-Controller indem diese Web-Requests übermittelt.

Front Controller: 'A controller that handles all requests for a Web site'. Der Controller instanziert abhängig vom Request Commandos welche die weiteren Verarbeitungen vornehmen.

Template View: 'Renders information into HTML by embedding markers in an HTML page.' Das ist wie eine ASP-Serverseite.

Application Controller: 'A centralized point for handling screen navigation and the flow of an application'.

Distribution Patterns
Remote Facade: 'Provides a coarse-grained facade on fine-grained objects to improve efficiency over a network.' Wichtig ist, als Unterschied zum Service Layer, dass die Remote Facade grob-strukturierte Methoden aufweist.

Data Transfer Object: 'An object that carries data between processes in order to reduce the number of method calls.' Kann zusammen mit Remote Facade zur Datenübertragung verwendet werden. In .NET werden so oft DataSet's oder XML-Serialisierte Objekte als DTO über Webservices verwendet.

Offline Concurrency Patterns
Optimisitc Offline Lock: 'Prevents conflicts between concurrent business transactions by detecting a conflict and rolling back the transaction.'

Pessimistic Offline Lock: 'Prevents conflicts between concurrent business transactions by allowing only one business transaction at a time to access data.'

Coarse-Grained Lock: 'Locks a set of related objects with a single lock'. Damit verwaltet ein Optimisitc Offline Lock oder ein Pessimistic Offline Lock die Locks auf höherer Ebene (z.B: Aggregate) um den Lockingaufwand zu verringern und die Konsistenzsicherheit zu erhöhen.

Implicit Lock: 'Allows framework or layer supertype code to acquire offline locks.' Stellt sicher dass das Locking implizit ist und somit vom Client-Code weder vergessen noch umgangen werden kann.

Session State Patterns
Client Session State: 'Stores session state on the client.' Wird z.B von ASP.NET-Seiten als ViewState implementiert. Kann aber auch für alle Arten von Sessions verwendet werden.

Server Session State: 'Keeps the session state on a server system in a serialized form'. Wird z.B von ASP.NET-Seiten auf dem System.Web.HttpContext.Session -Property implementiert.

Database Session State: 'Stores session data as committed data in the database.' Kann bei ASP.NET via Konfiguration für den Server Session State eingeschaltet werden.

Base Patterns
Gateway: 'An object that encapsulates access to an external system or resource.' Vereinfacht ein komplexes externes Inteface und stellt evtl. die Basis zur Verfügung um einen Service Stub anlegen zu können. Noch ein paar Erklärungen: Ein Gateway wird für externe Komponenten geschrieben während eine Facade von einer externen Komponente zur Verfügung gestellt wird. Ein Adapter wiederum ist eine technische Möglichkeit wie ein Gateway umgesetzt werden kann.
Mapper: 'An object that sets up a communication between two independent objects.' Ist komplexer zu implementieren als ein Gateway. Der Mapper kann als Zwischenschicht gesehen werden welche die zwei angrenzenden Schichten aufeinander abbildet. Beide angrenzenden Schichten bleiben dabei unabhängig und nur der Mapper hat eine Abhängigkeit auf diese.

Layer Supertype: 'A type that acts as the supertype for all types in its layer.' Mag ich persönlich nicht da dadurch schnell das Single Responsibility Prinzip verletzt wird. (z.B Domain Model hat Subertype für Persistenz).

Separated Interface: 'Defines an interface in a separate package from its implementation.' Die Konfiguration der konkreten Instanz (welche das Interface implementiert) kann danach von einem dritten, von beiden Packages abhängigen Komponente oder per Plugin zur Laufzeit erfolgen.

Registry: 'A well-known object that other objects can use to find common objects and services.'

Plugin: 'Links classes during configuration rather than compilation.' Dafür gibts heute eine Anzahl Frameworks (z.B Spring.NET). Plugin wird häufig auch im Zusammenhang mit dem Dependency Inversion Principle und Separated Interface verwendet.

Service Stub: 'Removes dependence upon problematic services during testing.'

Record Set: 'An in-memory representation of tabular data.' = DataSet in .NET

Sunday, October 14, 2007

Patterns 2 - Prinzipen

Im zweiten Beitrag zu Patterns möchte ich einige Prinzpien ansprechen welche helfen einen ausgereiften und qualtitativ guten Softwaredesign zu finden. Diesen Prinzipien zu folgen ist nicht immer ganz einfach, aber sie machen schlussendlich den Unterschied zwischen einer längerfristig wartbaren und qualtitativ hochstehenden Software und einem Hack aus.

DRY: Das Prinzip 'Don't repeat yourself' besagt dass in der Programmierung keine Duplikationen und Redundanzen enthalten sein sollen. Dies gilt für den Programmcode, für Datenbankdefinitionen usw... Das befolgen dieses Prinzips erhöht die Wiederverwendung von Programmcode enorm und ist Ausdruck einer sauberen Programmierung schlechthin.

Law of Demeter: Vereinfacht gesagt definiert das Gesetz von Demeter die Regel 'Sprich nur mit deinen engsten Freunden'. Damit wird erreicht dass eine Klasse sich nur an das nächste Umfeld koppelt (die engsten Freunde) und nichts über die Struktur des weiteren Umfelds wissen muss. Das Gesetz definiert formal für die Methode M auf dem Objekt o:
  1. o.M() -> darf auf Methoden von o zugreifen (z.B mit this.X())
  2. o.M(a) -> darf auf Methoden von a zugreifen (z.B mit a.X())
  3. 0.M() -> darf auf Methoden von selbst erzeugten Objekten zugreifen (z.B a = new A() wird in der Methode M() angelegt, dann darf o.M() -> a.X() aufrufen)
  4. o.M() -> darf auf Methoden von direkt aggregierten Objekten zugreifen(z.B a = new A() wird im Konstruktor angelegt, dann darf o.M() -> a.X() aufrufen)
Open/Close Principle: Das Prinzip verlangt dass eine Softwarekomponente (Module, Klasse, Methode usw...) erweitert werden kann ohne dass die Komponente in ihrer inneren Struktur angepasst werden muss. Der Sinn dahinter ist dass eine Komponente ihre Verantwortlichkeit (gemäß Single Responsibilty Principle sollte da nur eine sein) erfüllt ohne sich um die entsprechenden Variationen kümmern zu müssen. Für die Variation sollte die Komponente aber entsprechende Erweiterungpunkte mittels Delegation an Abstrakte Interfaces oder Vererbung durch Abstrakte Methoden gegen aussen zur Verfügung stellen. Auf der anderen Seite sollte die Komponente auch ihre interne Struktur gegen aussen verbergen. Dies ermöglicht dass die Komponente ihre Implementation ändern kann, ohne dass Erweiterung und Variationen davon betroffen wären.

Single Responsibilty Principle: Das Prinzip besagt, dass ein Komponente (Module, Klasse, Methode usw..) genau eine Verantwortlichkeit haben soll. Eine Komponente sei somit nur anzupassen, wenn bezüglich dieser Verantwortlichkeit die Anforderungen ändern. Um dies erreichen zu können müssen folgende wechselwirkenden Aspekte betrachtet werden.

  1. Es sollte eine hohe Kohäsion erreicht werden. Eine Komponente hat ein hohe Kohäsion falls sie eine wohldefinierte Aufgabe abschliessend und vollständig löst (siehe auch Link)

  2. Durch lose Kopplung sollen Komponenten über ein wohldefinierte und möglichst kleine Schnittstelle miteinander verbunden werden

  3. Mit Separation of Concerns sollen die Verantwortlichkeiten in einer Software systematisch aufgeteilt werden. Dabei werden die Verantwortlichkeiten eindeutig an ein Module, Klasse oder Methode übergeben.
Liskov Substitution Principle: "Methoden welche Basisklassen (auch Abstrakte Klassen oder Interfaces) als Parameter haben müssen diese Objekte benutzen können ohne die konkrete Klasse zu kennen". Dies bedeutet für die Überschreibung einer Methode oder die Implementierung einer Methode aus einem Interface:

  1. Vorbedingung dürfen nicht verschärft werden
  2. Nachbedingungen dürfen nicht abgeschwächt werden
  3. Es dürfen nur Klassen von Exceptions geworfen werden welche auch von der Basis-Methode geworfen werden oder als mögliche Exception dort dokumentiert wurde. Es dürfen auch Ableitungen von diesen Exceptions geworfen werden
Eine Verletzung des Liskov Subsitution Principle führt zu einer schlecht funktionierenden Umsetzung des Open/Close Principle da sich die Erweiterung instabil verhalten wird.

Dependency Inversion Principle: Das Prinzip hat zum Ziel einen flexiblen und somit wartbaren Design zu finden. Die Regeln besagen:
  1. Übergeordnete Komponenten (Module, Klasse, Methode usw...) und untergeordnete Komponenten sollten nicht direkt voneinander abhängig sein. Beide sollten nur von Abstraktionen abhängig sein.
  2. Abstraktionen sollten nicht von den Details abhängig sein. Details sollten von den Abstraktionen abhängig sein.

Das Prinzip ist auch für den TDD Entwicklungsansatz enorm wichtig, da dadurch das mocken und stubben von untergeordneten Modulen ermöglicht wird. So gesehen stellt TDD einen flexible Design sicher, da der übergeordnete Code im Unitest mit Mocks und Stubs und in der laufenden Applikation mit den konkreten untergeordneten Modulen arbeitet.

Noch was vergessen?