Hexagonale Architektur mit C#, .Net 6 und Blazor

Hexagonal

Zur Strukturierung von Software existiert eine Vielzahl von Architekturmustern. Unter dem Begriff Clean Architecture lassen sich mehrere davon zusammenfassen, welche sich zwar im Detail unterscheiden, beim Ziel allerdings auf das gleiche setzen: Trennung von Verantwortlichkeiten. Im Ideal wird dadurch eine Software erreicht, die sich langfristig einfach warten und erweitern lässt. In diesem Artikel möchte ich neben den allgemeinen Prinzipien der Clean Architecture ebenso anhand des Architekturmusters “Hexagonale Architektur“ ins Detail gehen. Über ein Praxisbeispiel werden wir einige Vor- und Nachteile dieser Vorgehensweise sehen. Das Beispiel verwendet C# / ASP.Net Core im Backend und APS.Net Core Blazor Webassembly im Frontend.

Weiterlesen …

RingBuffer in C#

In .Net existiert eine Vielzahl von Klassen zur Auflistung von Objekten oder Strukturen. Ein einfaches und häufig verwendetes Beispiel ist die Array. Die List<T> ist ebenso stark verbreitet. Daneben gibt es eine Vielzahl von Klassen, welche diverse Spezialfälle abbilden. So etwa für parallelen Zugriff mit den Klassen aus dem Namespace System.Collections.Concurrent. Einen Spezialfall hätte ich allerdings schon häufiger vermisst: Den RingBuffer (oder auch Circular Buffer [1]). Ein RingBuffer dient dazu, eine fortlaufende Liste mit einer maximalen Länge abzubilden. Wenn über die maximale Länge hinaus geschrieben wird, so werden einfach die ältesten Elemente überschrieben. Gehen wir von der max. Länge von 100 aus. Wenn das 101. Element hinzugefügt wird, fliegt das erste Element raus. Dieses neue Element ist dann aber nicht der neue Start der Liste. Der Start der Liste ist immer das älteste Element. Wo benötigt man so ein Verhalten? Ein gutes Beispiel dazu habe ich in der 3D-Engine SeeingSharp. Zur Berechnung der durchschnittlichen FPS (Frames per Second bzw. Bilder pro Sekunde) wird der aktuelle FPS Wert je Sekunde berechnet und an einer Liste angehängt. Nachdem diese Liste eine bestimmte Länge erreicht hat, wird mit jedem neuen Wert der älteste Eintrag entfernt. Die durchschnittlichen FPS ergeben sich anschließend mittels des Durchschnittswerts aller Elemente in der Liste. Mit einer List<T> könnte man dieses Verhalten per Add und RemoveAt(0) relativ einfach abbilden, bei einem RingBuffer dagegen steckt dieses Verhalten bereits im Add. Dazu gibt es beim RingBuffer noch weitere Vorteile aus Sicht der Performance, doch dazu im Verlauf dieses Artikels mehr.

Weiterlesen …

Testautomatisierung beim Parsing von Dokumenten

Testautomatisierung

Als Entwickler steht man regelmäßig vor der Aufgabe, Dokumente mit einem bestimmten Format zu lesen und weiterzuverarbeiten. Ebenso kommt es immer wieder vor, dass es für das gewünschte Dokumentenformat noch keinen passenden Parser gibt. Daraus entsteht die Aufgabe, selbst einen kleinen Parser zu entwickeln. Gesagt, getan. Bei Xml- oder Json-Dokumenten etwa ist das i. d. R. auch relativ schnell erledigt. Nicht selten gibt es aber dennoch verschiedene Besonderheiten, die es zu beachten gilt. Etwa bestimmte Elemente in der Datei, die nicht immer enthalten sind. Nehmen wir als Beispiel ein Dokument, welches einen Kassenbon abbildet. Standardmäßig stehen alle Artikel drin, welche der Kunde gekauft hat. Es gibt allerdings weitere Fälle, wie Artikelrückgaben (Pfand), Stornierung einzelner Zeilen durch das Kassenpersonal, rabattierte Artikel etc. Schnell ergibt sich eine längere Liste von Fällen, die das Parsing und die anschließende Weiterverarbeitung beeinflussen. Aus diesem Grund kann es eine gute Idee sein, möglichst viele Teile davon per Testautomatisierung abzusichern.

Weiterlesen …

Überwachen von Coding Conventions per Roslyn Analyzer

GPXviewer with Roslyn Analyzer

