Foundations for Professionals .NET Professionals im Profil guide to C# guidgen.de

Blog

Nutze den Augenblick
und teile der Welt mit, was Du zu sagen hast.

Welchen Nutzen bieten EBCs?

Donnerstag, 29. Juli 2010, 07:57 Uhr
Permalink | Kommentare (4) | Kommentare als RSSRSS

Gestern habe ich über meinen ersten Eindruck von Event-Based Components (EBC) geschrieben. Mein Fazit war sehr positiv – mit dem einzigen Kritikpunkt, dass sich EBCs noch sehr ungewohnt anfühlen und bislang bewährte und etablierte Best Practices fehlen.

Worum geht es bei EBCs überhaupt? Prinzipiell hat Ralf Westphal dies bereits in seinem Blogeintrag EBCs – Der nächste Schritt der Komponentenorientierung? beschrieben. Doch da ich gestern in Reaktion auf meinen Blogeintrag gefragt wurde, ob ich Sinn und Zweck von EBCs noch einmal in meinen Worten erklären könnte, will ich dieser Bitte gerne nachkommen.

Das bisher favorisierte Verfahren zur komponentenorientierten Entwicklung von Software basiert im Wesentlichen auf der strikten Trennung von Implementierung und Kontrakt, so dass Komponenten austauschbar werden – sofern sie den gleichen Kontrakt erfüllen.

Da Komponenten in der Regel nur im Verbund in der Lage sind, eine gestellte Aufgabe zu bewältigen, müssen diese dabei irgendwie miteinander in Kontakt treten. Hierfür enthalten Komponenten Referenzen auf andere Komponenten, von deren Funktionalität sie abhängen. Diese Referenzen verweisen dabei stets nur auf die Kontrakte und nicht auf die konkrete Implementierung.

Um eine Abhängigkeit aufzulösen, muss dann zur Laufzeit entschieden werden, welche konkrete Komponente instanziiert werden soll – wofür üblicherweise die Idee eines Kernels eingesetzt wird, wie beispielsweise in Form eines Dependency Injection-Containers wie LightCore.

So weit, so gut.

Das Problem bei diesem Ansatz zur komponentenorientierten Entwicklung besteht nun darin, dass sich die Reduktion von Komponenten auf ihre Kontrakte zwar positiv auf ihre Austauschbarkeit auswirkt – nicht jedoch auf ihre Unabhängigkeit. Komponenten sind zwar unabhängig von der konkreten Implementierung einer anderen Komponente – aber funktional sind sie dennoch abhängig, nur eben über den Kontrakt.

Dass dies überhaupt ein Problem darstellt, zeigt die Tatsache, dass es nicht möglich ist, eine Komponente nur für sich und isoliert zu testen. In der Regel müssen für andere Komponenten zumindest Stubs, wenn nicht gar Mocks entwickelt und injiziert werden. Das ist aufwändig – zu aufwändig, könnte man mutmaßen.

Denn Komponenten werden häufig mit Lego-Bausteinen verglichen, die man einfach so aufeinander stecken kann – dem Kontrakt sei Dank – doch so wirklich will sich dieses Lego-Gefühl bei der herkömmlichen komponentenorientierten Entwicklung von Software nicht einstellen: Einem roten Lego-Baustein ist es nämlich egal, ob er für sich alleine genutzt wird, oder in Verbund mit einem grünem, einem gelben oder einem blauen.

Ralf nennt diese Eigenschaft topologieunabhängig, weil Lego-Bausteine eben nicht von ihrer Umgebung abhängen. Und genau diese Eigenschaft der Topologieunabhängigkeit trifft auf klassische Komponenten nicht zu: Sie sind abhängig von ihrer Umgebung, allein schon deshalb, weil sie andere Komponenten benötigen, um ihre volle Funktionalität entfalten zu können.

Wendet man sich von Lego-Bausteinen einem anderen Feld, das der IT ein wenig näher steht, zu, kann man problemlos die gravierenden und relevanten Unterschiede zur komponentenorientierten Softwareentwicklung erkennen: Dem Feld von Platinen und Chips.

Ein Chip verkörpert das EVA-Prinzip in Reinkultur: Er erhält Daten per Stromfluss als Eingabe, verarbeitet diese, und gibt Daten per Stromfluss als Ausgabe wieder nach außen. Einem Chip ist es dabei vollkommen gleich, woher die Eingabedaten stammen oder wohin die Ausgabedaten fließen.

Für die Funktionsweise eines Chips sind nun nur zwei Aspekte wichtig:

  • Pins: Ein Chip verfügt über Ein- und Ausgabepins, über die er mit anderen Chips verdrahtet werden und mit diesen in Kontakt treten kann. Ob dies geschieht – und wenn ja, in welcher Form – beeinflusst die grundlegende Funktionsweise des Chips jedoch nicht.
  • Stromfluss: Den einzigen gemeinsamen Nenner, den zwei Chips aufweisen müssen, damit sie auf funktionsfähige Art miteinander verdrahtet werden können, ist ein gemeinsames Verständnis des Stromflusses. Anders formuliert: Alle an einer Kommunikation beteiligten Chips müssen die gleiche “Sprache” verstehen.

EBCs schließlich sind ein Ansatz, genau dieses Konzept von topologieunabhängigen Komponenten, deren einzige Gemeinsamkeit eine gemeinsame “Sprache” ist, auf Software zu übertragen.

Die Idee dazu ist prinzipiell einfach: Das grundlegende Problem der funktionalen Kopplung von Komponenten basiert auf der Tatsache, dass gegenseitig Methoden aufgerufen werden müssen.

EBCs ziehen aus dieser Tatsache die einzig logische Konsequenz: Wenn diese Art der funktionalen Kopplung vermieden werden soll, dürfen Komponenten keine Methoden anderer Komponenten aufrufen. Statt dessen dürfen sie nur signalisieren, dass sie gerne Daten zur Weiterverarbeitung an andere Komponenten abgeben würden – ob darauf dann eine andere Komponente reagiert oder nicht, betrifft die ursprüngliche Komponente nicht mehr.

In einem Satz zusammengefasst bildet dies den Schlüssel zum Verständnis von EBCs:

Im Gegensatz zu klassischen Komponenten rufen EBCs keine Methoden anderer Komponenten auf, sondern stellen nur den Wunsch nach Weiterverarbeitung ihrer Daten in den Raum.

Damit gewinnt man Topologieunabhängigkeit, denn jede EBC ist von ihrer Umgebung entkoppelt und kann entweder für sich alleine oder transparent im Verbund mit anderen Komponenten genutzt werden. Dass sich die Funktionalität von EBCs damit perfekt per Unittest validieren lässt, liegt auf der Hand. Daher eignen sich EBCs ausgezeichnet auch für die Entwicklung mit 4-Step TDD.

