Showing posts with label Patterns. Show all posts
Showing posts with label Patterns. Show all posts

Saturday, January 24, 2009

Implementing and testing mapping code with AutoMapper

I was thinking some months ago that it would be great to have a property mapper that eliminates tedious mapping code . I'm very happy that Jimmy Bogard and his team just have published a framework called AutoMapper that resolves that issue. I haven't tested it yet but the idea looks promising.

Why would I use AutoMapper?
  • In order to implement better encapsulation and layering with less amount of coding
  • In order to unit-test mapping code using a simple 'Mapper.AssertConfigurationIsValid()'

Thursday, October 16, 2008

Reflecting about how we implement aggregates

How we implement aggregates

Since the beginning of my current project we tried to apply the ideas of structuring our business-logic into aggregates. We started to implement the aggregates using the following guidelines:

  • define an aggregate root and implement it as a public class
  • define the internal classes of an aggregate and implement it as a public class
  • implement a repository that can query and return aggregate roots of this class
  • try to avoid accessing the internals (like traversing from aggregate root to an internal class) from outside the aggregate
  • put as much as possible of the business-logic into the aggregate root

Following this rules we had some sort of guidelines about where to locate the business-logic and when to implement a repository. I think it was worth to have the aggregates. It was a valuable concept when we had to change our application from a 2-tier to a 3-tier architecture.

Nevertheless we always had following problems with aggregates:

  • Most of the time we had code in the presentation-layer that accessed the internals of an aggregate. It appeared often in case where there was an one-to-one data binding. For instance, in a master-detail view, where the master was the aggregate root an the detail an internal entity of the aggregate. (e.g. editing invoice and invoice position)
  • There were relations on the data model that do not comply the aggregate rules. In such a case we often had a nice definition of an aggregate, however there was sometimes a nasty dependency. It happend that an internal entity was depending on another entity in another aggregate. We agreed that this is not bad and that it can be abstracted to a dependency between the two involved aggregates.
And what happened:

  • Since the internals could be accessed, the business-logic begun to spread out into the presentation layer. It's impossible always to have the discipline to comply the aggregate rules and all of us violated it from time to time.
  • We didn't centralize the creational-logic. The test code is often a mess and it deals with setting-up the aggregates as well the internals of an aggregate.
  • A NDepend-analyse revealed that our business-logic assembly will be a serious maintenance problem ('Zone of pain')

Why is our business-logic in the zone of pain?

We generally get closer to the 'zone of pain' (see link for details), when a high number of types in an assembly:

  1. are more concrete than abstract (see Abstractness in the formula)
  2. are used a lot by other assemblies (see Ca in the formula)
  3. do not depend on other types (see Ce in the formula)

Since I can't see any value in implementing business-logic abstract (1.) and our business-logic already depends on a small number of types in other assemblies (2.) the only variable would be to reduce the number of types that are used by other assemblies (3.).

How could we do it different?

We could try to better encapsulate the internals of an aggregate avoiding the problems described above. That means, not allowing to access any internals by another type than the aggregate root. This increases the afferent coupling of the aggregate root, on the other hand eliminates the afferent coupling of the internals. The afferent coupling of the business logic assembly itself would then decrease.

Therefore I transformed one of our aggregates from something like that

// File Invoice.cs
public class Invoice
{
public EntitySet<InvoicePosition> Positions { get; set; }
}
public class InvoicePosition
{
}

to