Das Projekt RK GPXviewer ist ein Modulith in Form einer Desktop Applikation. Das heißt, dass das Programm in mehrere lose gekoppelte Module aufgeteilt ist. Jedes Modul hat hierbei eine öffentliche Schnittstelle und eine nur innerhalb des Moduls sichtbare Logik. Diese Trennung zwischen öffentlicher Schnittstelle und privaten Logikklassen ist hierbei eine strenge Regel. Sie soll sicherstellen, dass das Geflecht an Modulen auch in Zukunft sauber wartbar und erweiterbar bleibt. Nur wie lässt sich die Einhaltung einer solchen Regel am besten sicherstellen? Alle Module befinden sich in der gleichen Projektmappe, eine Regelverletzung wirkt geradezu einladend. Ist eine notwendige Methode oder Eigenschaft gerade nicht in der Schnittstelle enthalten, ist es relativ einfach, ohne Umwege direkt auf die Logik-Klassen zuzugreifen. Man muss lediglich etwas von privat auf öffentlich umstellen – merkt schon keiner. Oder einen Verweis hinzufügen, merkt auch keiner… Damit das nicht passiert, lassen sich für solche Regeln Roslyn Analyzer schreiben. Diese prüfen den Code während des Compile-Vorgangs und geben nach Wahl direkt Fehler oder Warnungen aus. In diesem Artikel möchte ich auf den Roslyn Analyzer eingehen, welchen ich für RK GPXviewer zur Einhaltung obiger Regel umgesetzt habe.

Weiterlesen …

Prism als Basis von modern strukturierten Applikationen

RK GpxViewer

Auf Prism wurde ich zum ersten Mal bei einem Vortrag bei der .Net Usergroup Regensburg aufmerksam – vor locker 10 Jahren. Zunächst wusste ich gar nicht, um was es sich genau handelt. Nach dem Vortrag und weiterer Recherche hat Prism aber die Art und Weise, wie ich Desktop- und Mobile-Applikationen strukturiert habe, maßgeblich beeinflusst. Ein wichtiger Punkt für mich damals war die Aufteilung einer großen Applikation in mehrere, lose miteinander gekoppelte Module und die dafür bereitgestellten Best Practices. Prism folgt dabei den Grundsätzen des MVVM-Patterns und erweitert dieses um weitere Werkzeuge wie CompositeCommand, dem bereitgestellten EventAggregator oder eben Basisklassen für Module. Zusätzlich wird ein DI-Container integriert. Für die Desktop-Applikation RK GPXviewer [1], welche ich gerade für die Planungen meiner Wander- und Fahrrad-Touren im Sommer baue, verwende ich die aktuelle Version von Prism und möchte hier in diesem Artikel einige Erfahrungen damit teilen.

Weiterlesen …

Mit Null umgehen

Durch den Artikel “Result-Pattern statt Exceptions” im Windows Developer Magazin [1] bin ich auf die Library Functional Extensions for C# [2] gestoßen. Zunächst fand ich das Result-Pattern auch als sehr interessanten Weg, mit verschiedenen erwarteten Fehlern umzugehen und wollte das direkt mal ausprobieren. Die verwiesene Library macht aber einiges mehr, für mich primär interessant ist das Problem mit den Null-Referenzen.

Weiterlesen …

Parameterprüfungen zur Laufzeit

Programmierfehler sollen auffallen, und zwar so bald wie möglich! So banal, wie dieser Satz klingt, so leicht drückt man sich um das Kernthema herum. Ich kann mich noch gut daran erinnern, wie ich früher mit den Gedanken entwickelt habe: Auslösen von Exceptions vermeiden, Aufpoppen von Fehlermeldungen vermeiden, … Getrieben von diesen Vorsätzen programmiert man Methoden beispielsweise so, dass im Fehlerfall ein Default-Wert zurückgegeben wird, welcher sicherstellen soll, dass das Programm halt doch noch irgendwie weiterläuft und niemand etwas merkt. Im Extremfall tauchen leere Catch-Blöcke auf oder es wird (wenigstens) in eine Protokolldatei reingeschrieben, in die sowieso nie jemand reinschaut. Zunächst einmal hat es ja etwas Gutes, dass Programm stürzt nicht ab, wirkt sogar stabil. Aber… irgendwann wird man von der Zeit eingeholt und man hat ein echtes Problem, die tatsächlichen Fehler im Hintergrund zu finden und auszumerzen.

Weiterlesen …

Der Messenger in Seeing#

Messenger

In Vorbereitung für ein neues Spiel habe ich heute die Kern-Logik von Seeing# an ein paar Stellen erweitert. Wichtigster Punkt dieser Tage ist der Messenger, welcher in den Klassen von Seeing# als SeeingSharpMessenger bezeichnet wird. Ursprünglich habe ich mich bei dem Konzept etwas vom EventAggregator des Prism-Frameworks inspirieren lassen [1]. Grundsätzlich geht es darum, eine gemeinsame Klasse zu haben, an der sich Ereignis-Empfänger registrieren können, um so Ereignisse von beliebigen Quellen der Anwendung zu empfangen, ohne diese Quellen selbst zu kennen. Bei Seeing# habe ich dieses im Grunde sehr einfache Prinzip hauptsächlich dahingehend erweitert, damit es besser mit verschiedenen Threads innerhalb des Programms umgehen kann.

Weiterlesen …