Die einzige verbleibende Frage ist, wie dieses Schema umgesetzt werden kann. Die Antwort ist – nochmals – sehr einfach: Input-Pins entsprechen klassischen Methoden, Output-Pins werden als Events deklariert, woher auch der Name der EBCs rührt.

Passt die Signatur eines solchen Events einer Komponente zur Signatur einer Methode einer anderen Komponente, können beide per Eventbinding von außen miteinander verdrahtet werden – die Komponenten selbst bemerken dies nicht und agieren auch im Verbund ebenso wie sie es isoliert für sich täten.

Dadurch entsteht anders als bei klassischen Komponenten kein Codefluss von direkter Aktion und Reaktion, sondern ein Fluss von Daten: Jeweils eine Komponente verarbeitet Daten und reicht diese danach an eine oder mehrere andere Komponenten weiter.

Auch Rückgabewerte lassen sich auf diese Art realisieren: Statt aus der aufgerufenen Methode einen Rückgabewert zurückzuliefern, wird ein entsprechendes Event ausgelöst – auf das die ursprünglich “aufrufende” Komponente dann entweder reagieren kann oder nicht – je nach Belieben. Der “aufgerufenen” Komponente ist das gleich – sie löst unabhängig von ihrer Umwelt lediglich ihre Events aus.

Dies sind die grundlegenden Ideen von EBCs, im nächsten Blogeintrag wird es dann darum gehen, wie eine konkrete Implementierung einer solchen EBC aussehen und wie diese mit anderen EBCs verdrahtet werden kann.

Erster Eindruck von Event-Based Components

Mittwoch, 28. Juli 2010, 15:47 Uhr
Permalink | Kommentare (0) | Kommentare als RSSRSS

Vor ungefähr fünf Monaten hat Ralf Westphal in seinem Blog die Frage aufgeworfen, ob nachrichtenorientierte Komponenten den nächsten Schritt der Komponentenorientierung darstellen.

Als entsprechendes Architekturmodell hat die sogenannten Event-Based Components (EBC) vorgestellt: Ein einfaches Komponentenmodell mit einigen kompakten Regeln, dessen wesentliches Merkmal des Einsatz von Events als Kommunikationsmedium an Stelle klassischer Methodenaufrufe ist.

Im Prinzip ähnelt das Konzept der EBCs dem von Chips und Platinen: Ähnlich wie diese können auch EBCs zusammengesteckt werden und größere Komponenten bilden. Der Vorteil des Ganzen liegt in einer ausgesprochen geringen Kopplung – effektiv sind die einzelnen EBCs nicht mehr voneinander, sondern nur noch von gemeinsamen Typen abhängig.

Die funktionale Abhängigkeit ist aufgelöst. Die Vorteile liegen auf der Hand: EBCs sind einfacher zu instanziieren, einfach zu komponieren, einfacher zu isolieren und daher auch einfacher zu testen.

So weit, so gut. Die Frage ist, inwieweit dieses theoretisch interessante Konzept in der Praxis funktioniert.

In den vergangenen Tagen hatte ich die Gelegenheit, einen EBC-basierten Prototypen einer bestehenden Anwendung zu erstellen: Die zu erzielende Funktionalität war also vorgegeben, die einzige Aufgabe war also tatsächlich, das Konzept der EBCs in die Praxis umzusetzen.

Inzwischen ist dieser Prototyp so weit gediehen, dass er die ersten funktionalen Anforderungen der ursprünglichen Anwendung erfüllt. Zeit, im Rahmen einer kleinen Retrospektive zurückzublicken und ein erstes Fazit zu ziehen. Die aus meiner Sicht zwei wesentlichsten Aspekte sind:

  • EBCs sind effizient: EBCs ersparen einem die Mühe, eine aufwändige Architektur zu entwickeln – sie implizieren eine kompakte Form. Diese kann in technischer Hinsicht auch leicht umgesetzt werden – auch oder erst recht mit TDD. Die geringe Kopplung wirkt sich hierfür enorm positiv aus. Die Erweiterbarkeit des gesamten Systems ist beeindruckend, und auch die Wart- und Evolvierbarkeit des Codes können sich sehen lassen. Noch mehr als von TDD alleine wird man durch den Einsatz von EBCs mit TDD gezwungen, sich zuvor fundierte Gedanken über das Design der Komponenten zu machen.
  • EBCs sind ungewohnt: Der größte Makel von EBCs ist – für mich persönlich – derzeit ihre Andersartigkeit. Sie fühlen sich schlicht und ergreifend ungewohnt an, denn die Kommunikation zwischen den Komponenten folgt nicht mehr dem klassischen und zur Genüge gewohnten Aktion-Reaktion-Schema. Statt dessen modellieren EBCs einen Fluss von Daten durch die Anwendung. Die größte Herausforderung in den vergangenen Tagen war, gedanklich immer wieder zu diesem Fluss-basierten Modell zurückzukehren und sich zu überlegen, wie Funktionalität als Fluss implementiert werden kann.

Natürlich ist der zweite Punkt reine Gewohnheits- und Übungssache. Je öfter man EBCs modelliert, desto leichter gelingt dies. Der Einstieg in EBCs ähnelt daher dem Einstieg in TDD in gewissem Sinne – es ist gar nicht so sehr das rein technische Vorgehen, das kann kompakt und übersichtlich veranschaulicht werden, sondern es sind das fehlende Gefühl für EBCs und die fehlenden Best Practices.

Während das Gefühl nur jeder für sich entwickeln kann, indem er sich mit EBCs beschäftigt und die Arbeit mit ihnen ausprobiert, können Best Practices vermittelt werden – falls diese bereits bekannt sind. Hierzu ist es notwendig, einen Konsens oder zumindest verschiedene, fundierte und argumentativ belegte Meinungen zu finden.

Ralf hat zu EBCs seine Sicht der Dinge – ich habe meine. Aus diesem Grund werde ich in den kommenden Tagen viel über meine Erfahrungen und Erkenntnisse bezüglich EBCs schreiben.

Wenn ich eine andere Meinung vertrete oder EBCs anders nutze als Ralf, werde ich dies begründen – nicht, um Ralfs Position zu schwächen, sondern schlichtweg, um einen zweiten Standpunkt darzulegen.

Vielleicht ergeben sich in der Synergie aus Ralfs Sichtweise, meiner Sichtweise und der Sichtweise von allen anderen Verwendern von EBCs ja Best Practices, die es wert sind, weitergetragen zu werden.