// File Invoice.cs
public partial class Invoice
{
private EntitySet<Invoice.InvoicePosition> Positions { get; set; }
}
// File InvoicePosition.cs
public partial class Invoice
{
private class InvoicePosition
{
}
}
After completing the refactoring and changing some logic in the presentation layer I got the following code metrics:

  1. The assembly containing the business logic moved a little bit away from the zone of pain (from D=0.577 to 0.575). This is still bad, however the refactoring had a positive effect.
  2. The type-rank for the refactored aggregate root increased from 10.7 to 12. Ca (Afferent coupling) increased from 62 to 66. That means, it got a more important part of the business logic.
  3. The CC (cyclomatic complexity) for the aggregate root increased from 38 to 77, while CC decreased generally for the presentation layer (e.g. in one case from 26 to 16). That means that we put complexity into the business logic. It's a good thing since the complexity generally is easier to test and therefore to maintain in the business logic. It was also possible to eliminate duplicated business logic found in the presentation layer.

Testability and Unittests

As the internals are completely hidden from outside now I test the refactored aggregate with state-based black-box tests. I think that's a reasonable way to do it since the test setup do not depend anymore on the implementation details of an aggregate. I'm not sure if this should be a general testing strategy for aggregates. I think there are more complex cases, where white-box tests would still be needed. The following pictures shows on the left, the test-code before the refactoring and on the right, the test-code after:

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?

Monday, September 24, 2007

Patterns 1 - Gang of Four

Seit ich mich mit Entwurfsmuster, oder eben Patterns wie ich normalerweise sage, beschäftige haben sich einzelne Steine zu einem grösseren Mosaik zusammengefunden. Das Mosaik hat noch viele Lücken, aber ich möche hier alle mir wichtig erscheinenden und von mir verstandenen Patterns zusammenfassen. Dies ist mein aktuelles "Weltbild" des objektorientierten Software-Designs. Noch ein Wort zu meiner Welt. Meine Welt ist die der grösseren datenzentrierten Geschäftsanwendungen, mit einer Rich-Client Benutzeroberfläche.

In der Folge möchte ich in mehreren Beiträge diese Patterns auflisten und zusammenstellen. In einem ersten Beitrag gehts es um die 'Gang of Four - Patterns' welche eine Ansammlung von systemnahen, technisch orientierten Patterns darstellen.

Abstract Factory: Erzeugt eine Familie von Objekte unterschiedlicher Typen ohne dass man bei der Benutzung gegen eine konkrete Familie programmiert. Als Beispiel dient hier eine IDbFactory welche IConnection, ITransaction für den Datenbankzugriff erstellt. IDbFactory kann nun konkrete für SQL-Server oder Oracle implementiert werden ohne dass es für den Benutzer von IDbFactory einen Unterschied ausmacht.

Builder: Abstrahiert den Konstruktionsprozess für komplexe Objekte. Falls erforderlich kann der Konstruktionsprozess für andere Typen wiederverwendet werden. So kann zum Beispiel das abstrakte Erstellen einer Rechnung mit einer Anzahl Rechnungspositionen mit einem abstrakten Builder realisiert werden. Als Varianten für konkrete Builder wäre ein KreditorenrechnungsBuilder und ein DebitorenrechnungsBuilder möglich

Factory Method: Erzeugt ein Objekt wobei die implementierende Subclass über den konkreten Typ des erzeugten Objekts entscheidet. Ist mit dem Abstract Factory-Pattern dadurch verwandt, dass dort eine Familie von Objekten und hier nur ein Objekt erzeugt wird.

Singleton: Stellt sicher, dass eine Klasse nur einmal instanziert ist und global zugegriffen werden kann. Damit kann zum Beispiel eine Infastrukturdienst wie die Protokollierung einfach für alle Programmstellen zugreiffbar gemacht werden. Das Pattern sollte aber sehr bedacht eingesetzt werden, da man sich damit sehr einfache versteckte Abhängigkeiten baut, welche die Flexibiltät und Wartbarkeit von Programmcode stark einschränken.

Adapter: Ermöglicht den Zugriff auf eine Klasse via ein Interface welches die Klasse selbst aber nicht unterstützt. Als Beispiel könnte man damit in einer Windows.Forms-Anwendung Controls über eine Interface IControl ansprechen obwohl die Windows.Forms-Library kein solches Interface unterstützt. Ein ControlAdapter würde dann Control auf IControl adaptieren.

