fotolia 2605482 s 870x252

Wie Quelltext in einer modularen, erweiterbaren .NET Anwendung strukturieren?

struktur modulare erweiterbare visual studio solution
Es gibt viele Möglichkeiten Ordner-Struktur einer modularen .NET-Solution zu entwerfen. Wichtig ist allerdings den festen Rumpf von den optionalen Modulen zu trennen und die Abhängigkeiten zwischen den Anwendungskomponenten maximal zu reduzieren.

Bei der Erstellung von Ordner-Struktur soll immer die Länge der Dateinamen berücksichtigt werden. Ist die Ordner-Struktur zu verschachtelt, verweigert das Windows Dateisystem oder TFS seinen Dienst.

Es ist ratsam alle Projekte und alle Solutions auf derselben Ebene in der Verzeichnisstruktur zu platzieren. So laden Solutions ihre NuGet-Pakete immer in dasselbe Verzeichnis "Packages" im Ordner wo sich die Solutions befinden. Die Projekte können wiederum von einem Ort an einen anderen in derselben Ebene verschoben werden, ohne ihre Referenzen zu brechen.

Die Namespaces sollen möglichst kurz sein, z.B alle Projekte die mit "UserManagement.Client..." beginnen sollen diesen Namespace haben. Beim Refactoring kommt immer wieder die Frage vor - gehört diese Klasse zu diesem Projekt? Dank den einheitlichen Namespaces können Klassen zwischen Projekten einfach verschoben werden ohne Referenzen an allen Stellen anpassen zu müssen, wo die gegebene Klasse benutzt wird.

Die definierte in der Solutiondatei Ordner-Struktur ist virtuell. Gegenüber dem Dateisystem von Windows muss hier die Länge der Dateinamen nicht geachtet werden. Die Projekt-Verschachtelung kann dadurch wesentlich granularer werden. Es ist auch praktisch mehrere Solutions für dieselben Projekte zu erstellen um unterschiedliche Projekt-Strukturen nach Bedarf zu präsentieren. Die Solutions sind hier als Masken oder Context zu verstehen. Die Projekt-Struktur kann aus Sicht eines Testers komplett anders aussehen als die Struktur aus Sicht eines Entwicklers.
Oft werden Solutions erstellt, die nur einen Teil des gesamten Systems abdecken - z.B. nur eine einzelne Exe-Anwendung. Dieses Vorgehen beschleunigt Kompilierung und fokussiert den Entwickler auf die ausgewählten Projekte.


Modulares System
Startpunkt eines modularen Systems ist der Host. Es ist eine EXE-Datei. Host besteht aus dem Kern und Modulen. Zur Entwurfszeit hat Host keine Kenntnisse von den implementierten Modulen. Er referenziert lediglich die Modul-Kontrakte. Die Kontrakte definieren Funktionalitäten - welche Funktionen kann er anbieten. Die aktuelle Modul-Implementierung wird erst zur Laufzeit über Dependency-Injection dem passenden Kontrakt zugeordnet.

.NET Framework bietet in der Assembly System.ComponentModel.Composition MEF - Microsoft Extensibility Framework. MEF kann beim Anwendungsstart beliebige Verzeichnisse nach Assemblies durchsuchen und aus der Menge passende zu den angeforderten Modul-Kontrakten Implementierungen auffinden und den vorgesehenen Kontrakt-Platzhaltern zuweisen. Mehr Info über MEF hier:

Managed Extensibility Framework (MEF)


Core
Enthält grundlegende Funktionalität die von allen Systembestandteilen (Hosts, Module u.a. Kern-Komponente) genutzt werden kann. Core wird nicht über Kontrakte abstrahiert - Core ist die pure Funktionalität. Die Core-Assemblies werden fix in anderen Projekten referenziert.

Core.cs
Seine Zuständigkeit ist Erweiterungsmethoden, Converters, Formatters, Helpers, und andere gemeinnützige Tools bereitzustellen.

Core.Configuration.cs
Zuständig für das Laden/Speichern der Konfiguration. Kann ausgereifte Logik für die Verwaltung von Local bzw. Roaming Konfiguration enthalten.

Core.Logging.cs
Ein universelles Log, mit keiner bestimmten Log-Technologie verbunden. Enthält an die Bedürfnisse der aktuell entwickelten Anwendung optimierte Protokollierungsmethoden.

Core.Logging.Log4Net.cs
Ein Wrapper um Log4Net um Protokolle in die verwalteten von Log4Net-Dateien zu speichern. Log4Net ist nur ein Beispiel - die Anwendung kann natürlich weitere Module mit Unterstützung von .NET Tracing, SeriLog o.a. enthalten.

Core.UI.cs
Allgemeine UI Funktionen, Umrechnung von virtuellen Pixeln auf die reale Bildschirm-Auflösung, Caching, Buffering, Convertierung etc.

Core.UI.Wpf.cs
Gemeinsam genutzte Benutzer-Dialoge, visuelle Nachrichten, Fehlerpräsentierung und andere gemeinsam genutzte visuelle Komponente. Hier können Container für die visuellen PlugIn Systeme enthalten sein.


Host
Es ist eine EXE-Anwendung. Host referenziert Module ausschliesslich über ihre Kontrakte. Die Modul-Implementierungen werden über Dependency Injection zur Laufzeit der Anwendung aufgelöst und erstellt.

MyProductServerConsoleHost.cs
Eine Console-Anwendung für das Hosting des Servers.

MyProductServerWindowsServiceHost.cs
Ein Windows-Service für das Hosting des Servers.


Modules
Module sind nicht selbstständig lauffähig. Es sind Dll-Dateien. Sie werden über ihre Kontrakte (Contracts) in anderen Projekten referenziert. Die Modul-Implementierung wird erst zur Laufzeit über Dependency Injection aufgelöst.

Mehr detailierte Module können mehr allgemeine Module direkt referenzieren. So kann Client.UI.WPF Kenntnisse von Client.UI sowie Client haben aber nicht umgekehrt.

UserManagement.Client.Contracts.cs
Enthält Interfaces, Datenklassen und Modelle die von anderen Projekten referenziert werden. Der Name des Projektes mit der Modul-Implementierung wird mit dem Suffix .Contracts erweitert.

UserManagement.Client.cs
Implementierung des Moduls für die Benutzer-Verwaltung aus Sicht des Clients. Hier werden Anwendungsservices SOA implementiert.

UserManagement.Client.UI.cs
Backend und Hilfsmethoden der visuellen Komponenten ohne Bindung an eine bestimmte UI-Technologie. Es können Converters, Parsers, Formatters, Validators, Controllers o.a. sein.

UserManagement.Client.UI.Wpf.cs
WPF-gebundene visuelle Komponenten. Das Projekt referenziert System.Windows.

UserManagement.Client.Tests.cs
Unit-Tests. Der Name des zu testenden Projektes wird um den Suffix .Tests erweitert.


Resources
Enthält gemeinsame Ressourcen wie Bilder, Dokumente etc. Die Rich-Text Dokumente bzw. PowerPoint Präsentationen können in den Quelltext-Kommentaren referenziert werden und diese somit mit multimedialen Möglichkeiten erweitern.


Zusammenfassung
Die o.g. Struktur ist nur ein Vorschlag und kann an eigene Bedürfnisse adaptiert werden. Wichtig ist nur Rücksicht auf die Punkte die ich am Anfang des Artikels erwähnt habe.