EBCs an sich sind dies allemal wert – sie sind ein großartiges Konzept, das mir sehr gut gefällt. Der langfristige Nutzen wird sich noch beweisen müssen, aber so weit ich EBCs bislang beurteilen kann, sind sie definitiv ein wichtiger Schritt in die richtige Richtung für die moderne Softwareentwicklung.

Neue Version von LightCore

Sonntag, 25. Juli 2010, 10:13 Uhr
Permalink | Kommentare (0) | Kommentare als RSSRSS

Am 1. Januar 2010 hat Peter Bucher einen eigenentwickelten Dependency Injection-Container namens LightCore vorgestellt. Heute Nacht, nahezu acht Monate später, ist die neue Version 1.4 erschienen, zudem wurde die Webseite grundlegend überarbeitet, ist nun über die eigene Domain www.lightcore.ch erreichbar und wartet unter anderem mit einem neuen Design auf.

Was ist neu in LightCore 1.4?

Bemerkenswertestes Feature ist nach wie vor die hohe Ausführungsgeschwindigkeit, die andere Dependency Injection-Containern im Vergleich weit hinter sich lässt. Abgesehen von klassischer Objekterzeugung per new-Schlüsselwort ist mir kein schnellerer Weg bekannt, Objekte zu instanziieren.

Doch abgesehen davon gibt es auch noch einige echte Neuerungen, die es durchaus wert sind, einzeln genannt zu werden:

  • Unterstützte Plattformen: LightCore 1.4 steht nunmehr für .NET 4.0, .NET 3.5, Silverlight 3 und das .NET Compact Framework 3.5 zur Verfügung.
  • Collections: LightCore 1.4 ist nun in der Lage, mit Collections umzugehen. Das bedeutet: Werden beispielsweise mehrere Implementierungen von IFoo registriert, wird ein Parameter vom Typ IEnumerable<IFoo> automatisch erwartungsgemäß aufgelöst.
  • Dynamische Argumente: Erwartet eine Komponente die Übergabe von Parametern, so stand bislang nur die statische Angabe dieser Parameter während der Registrierung zur Verfügung. LightCore 1.4 ermöglicht nun auch den Einsatz von dynamischen Argumenten, das heißt, von Argumenten, die erst zur Laufzeit übergeben werden.
  • Verzögerte Instanziierung: Zumindest in der für .NET 4.0 kompilierten Version bietet LightCore 1.4 Unterstützung für den in .NET 4.0 eingeführten Lazy<T>-Typ, mit dessen Hilfe die verzögerte Instanziierung von großen Objekten – quasi on Demand – möglich wird.
  • Property Injection: Zwar ist Property Injection auch in LightCore 1.4 nach wie vor nur optional verfügbar – sie ist dafür aber um einiges schneller geworden. Wer also auf Property Injection nicht verzichten kann, erhält zumindest eine deutlich beschleunigte Ausführung.
  • Lizenz: Endlich ist auch die Frage geklärt, unter welcher Lizenz LightCore verfügbar ist. Prinzipiell gilt: Im Binärformat darf LightCore in Projekten jeglicher Art genutzt werden, als Quellcode steht LightCore unter der Ms-RSL für Referenzzwecke zur Verfügung. Änderungen wie auch abgeleitete Werke sind nicht gestattet.

Darüber hinaus enthält LightCore 1.4 noch zahlreiche weitere kleine Neuerungen – wie beispielsweise eine vollständige Abdeckung durch Unittests, im Vergleich zu Version 1.0 deutlich aufgeräumteren und besser refaktorisierten Quellcode und eine bessere Fehlerbehandlung.

Erwähnenswert ist allerdings auch noch ein Breaking Change im Vergleich zu Version 1.0: Benannte Registrierungen sind entfallen. Damit war es möglich, auf eine einzige Schnittstelle verschiedene Implementierungen zu registrieren, die nur durch ein händisch vergebenes Tag unterschieden werden konnten. Zur Ausführungszeit konnte dann mittels dieses Tags angegeben werden, welche konkrete Implementierung instanziiert werden soll.

Die Begründung, warum dieses Feature in LightCore 1.4 entfallen ist, findet sich in meinem Blogeintrag Welche Bedeutung wohnt einer Schnittstelle inne?. Für mich persönlich stellt dieser Breaking Change einen Schritt in die richtige Richtung dar, weshalb ich mit damit gut leben kann.

Insgesamt kann ich mein Fazit für Version 1.0 damit auch für Version 1.4 weiter verwenden:

Alles in allem ist LightCore also eine kompakte, performante, flexible und ausgereifte Komponente, die für meine Projekte zukünftig die Antwort auf die Frage nach einem geeigneten Microkernel darstellt.

Auch weiterhin bleibt LightCore für mich also der Dependency Injection-Container meiner Wahl.

LightCore

Freitag, 1. Januar 2010, 00:00 Uhr
Permalink | Kommentare (1) | Kommentare als RSSRSS

Vor ungefähr neun Monaten habe ich in meinem Blogeintrag Microkernel im Eigenbau beschrieben, wie mit wenigen Zeilen ein eigener Microkernel unter .NET realisiert werden kann. Auch wenn diese Variante durchaus für einfache Szenarien genügt, bestehen in der Praxis häufig weitere Anforderungen.

Da in der Regel kein eigener Microkernel entwickelt werden soll, bleibt die Wahl der Qual, welcher Microkernel zum einen die eigenen Anforderungen erfüllt und zum anderen aber zugleich dem persönlichen Gusto am ehesten entspricht. Erschwert wird die Wahl dabei durch die Vielzahl der verfügbaren Systeme.

Wie viele andere Entwickler konnte auch ich mich in der Vergangenheit nicht eindeutig für ein einziges System entscheiden:

  • So hat mir autofac zwar mit seinem an C# 3.0 angelehnten Programmiermodell mit umfangreicher Nutzung von Lambdaausdrücken zugesagt, nicht jedoch von der Performance.
  • Unity hingegen gefällt mir ebenfalls von dem verwendeten Programmiermodell, jedoch habe ich mit den Microsoft Application Blocks in der Vergangenheit keine all zu guten Erfahrungen gemacht, weshalb ich bei Unity sehr skeptisch bin.
  • Spring.NET ist als Framework empfehlenswert, wenn nicht nur ein Microkernel, sondern auch Logging, O/R-Mapping, aspektorientierte Funktionen und ähnliches gewünscht werden – für alles andere ist es jedoch zu umfangreich und komplex.

Diese Liste könnte ich noch problemlos fortsetzen und um einige weitere Microkernel ergänzen.

Mit LightCore hat Peter Bucher nun einen weiteren Microkernel entwickelt, der zudem als Dependency Injection-Container dient. LightCore ist vollständig in C# geschrieben, folgt dabei den Richtlinien der Clean Code Developer-Initiative und ist weitestgehend mit Unittests abgedeckt.