Composite: Ist eine Datenstruktur in Form eines Baumes mit Vater-Kind-Beziehungen. Jeder Knoten im Baum ist wieder ein Composite mit weiteren enthaltenen Composites oder dann ein Leaf (Blatt) ohne weiteren Composites.

Decorater: Damit kann zur Laufzeit ein Objekt dynamisch erweitert werden. Bestehende Methoden können erweitert werden und auf dem Decorater können neue Funktionalitäten für das Objekt realisiert werden. Als Beispiel könnte man damit eine Klasse Strassenverbindung abhängig von der Strassensituation als GesperrteStrasse oder als StrasseMitStau instanzieren was dann das Verhalten der Strassenverbindung zur Laufzeit beeinflusst.

Chain of responsibilty: Ermöglicht dass eine Anforderung oder eine Meldung von einer Anzahl Objekten verarbeitet werden kann, wobei jedes Objekt eine spezifische Aufgabe wahrnimmt und die Meldung an ein nächstes weiterleitet. Als Beispiel kann das Verarbeiten einer Meldung in verschiedenen Schritten angesehen werden: 'Dekomprimieren der Meldung' -> 'Authentizität der Meldung prüfen' -> 'Meldung interpretieren' usw....

Command: Bildet eine Anfrage an ein Objekt ab und erlaubt damit Transport, Protokollierung, Persistierung usw. der Anfrage. Zum Beispiel kann damit ein Command in einer Queue gespeichert und zu einem späteren Zeitpunkt ausgeführt werden.

State: Ermöglicht einem Objekt sein Verhalten abhängig von einem Zustand zu ändern. Die Implementation des Verhalten wird an den State delegiert.

Strategy: Kapselt einen Algorithmus in einer Klasse und ermöglicht damit eine allgemeine Verarbeitung mit spezifischen Strategien zu parametriesieren. So könnte damit die Berechnung einer Strassenverbindung mit der 'KuerzesterWegStrategie' oder mit der 'SchnellsterWegStrategie' parametrisiert werden. Der darunterliegende Berechnungsalgorithmus ist somit unabhängig und kann mit verschiedenen Strategien kombiniert werden.

Template Method: Kapselt Teile eines Algorithmus in Methoden und erlaubt Subklassen diese Teile zu implementieren. Somit realisiert eine Basisklasse das Grundgerüst eines Algorithmus und die Subklassen können einzelne Verarbeitungsschritte verfeinern.

Friday, August 17, 2007

Domain-Driven Design von Eric Evans. Kapitel 1-2

Nachfolgend fasse ich die Kapitel 1-2 vom Buch Domain-Driven Design von Eric Evans aus meiner Sicht zusammen. Folende Patterns zeigten für mich neue Aspekte auf und werden meine Arbeit in Zukunft beeinflussen.

Layered Architecture: In diesem Kapitel werden die verschiedenen Schichten wie 'Infrastructure Layer', 'Domain Layer', 'Application Layer' und 'Presentation Layer' konkret im Zusammenhang mit Domain-Driven Design beschrieben. In Kombination mit dem Pattern Model-View-Presenter habe ich nun eine Idee, wie ein Model darin aufgebaut werden könnte.

Modules: Das Kapitel zeigt mir auf, dass bei der Auftrennung von Modules (Namespaces,Assemblies) eher Grenzen zu wählen sind welche in der Fachdomäne auch vorhanden sind. So macht es zum Beispiel eher Sinn anhand von Begriffen wie 'Kundenverwaltung' oder 'Rechnungmodul' Module aufzuteilen, als anhand von Begriffen wie 'DomainLogic', 'DataServices' usw. Die führt dazu dass die Module eine geringe Kopplung aufweisen und eine abgeschlossene Einheit bilden.