Für sich genommen wecken diese Aspekte durchaus Interesse an dem zu Grunde liegenden Code – doch wirklich interessant wird LightCore durch seine Eigenschaften beziehungsweise Fähigkeiten, die LightCore von anderen Microkerneln abheben:

  • LightCore ist leichtgewichtig und besteht im Wesentlichen aus einer 27 KByte großen Assembly. Je nach Anforderungen kommen noch einige Assemblies hinzu, doch über 50 KByte wächst der Platzbedarf in keinem Fall.
  • LightCore ist verdammt schnell: Im Vergleich zu nativem .NET-Code ist die Erzeugung von 100.000 Objektinstanzen in LightCore lediglich um 7% langsamer – autofac bedarf dafür hingegen ein Vielfaches der Zeit.
  • LightCore ist einfach und flexibel zu konfigurieren: Zum einen kann die Konfiguration vollständig im Code erfolgen, zum anderen wird XAML als Konfigurationssprache unterstützt, wobei nahezu beliebige Datenquellen genutzt werden können.
  • LightCore unterstützt die Instanziierung von Objekten nicht nur per Reflection, sondern auch per Lambdaausdruck, wodurch die Performance im Vergleich zu der Reflection-basierten Variante deutlich zunimmt.
  • LightCore kann mit verschiedenen Lebenszyklen von Objekten umgehen: Transiente Objekte können dadurch ebenso erzeugt werden die Singletons, threadaffine Singletons und HttpRequest-bezogene Objekte.
  • LightCore ermöglicht die Registrierung von Typen an Hand eines Kontrakts, eines Namens oder einer Klasse. Zusätzlich können Argumente spezifziert werden, die während der Instanziierung an den jeweiligen Konstruktor übergeben werden.
  • LightCore unterstützt die Gruppierung von Registrierungen, so dass auf einfache Art zwischen verschiedenen Konfigurationen von Registrierungen gewechselt werden kann.
  • LightCore integriert sich in klassisches ASP.NET und ASP.NET MVC, so dass Dependency Injection auch in diesen Technologien ohne zusätzlichen Aufwand genutzt werden kann.
  • LightCore unterstützt das CommonServiceLocator-Projekt, das seinerseits eine Abstraktion über verschiedene Microkernel ermöglicht.

Dieser umfangreichen und bemerkenswerten Featureliste zum Trotz verfügt LightCore über ein ausgesprochen schlichtes API, so dass zur tatsächlichen Verwendung nur einige wenige Zeilen Code erforderlich sind.

Alles in allem ist LightCore also eine kompakte, performante, flexible und ausgereifte Komponente, die für meine Projekte zukünftig die Antwort auf die Frage nach einem geeigneten Microkernel darstellt.

Für die Zukunft wünsche ich LightCore daher alles Gute.

Was gehört in den Businesslayer?

Montag, 17. August 2009, 20:31 Uhr
Permalink | Kommentare (0) | Kommentare als RSSRSS

Vor ziemlich genau einem Jahr wurde ich von Peter Bucher mit der Frage Was ist Architektur? konfontriert. Eine vernünftige und fundierte Antrwort auf diese zunächst einfach erscheinende Frage geben zu können, hat mich damals einige Wochen in Anspruch genommen.

Vorgestern hat mir Peter nun wieder eine solche Frage gestellt, deren Beantwortung aus dem Stegreif zumindest nicht ganz trivial ist:

Was gehört in den Businesslayer?

Des weiteren ergänzt Peter seine Frage um eine Erklärung, inwiefern er überhaupt Diskussionsbedarf sieht:

[…] alles was Redundanzen im Datalayer verursacht (da es mehrere geben kann), gehört dort hin.

Aber ich höre und lese so viel von Geschäftslogik / Businesslogik und verstehe darunter irgendwelche Logik, wie beispielsweise Preisberechung oder Algorithmen die ausgeführt werden müssen.

Auf jeden Fall habe ich noch nie mehr als Validation / Caching und Logging in einem Businesslayer gesehen. Meistens ist es nur Delegation oder die Transformation von einer DataRow zu einem Objekt […]

Unsere – bislang gemeinsam genutzte Sichtweise – war, dass in die Businesslogik all jenes gehört, was weder in die UI noch in die Datenzugriffsschicht gehört. Allerdings ist eine solche Negativdefinition an Hand einer Exklusion nicht besonders tragfähig: Eine Definition an Hand charakteristischer Merkmale des zu inkludierenden Codes wäre bedeutend nützlicher.

Interessant finde ich, dass die von Peter angesprochenen Aspekte Validierung, Caching und Logging zwar durchaus im Businesslayer zu finden sind, allerdings weder explizite Businesslogik darstellen, noch auf diesen beschränkt wären: So kann sich Validierung durchaus auch zusätzlich in der UI und der Datenzugriffsschicht befinden. Gleiches gilt für die beiden anderen Aspekte Caching und Logging.

Schließlich spricht Peter noch die Transformation und Delegation zwischen Objekten der Datenbank und Objekten des Objektmodells an: Korrekt ist, dass solcher Code häufig im Businesslayer enthalten ist – ob dies jedoch die geeignete Stelle für solchen Code ist, wage ich zu bezweifeln.

Wie also würde ich Peters Frage beantworten?

Ausgehend von meiner Argumentation in Was ist Architektur? verfügt eine Anwendung über einen Sinn: Eine Anwendung wird geschrieben, um ein Problem zu lösen. Bezogen auf diesen Sinn sind die beiden Elemente UI und Datenzugriffsschicht zwar notwendig, aber nicht wesentlich, denn im Gegensatz zum Kern der Anwendung sind sie letztlich beliebig austauschbar.

Es spielt keine Rolle für die Lösung des zu Grunde liegenden Problems, ob die UI in WPF, in Silverlight oder in einer schlichten Konsole umgesetzt wurde – ebenso wie es keine Rolle spielt, ob die Daten in einem SQL Server oder einer schlichten XML-Datei abgelegt werden.

Der Kern einer Anwendung hingegen ist unverändlich. Würde man diesen grundlegend verändern, so würde damit implizit ein anderes als das ursprüngliche Problem gelöst; Die Anwendung erhielte einen anderen Sinn.

Insofern kann man den Businesslayer also mit dem Kern der Anwendung gleichsetzen, und der Kern enthält zunächst einmal sämtliche anwendungsbezogene Logik – eben die sogenannte Businesslogik. Diese kann in Form von komplexen Workflows definiert sein, aber auch ganz schlicht und rudimentär in Form einiger weniger if-Abfragen.

Zunächst kann man also festhalten, dass ich mit Peters Formulierung

Aber ich höre und lese so viel von Geschäftslogik / Businesslogik und verstehe darunter irgendwelche Logik, wie beispielsweise Preisberechung oder Algorithmen die ausgeführt werden müssen.

einverstanden bin. Doch warum sieht man so wenig von dieser Logik?

Dies liegt schlichtweg darin begründet, dass viele der gängigen kleineren und mittleren Anwendungen – insbesondere im Web – keine ausgefeilte Logik benötigen! Zahlreiche dieser Anwendungen begnügen sich nämlich mit den üblichen CRUD-Funktionen: Im Grunde stellen sie nur eine visualisierte Schnittstelle zur Datenbank dar, und verfügen daher über keine dedizierten Workflows.

Und die wenigen Workflows, die vorhanden sind, nimmt man dann neben dem Berg an CRUD-Funktionen nicht mehr als eigenständiges Regelwerk wahr.

Auf der anderen Seite führt ein derart minimalistischer Businesslayer dann dazu, dass Aspekte wie Validierung – die im Grunde gar keine Businesslogik darstellen – ebenfalls dort angesiedelt werden. Schließlicht macht man sich über eine weitere Unterteilung des Businesslayers gar keine Gedanken mehr – dieser ist ja bereits so klein.

Der Aufwand, einen übergreifend zur Verfügung stehenden dedizierten Servicelayer zu entwickeln, wird dann gerne gescheut – obwohl er gegebenenfalls die bessere Wahl wäre.

Interessanterweise gibt es auch Aspekte, die durchaus in den Businesslayer gehören, und dort häufig nicht angesiedelt werden: Hierzu zählt beispielsweise – und das mag verwundern – das Transaktionsmanagement. Transaktionen sind schließlich nicht per se kurzlebiger Natur und auf die Datenbank bezogen.

Schlussendlich möchte ich Peter also nicht nur recht geben, wenn er den Businesslayer mit Anwendungslogik gleichsetzt, sondern ich möchte auch eine Lanze dafür brechen, dem Businesslayer an sich mehr Aufmerksamkeit zu schenken, und auch mehr darüber nachzudenken, ob eine weitere Unterteilung des Businesslayers in die eigentliche Logik und weitere Komponenten nicht durchaus Sinn ergibt.

Microkernel im Eigenbau

Freitag, 13. März 2009, 21:17 Uhr
Permalink | Kommentare (9) | Kommentare als RSSRSS

Ende November des vergangenen Jahres habe ich in Architektur lernen empfohlen, sich mit dem Einsatz eines Microkernels zu beschäftigen:

Auch dieses Thema fördert das Denken in Schnittstellen, allerdings auf einer anderen Ebene als die Entwurfsmuster - beziehen diese sich nämlich auf das Klassendesign, bezieht sich der Einsatz eines Microkernels auf das Komponentendesign einer Anwendung.

Außerdem habe ich behauptet, dass es allen bestehenden Microkerneln zum Trotz nicht schwierig sei, einen einfachen Microkernel selbst zu implementieren:

Zwar gibt es einige Projekte wie beispielsweise Unity, die man sich näher anschauen kann, allerdings ist ein einfacher Microkernel in wenigen Zeilen selbst implementiert, wobei der Lerneffekt dann allerdings weitaus größer ist.

Da ich seither immer wieder nach einem Beispiel gefragt werde, beschreibe ich nun hier, wie man einen eigenen Microkernel erstellt. Ich werde mich dabei auf das wesentliche Feature – nämlich die Instanziierung eines konkreten Typen an Hand eines gegebenen Contracts – beschränken, und Aspekte wie Fehlerbehandlung, Performance, Sicherheit und Konfigurierbarkeit außen vor lassen.

Letztlich sind folgende Schritte nötig, um einen eigenen lauffähigen Microkernel zu entwickeln:

  • Erzeugen einer Konfiguration, die das Mapping von Contracts auf konkrete Typen übernimmt. Es bietet sich an, diese Konfiguration in einer XML-Datei abzulegen, da diese erstens dank LINQ to XML mit wenig Aufwand ausgelesen werden kann, und dies zum anderen ermöglicht, die Konfiguration des Microkernels zu ändern, ohne das Projekt neu kompilieren zu müssen.
  • Instanziieren des für einen gegebenen Contract definierten konkreten Typs. Dies geschieht im Wesentlichen mit Hilfe der Activator-Klasse von .NET, wobei so wohl in das Projekt integrierte Assemblies wie auch zusätzliche Assemblies unterstützt werden sollen.

Zunächst muss also eine Konfiguration erzeugt werden, die das Mapping von einem Contract auf einen konkreten Typen übernimmt. Dazu dient folgende Struktur, die das Grundgerüst einer entsprechenden XML-Datei darstellt:

<?xml version="1.0" encoding="utf-8" ?>
<serviceLocator>
  <mappings>
    <mapping contract="" type="" />
  </mappings>
</serviceLocator>

Als einfaches Beispiel soll eine Instanz des Typs MemoryStream im Namensraum System.IO verwendet und auf die Schnittstelle IDisposable gemappt werden.

Hierzu müssen lediglich die Namen der beiden Typen in der XML-Datei angegeben werden, wobei diese vollqualifiziert  – also einschließlich des jeweiligen Namensraums – angegeben werden müssen:

<?xml version="1.0" encoding="utf-8" ?>
<serviceLocator>
  <mappings>
    <mapping contract="System.IDisposable" type="System.IO.MemoryStream" />
  </mappings>
</serviceLocator>

Im nächsten Schritt wird eine statische Klasse ServiceLocator erzeugt, die über die folgenden Methoden verfügt:

  • Einen statischen Konstruktur: Dieser ist dafür zuständig, die Konfiguration aus der XML-Datei auszulesen, und die dort angegebenen Namen der Contracts und Typen in jeweils eine Instanz der Type-Klasse zu überführen, und diese Zuordnungen danach in einem Dictionary abzuspeichern.
  • Eine statische Methode namens GetService: Diese Methode hat als einzige Aufgabe, den konkreten Typ für den angeforderten Contract aus dem Dictionary zu ermitteln, diesen zu instanziieren und entsprechend gecastet an den Aufrufer zurückzugeben.

Insgesamt ergibt sich daraus die folgende Grundstruktur für die Klasse ServiceLocator:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;

namespace CherryFlavored.NET.Core.Kernel
{
    /// <summary>
    /// Represents a service locator.
    /// </summary>
    public static class ServiceLocator
    {
        /// <summary>
        /// Contains the mappings from contracts to types.
        /// </summary>
        private static Dictionary<Type, Type> _mappings;

        /// <summary>
        /// Initializes the <see cref="ServiceLocator" /> type.
        /// </summary>
        static ServiceLocator()
        {
        }