Aggregates: Das Pattern hilft durch seine Regeln das Domänenmodell auf einfache Art und Weise übersichtlich und somit auch wartbar und einfach verständlich zu halten. Hätte ich früher noch über Module nachgedacht um die Kopplung zwischen Teilen des Domänenmodels zu reduzieren, geht das nun mit Aggregates deutlich einfacher und flexibler. Mir hatte immer eine Element im Design gefehlt wo spezielle Verantwortlichkeiten wie z.B Validierung und Konsistenzprüfung untergebracht werden konnten. Dies kann nun einfach im Domänenmodell selbst im Aggregateroot untergebracht werden. Allerdings habe ich auch schon gesehen, dass im Aggregateroot Infrastrukturdienste wie die Persistierung untergebracht wurden. Hier muss man sich aber strikte an die Verantwortlichkeiten des Domänenmodells und somit eines Aggregates erinnern und nur Domänenlogik implementieren.

Factories und Repositories: In beiden Kapitel wird konkret erläutert was die Verantwortlichkeiten von Factories und Repositories sind. Während Factories für das Erstellen von neuen Objekte und neuen Aggregates zuständig sind, behandeln Repositories das Auffinden und Instanzieren von persistierten Objekten und Aggregates. Mir werden diese Kapitel in Zukunft helfen, unabhängig von dem unterliegenden Datenzugriffsmechanismus einen einheitlichen, objektorientierten Design für die Persistenzschicht zu finden.

Saturday, June 9, 2007

Model-View-Presenter (MVP) und seine Geschmacksrichtungen

Neulich hat Jeremy D. Miller in seinem Blog die verschiedenen Ausprägungen des Model-View-Presenter Patterns erörtert. Hier ein kurze Zusammenfassung dessen was mir wichtig erscheint:

Dem MVP liegen im allgemeinen folgende Definitionen zu Grunde:
  • Model ist eine Abbildung dessen was dargestellt wird.
  • View ist die Darstellung.
  • Presenter bringt das Model auf der View zur Darstellung und aktualisiert das Model aufgrund von Benutzereingaben.

The Autonomous View

Das 'Anti-Pattern'. Model-View-Presenter sind in einer Klasse vereint.

Supervising Controller
  • Presenter: Übergibt das Model (oder Teile davon) der View zur Darstellung. Der Presenter wird von der View aufgerufen um auf Benutzereingaben zu reagieren.
  • View: bringt das vom Presenter an die View übergebenen Model zur Darstellung (z.B mit Databinding). Leitet Benutzereingaben an den Presenter weiter.

Die View ist somit direkt vom Modell abhängig.

Passive View
  • Presenter: Kapselt das Model gegenüber der View. Übernimmt das Databinding indem die dargestellten Werte (nicht das Model) explizit der View zur Darstellung übergeben werden. Der Presenter wird von der View aufgerufen um auf Benutzereingaben zu reagieren.
  • View: Stellt die vom Presenter übermittelten Werte dar und leitet Benutzereingaben an den Presenter weiter.

Gegenüber dem Supervising Controller Pattern hängt die View hier nicht vom Modell ab.

Presentation Model
  • Presentation Model: Das Model und der Presenter sind hier zusammengeführt. Das Presentation Model zeigt gegenüber der View die darzustellenden Werte und reagiert falls ein solcher Wert verändert wird. Das Presentation Model wird von der View aufgerufen um auf Benutzereingaben zu reagieren.
  • View: Bindet das Presentation Model direkt für die Darstellung.

Für die Kommunikation zwischen View und Presenter gibt es bei allen Pattern zwei Möglichkeiten:

  1. Direkt: Die View ruft den Presenter direkt und explizit via Methoden auf (z.B im Eventhandler in der View).
  2. Indirekt: Der Presenter abonniert Events welche die View zur Verfügung stellt.

Zur Zeit verwende ich meistens das Passiv View Pattern um eine höchstmögliche Testabdeckung in den Unittests zu erreichen.