        /// <summary>
        /// Gets an instance for the specified contract.
        /// </summary>
        /// <typeparam name="TContract">The type of the contract.</typeparam>
        /// <returns>An instance that matches the specified contract.</returns>
        public static TContract GetService<TContract>() where TContract : class
        {
        }
    }
}

Um die Konfigurationsdatei auszulesen und das Dictionary entsprechend zu erzeugen, bietet sich – wie oben bereits erwähnt – die Verwendung von LINQ to XML an:

// Load the configuration.
_mappings =
    (from mapping in
         XElement.Load("ServiceLocator.xml").Element("mappings").Elements("mapping")
     select
         new KeyValuePair<Type, Type>(
             Type.GetType(mapping.Attribute("contract").Value),
             Type.GetType(mapping.Attribute("type").Value))).ToDictionary(
        kvp => kvp.Key, kvp => kvp.Value);

Die Instanziierung des konkreten Typs in der Methode GetService ist sogar noch wesentlich einfacher, und kann in einer einzigen Zeile erfolgen:

// Instantiate the type and return it to the caller.
return (TContract)Activator.CreateInstance(_mappings[typeof(TContract)]);

Um nun tatsächlich eine Instanz der Klasse MemoryStream an Hand ihres Contracts zu erzeugen, genügt eine Referenz auf den Microkernel und folgende Zeile:

// Try to get an instance for an IDisposable type.
IDisposable memoryStream = ServiceLocator.GetService<IDisposable>();

Dieser Microkernel kann nun bereits verwendet werden, um Komponenten innerhalb eines Projektes zu entkoppeln. Häufig ist der zu instanziierende Typ aber gar nicht Bestandteil des eigentlichen Projektes, sondern liegt in einer eigenständigen Assembly, die potenziell erst nach dem Kompilieren zu einer bereits erfolgten Installation beigefügt wird.

Alles, was zur Kompilierungszeit in einem solchen Fall zur Verfügung steht, ist der Contract – der eigentliche Typ wird unabhängig davon bereitgestellt, um beispielsweise Addins zu ermöglichen.

Um ein solches Szenario zu unterstützen, muss der Microkernel also eine Möglichkeit bieten, einen Typ auch aus einer Assembly zu laden, die neben dem eigentlichen Projekt losgelöst im Dateisystem liegt, ohne dass sie bereits zur Kompilierzeit verfügbar war.

Das schöne ist: Dieses Szenario wird bereits unterstützt. Alles, was hierfür notwendig ist, ist, in der Konfigurationsdatei neben der Klasse auch noch die enthaltende Assembly anzugeben, wie folgendes Beispiel an Hand der Post-Klasse von BlogEngine.NET zeigt:

<?xml version="1.0" encoding="utf-8" ?>
<serviceLocator>
  <mappings>
    <mapping contract="System.IDisposable" type="BlogEngine.Core.Post, BlogEngine.Core" />
  </mappings>
</serviceLocator>

Mehr ist für einen eigenen Microkernel prinzipiell nicht notwendig. Natürlich empfiehlt es sich, diesen für einen produktiven Einsatz noch an etlichen Stellen zu optimieren und um weitere Funktionen zu ergänzen. Die grundlegende Funktionsweise dürfte aber klar geworden sein.

Architektur lernen

Donnerstag, 27. November 2008, 13:34 Uhr
Permalink | Kommentare (4) | Kommentare als RSSRSS

Von Zeit zu Zeit werde ich gefragt, wie man als Entwickler eigentlich dem Thema Architektur begegnen sollte und wie man einen entsprechenden Einstieg findet. Da es kein allgemein gültiges Rezept gibt, empfehle ich in der Regel die Beschäftigung mit typischen Architekturthemen, an Hand derer ein Einstieg gelingen kann. Dazu gehören unter anderem:

  • Die klassischen Entwurfsmuster. Auch wenn diese letztlich nur ein Werkzeug für den Architekten darstellen, verhilft die Beschäftigung mit ihnen zu einer anderen Denkweise, vor allem hinsichtlich des Nutzens von Schnittstellen. Entwurfsmuster helfen, die eigene objektorientierte Denkweise zu schulen und zu verbessern, was sich wiederum in besser strukturiertem Code niederschlägt. Ein äußerst empfehlenswertes Buch zu diesem Thema ist C# 3.0 Entwurfsmuster von Judith Bishop, das neben den eigentlichen Entwurfsmustern auch deren Einsatz in C# 3.0 beschreibt.
  • Der Einsatz eines Microkernels. Auch dieses Thema fördert das Denken in Schnittstellen, allerdings auf einer anderen Ebene als die Entwurfsmuster - beziehen diese sich nämlich auf das Klassendesign, bezieht sich der Einsatz eines Microkernels auf das Komponentendesign einer Anwendung. Zwar gibt es einige Projekte wie beispielsweise Unity, die man sich näher anschauen kann, allerdings ist ein einfacher Microkernel in wenigen Zeilen selbst implementiert, wobei der Lerneffekt dann allerdings weitaus größer ist.
  • Die aspektorientierte Programmierung. Als Werkzeug empfehle ich hierfür regelmäßig PostSharp mit dem dazugehörigen LAOS-Framework, um einen Einstieg in die Beschäftigung mit den sogenannten Crosscutting Concerns zu erhalten. Als typische Einsatzgebiete für den Anfang dienen in der Regel die Themen Validierung, Sicherheit, Logging und Transaktionen.
  • Der Einsatz von Softwarezellen. Diese sind ein Konzept von Ralf Westphal, mit dessen Hilfe sich die Architektur einer Anwendung auf jeder Granularitätsebene darstellen lässt, und das etliche der gängigen Nachteile des klassischen Schichtenmodells vermeidet. Nähere Informationen hierzu finden sich in Ralfs Blog unter dem Tag Software Cells.

Die meisten Entwickler fragen trotz dieser Fülle an Informationen nach zusätzlicher, empfehlenswerter Literatur. Eine wirklich befriedigende Antwort konnte ich auf diese Frage bislang jedoch nicht geben, da es sich bei Architektur - im Gegensatz zu beispielsweise einer Programmiersprache - um ein ausgesprochen weites Feld handelt, das nicht mit der Lektüre einiger Bücher abgedeckt ist.

Vielmehr erfordert eine gute Architektur vor allem viel Erfahrung, und diese erlangt man letztlich nur durch Learning by Doing. Trotzdem wäre es natürlich hilfreich, zumindest einen Leitfaden mit Fragen an der Hand zu haben, an dem man sich orientieren kann, und der einen in die richtige Richtung denken lässt.

Genau einen solchen Leitfaden hat das patterns & practices-Team von Microsoft nun unter dem Namen Application Architecture Guide bereits in einer zweiten Version veröffentlicht, der kostenlos von der entsprechenden Webseite heruntergeladen werden kann.

In fünf Abschnitten wird detailliert auf verschiedene Themen wie allgemeine Architekturüberlegungen, Dienste, die verschiedenen Schichten, Performance, Sicherheit, RIAs, Webanwendungen und zahlreiches anderes mehr eingegangen. Erfreulicherweise ist der Inhalt größtenteils nicht als Fließtext, sondern als eine Art Checklisten implementiert, was dem Lesefluss und vor allem der praktischen Anwendbarkeit des Guides sehr zu Gute kommt.

Von Entwurfsmustern und Werkzeugen

Freitag, 19. September 2008, 22:32 Uhr
Permalink | Kommentare (0) | Kommentare als RSSRSS

Der österreichische Philosoph und Autor Paul Watzlawick hat folgenden Satz formuliert:

"Wer als Werkzeug nur einen Hammer hat, sieht in jedem Problem einen Nagel."

Diese Aussage begründet in meinen Augen äußerst zutreffend, warum es für Architekten ausgesprochen wichtig ist, Entwurfsmuster zu kennen. Gregor Biswanger hat in seinem Kommentar zu meinem Blogeintrag Architektur = Planen + Entwurfsmuster angemerkt, dass ein gezieltes Denken in Entwurfsmustern mehr schaden als nutzen könne.

Und ich muss zugeben: Gregor hat damit vollkommen recht! Auch ich vertrete den Standpunkt, dass man nicht versuchen sollte, möglichst viele Entwurfsmuster auf Biegen und Brechen in eine Architektur zu stopfen, denn letztlich verfehlt man damit in der Regel sein Ziel und arbeitet daher kontraproduktiv.

Statt dessen sehe ich Entwurfsmuster als Werkzeuge, deren Kenntnis äußerst hilfreich sein kann, deren Einsatz aber auch bewusst und bedacht für jedes Muster individuell entschieden werden muss. Der Bedarf bestimmt also im Idealfall, welches Werkzeug benutzt wird, und nicht die eigene Fähigkeit, das Werkzeug tatsächlich benutzen zu können.

Im Prinzip verhält es sich mit Entwurfsmustern somit wie mit handwerklichen Werkzeugen, wie beispielsweise Hammer, Schraubenzieher und Säge: Um einen Nagel in der Wand zu befestigen, genügt ein Hammer, es ist nicht notwendig, den Schraubenzieher oder die Säge zu verwenden. Trotzdem ist es sinnvoll, auch diese Werkzeuge zu kennen, so dass man nicht versucht, auch eine Schraube mit dem Hammer in die Wand zu treiben.

Allgemein kann man also sagen, dass ein großes Wissen zwar von Nöten ist, um Probleme überhaupt erkennen zu können, dass aber Erfahrung und Weisheit dazugehören, sein Wissen zielgerichtet einsetzen zu können.

Architektur = Planen + Entwurfsmuster

Donnerstag, 11. September 2008, 11:25 Uhr
Permalink | Kommentare (4) | Kommentare als RSSRSS

Vor einigen Tagen habe ich versucht, die Frage Was ist Architektur?, die mir Peter Bucher einige Wochen zuvor gestellt hat, zu beantworten. Peter hat daraufhin meinen Eintrag direkt aufgegriffen und ihn in einem eigenen Eintrag um einen wesentlichen Aspekt ergänzt.

Ein guter Anfang um den Zielen "Qualitativ gute Software" und "Entwickler" Rechnung zu tragen, ist sicher die vorgängige Auseinandersetzung mit den Anforderungen. Also auch mal den Block zur Hand nehmen. Etwas auf Papier bringen, aus verschiedenen Blickwinkeln betrachten und dann erst einen Prototyp zu bauen.

Diesen Sätzen möchte ich mich ohne jegliche Einschränkung anschließen, meiner Meinung nach kann man den Aspekt der Planung für die Architektur nicht genug betonen, er ist sozusagen die Essenz des Ganzen.

Doch warum ist das so? Warum ist eine vernünftige Planung so wichtig für die Softwareentwicklung?

Die Antwort ist simpel: Es liegt daran, dass grundlegende Korrekturen desto teurer werden, je weiter fortgeschritten das Projekt ist. Im Prinzip ist es wie beim Hausbau:

  • Den Grundriss des Hauses auf dem Papier zu ändern, bevor mit dem Bau begonnen wurde, ist simpel.
  • Den Grundriss des Hauses zu ändern, sobald das Fundament gelegt wurde, ist möglich, aber mit Aufwand verbunden.
  • Den Grundriss des Hauses zu ändern, bevor abschließend das Dach aufgesetzt wird, ist extrem aufwändig und teuer.

Genauso verhält es sich bei der Softwareentwicklung: Je früher eine Änderung notwendig ist, desto weniger Code ist direkt (und indirekt!) betroffen und desto weniger langwierige Änderungen sind notwendig. Da jede Codeänderung zudem entsprechenden, eigentlich vermeidbaren, Testaufwand nach sich zieht, wird schnell klar, warum dies so ist.

Es rentiert sich also durchaus, eine Software zunächst grundlegend und detailliert zu durchdenken, bevor man sich an die Implementierung begibt. Besonders nützlich ist natürlich, wenn sich für gewisse Situationen bereits Lösungsansätze oder Vorgehensweisen bewährt haben, die man lediglich erneut einsetzen muss. Dann wird nämlich der Aufwand so wohl für die Planung wie auch für die Entwicklung geringer.

Daraus wird auch ersichtlich, warum Entwurfsmuster für die Softwarearchitektur eine so große Rolle spielen: Sie geben dem Architekten erprobte Werkzeuge an die Hand, um gewisse Aufgaben zu lösen. Für eine gute Architektur ist es zwar nicht ausreichend, Entwurfsmuster miteinander zu kombinieren, aber sie zur Hand zu haben und zu wissen, welches Entwurfsmuster man in welchem Kontext wie einsetzen kann, ist enorm hilfreich.

Außerdem erleichtern Entwurfsmuster die Kommunikation zwischen Architekten und Entwicklern, denn beide Seiten haben damit eine gemeinsame Terminologie, die exakt definiert wurde. So bleibt weniger Raum, in dem Missverständnisse entstehen können, und es lassen sich überflüssige Diskussionen vermeiden.

Häufig werde ich gefragt, wie man beginnen sollte, sich mit Architektur zu beschäftigen. Ich habe dafür zwar kein festes Schema, denn einen großen Teil macht schlichtweg die Erfahrung und die Beschäftigung mit der Materie aus, aber ich rate fast jedem Entwickler, der mich dies fragt, dazu, sich mit Entwurfsmustern auseinanderzusetzen und versuchen, zu verstehen, warum diese so und nicht anders aufgebaut sind.

Im Lauf der Zeit ergeben die einzelnen, zunächst isoliert erscheinenden Entwurfsmuster, dann ein Netz, das sich immer dichter verknüpfen lässt, und man beginnt, die Konzepte hinter den Entwurfsmustern zu verstehen. Ist dieser Punkt erst einmal erreicht, so kann man dies durchaus als einen gelungenen Start in die Welt der Architektur bezeichnen.

Was ist Architektur?

Mittwoch, 3. September 2008, 18:37 Uhr
Permalink | Kommentare (2) | Kommentare als RSSRSS

Von Zeit zu Zeit wird man in seinem Leben mit einer bestimmten Art von Fragen konfrontiert, bei denen jeder - vor allem man selbst - davon ausgeht, dass man diese mit Leichtigkeit beantworten können müsste, aber: Nichts da! Just in dem Moment, in dem man zu einer Antwort ansetzt, fällt einem ein Aspekt ein, den man noch nicht bedacht hat. Und je länger man über die Antwort nachdenkt, desto tiefer steigt man in die verworrenen Feinheiten der Frage ab und kommt nach Tagen zu dem Ergebnis, dass man eigentlich mit dem Nachdenken und all den neu gewonnenen Erkenntnissen noch einmal ganz von vorne anfangen müsste.

Vor einigen Wochen war es bei mir wieder einmal so weit: Peter Bucher, MVP für ASP.NET und ein von mir äußerst geschätzter MSN-Kontakt, hat mich gefragt, wie ich eigentlich den Begriff der Softwarearchitektur definieren würde. Als Architekt, so dachte ich, müsste mir die Antwort bereits vorformuliert auf der Zunge liegen, nur noch darauf wartend, dass ich sie ausspreche. Meine spontane Antwort lautete, dass ich erst einmal einen Tag Bedenkzeit benötigte.

Aus dem einen Tag sind inzwischen einige Wochen geworden, und ich glaube, die Frage inzwischen zumindest nach bestem Wissen und Gewissen als Diskussionsgrundlage beantworten zu können. Ich bin mir sicher, dass ich mindestens einen, garantiert aber eher zahlreiche Aspekte nicht abgedeckt habe, aber eben deshalb stellt meine Antwort auch nur eine Grundlage für eine weiterführende Diskussion dar.

Der Frage von Peter liegt meines Erachtens eine andere, tiefer gehende Frage zu Grunde: Wie schreibt man gute Software? Letztendlich geht es bei der Entwicklung von Software auch in Zeiten von C#, WPF und Silverlight immer noch um das klassische EVA-Prinzip: Irgendwelche Daten werden eingegeben, sie werden verarbeitet und schließlich wieder ausgegeben. Egal, ob es sich um eine kryptische Konsolenanwendung oder eine grafisch aufpolierte und moderne Web 2.0-Anwendung handelt, die in der neuesten Hype-Technologie geschrieben wurde: Fehlt einer dieser drei Aspekte, verliert die entsprechende Anwendung ihren Sinn.

Anders formuliert: Anwendungen sind dazu da, um Probleme zu lösen, wobei das Wort Problem im weitesten Sinne gebraucht wird (auch das Schreiben einer E-Mail an meine Schwiegereltern ist in diesem Zusammenhang ein Problem, obwohl ich sehr nette Schwiegereltern habe). Man könnte also auch sagen, Anwendungen sind dazu da, um Aufgaben zu meistern, die wir ihnen stellen, und die wir ohne sie (zumeist) nicht lösen könnten. Natürlich kann ich an Stelle einer E-Mail auch einen Brief auf einer Schreibmaschine schreiben, aber das hätte dann nichts mehr mit Software zu tun.

Wann ist also eine Software gut? Dann, wenn sie die zu lösende Aufgabe löst, wenn sie also ihre Funktion erfüllt, wegen derer sie überhaupt erst geschrieben wurde. Doch dies ist - wenn nicht falsch - so doch im äußersten Maße unzureichend. Denn die Bereitstellung der Funktionalität ist zwar eine notwendige Voraussetzung, sie ist aber bei weitem nicht die einzige: Neben der Funktionalität spielen zahlreiche Faktoren wie Stabilität, Portabilität, Erweiterbarkeit, Austauschbarkeit, Wartbarkeit, ... eine Rolle - und hier kommt schnell eine weitere Frage ins Spiel: Interessant ist auf einmal nämlich nicht mehr, was gemacht wird, sondern wie es gemacht wird.

Genau das ist der entscheidende Unterschied zwischen Programmierung, Entwicklung und Architektur: Während sich die Programmierung mit dem reinen Was auseinandersetzt, beschäftigt sich Architektur mit dem Wie. Und die Schnittstelle zwischen beidem - quasi die architekturbewusste Programmierung - ist die Entwicklung. Deshalb besteht für mich auch immer ein großer Unterschied zwischen Programmierern und Entwicklern - während erstere nur versuchen, ihren Code irgendwie herunterzuschreiben, denken zweitere auch darüber nach, wie man Code beispielsweise effizient, sicher oder performant gestaltet.

Nun ist es aber auch so, dass es nicht nur einen einzigen Weg gibt, der glücklichmachend ist - das Wie kann also auf viele verschiedene Arten umgesetzt werden. So könnte der Schwerpunkt der Entwicklung beispielsweise auf eine möglichst gute Erweiterbarkeit oder eine möglichst gute Portabilität gelegt werden. Idealerweise sollten alle diese Aspekte gleichermaßen berücksichtigen, aber das ist aus Zeit-, Geld- oder sonstigen Gründen oftmals nicht möglich. Häufig schließen sich bestimmte Aspekte sogar gegenseitig aus. Performance und Plattformunabhängigkeit beispielsweise: Entweder schreibt man performanten, an eine Plattform optimierten und damit gebundenen Code - oder generischen, plattformunabhängigen Code, der dafür nicht mehr das Optimum aus der jeweiligen Plattform herausholt.

Insgesamt ergeben sich daraus viele verschiedene Stile, wie man das Wie leben kann. Und letztendlich führen diese Überlegungen, die für jede Software neu angestellt werden müssen, letztlich zu einer bestimmten Art und Weise der Herangehensweise - eben der Architektur.

Zusammenfassend könnte man also sagen, dass Architektur das vorausschauende Planen einer Software unter Berücksichtigung aller derzeitig und potenziell zukünftig relevanten Aspekte in dem ihnen zustehenden Maß ist, ohne dabei die Funktionalität aus den Augen zu verlieren, auf Grund derer die Software überhaupt erst entwickelt wird.