Luca Morelli
Softwareanalytiker und -entwickler
Teamleiter

Softwareentwicklung: Definition der Architektur oder Arbeit innerhalb einer bestehenden.

Ich habe die Architektur von Projekten definiert, für die ich die technische Verantwortung trug. Die meisten der Projektbeispiele, die hier veröffentlicht sind, beschreiben jedoch Beratungsaufträge, bei denen ich bestehenden Teams und Produkten beigetreten bin. Mein Entscheidungsspielraum unterscheidet sich stark zwischen diesen Situationen, ebenso wie die Art und Weise, wie ich Methoden und Muster anwende.

Wenn ich das System entwerfewähle ich seine Struktur und Technologien basierend auf dem Problem, den Einschränkungen und den Personen, die es warten werden.
Wenn ich einem Produktteam beitreteverstehe ich die bestehenden Entscheidungen, folge den Teamkonventionen und trage innerhalb meines zugewiesenen Rahmens bei.

Zwei Arbeitsweisen

01

Wenn ich die Architektur definiere

Wenn ich für die Gestaltung eines Projekts verantwortlich bin, treffe ich die Architekturentscheidungen und trage die Verantwortung dafür.

Ich beginne mit Anforderungen und Einschränkungen, lege dann Modulgrenzen fest, wähle den Technologie-Stack und entscheide, wie die Teile kommunizieren. Von Anfang an berücksichtige ich, wer das System bauen, testen und warten wird. Ich führe ein Muster ein, wenn es ein echtes Problem löst oder die Anwendung leichter weiterentwickelbar macht.

Entscheidungen, die ich bewerte

  • Modulverantwortlichkeiten und -grenzen
  • Domain-Regeln und Use Cases
  • Persistenz und externe Integrationen
  • Testbarkeit, Releases und Wartung
02

Wenn ich einem bestehenden Produkt beitrete

Bei Beratungsaufträgen sind die Architektur und Richtlinien oft bereits vorhanden.

Meine erste Aufgabe besteht darin, das System zu verstehen: Konventionen, Abhängigkeiten, Flüsse zwischen Diensten und die Gründe für frühere Entscheidungen. Ich analysiere, entwickle und teste die mir zugewiesenen Aufgaben in Abstimmung mit dem Team. Wenn ich ein strukturelles Problem finde, schlage ich eine Änderung vor, die zum Projekt und zu den Verantwortlichkeiten derjenigen passt, die die Architektur besitzen.

Diese Situation spiegelt sich in den meisten Projektbeispielen auf dieser Website wider. Die Seite Arbeitsweise erläutert meine verschiedenen Rollen ausführlicher.

Mein Beitrag

  • Analyse jeder Aufgabe innerhalb des größeren Systems
  • Entwicklung nach den Standards des Teams
  • Testen und Verhindern von Regressionen
  • Vorschlagen technischer Verbesserungen, wenn ein Teil des Systems verbessert werden kann

Backend und Frontend

Ich arbeite seit mehr als fünfundzwanzig Jahren in der Softwareentwicklung. Das Microsoft-Ökosystem ist mein Hauptreferenzpunkt, neben den Webtechnologien, die jedes Projekt benötigt.

Auf der Backend-Seite arbeite ich hauptsächlich mit .NET und ASP.NET Core: APIs, Anwendungsregeln, Daten und Integrationen. Ich bin der Plattform von .NET Framework bis zu den aktuellen .NET-Versionen gefolgt und habe für kleine und mittelständische Unternehmen sowie in Unternehmensumgebungen gearbeitet.

Auf der Frontend-Seite habe ich umfangreiche Erfahrung mit Angular: zunächst AngularJS, dann Angular ab seinen frühesten Versionen. Ich habe auch React in großen Projekten eingesetzt. Das Produkt und das Team bestimmen die Wahl des Tools; in jedem Fall strebe ich an, Schnittstellen zu erstellen, die auch bei wachsender Funktionalität und Teamgröße überschaubar bleiben.

Organisation von Code, wenn ich die Wahl habe

Ich beginne nicht mit dem Namen einer Architektur. Ich beginne mit den Regeln, die der Code ausdrücken muss, und den Änderungen, die er unterstützen muss.

In Modulen mit erheblicher Geschäftslogik trenne ich die Domäne, die Use Cases und die technischen Details. Die Domäne enthält die Konzepte und Geschäftsregeln des Kunden; die Anwendungsschicht koordiniert die Operationen; Datenbanken, Nachrichtenübermittlung und externe APIs sind Integrationsdetails. Dies wendet das zentrale Prinzip der Clean Architecture an: Systemregeln sollten nicht direkt von der Technologie abhängen, die zur Speicherung oder Offenlegung verwendet wird.

Ich organisiere die Arbeit oft nach Funktionen mit einem Vertical Slice Ansatz. Der Endpunkt, die Anfrage, der Handler und die Validierung für einen Use Case bleiben eng beieinander, sodass jemand, der eine Funktion ändert, ihrem Weg folgen kann, ohne entfernte Ordner durchsuchen zu müssen. Clean Architecture und Vertical Slice behandeln unterschiedliche Fragen: die erste betrifft Abhängigkeiten, die zweite die Gruppierung des Codes.

Ich setze Dependency Injection ein, um Abhängigkeiten explizit darzustellen, und trenne Befehle, Abfragen und ihre Handler, wenn dies die Anwendungsfälle klarer macht. Diese Werkzeuge erfordern Maßhalten: zusätzliche Schichten und Abstraktionen können eine einfache Funktion schwerer nachvollziehbar machen.

Web / APIEndpunkte
Controller
AnwendungAnwendungsfälle · Befehle · Abfragen
Integrationsschnittstellen
DomäneEntitäten · Wertobjekte
Geschäftsregeln
InfrastrukturEF Core · SQL · externe APIs
Nachrichtenübermittlung
AnwendungSchnittstellen zur Implementierung
Clean Architecture, vereinfacht: Code-Abhängigkeiten zeigen in Richtung Anwendungs- und Domänenregeln; die Infrastruktur implementiert die von der Anwendung benötigten Schnittstellen.
FunktionenCode ist nach Funktionalität gruppiert
Kunden / ErstellenEndpunkt · Befehl · Handler · Validator
Kunden / AbrufenEndpunkt · Abfrage · Handler
Aufträge / ErstellenEndpunkt · Befehl · Handler · Validator
Vertical Slice: Dateien für eine Funktion bleiben zusammen, anstatt über Ordner nach technischem Typ verteilt zu sein.

Microservices und ereignisgesteuerte Kommunikation

Die Trennung von Modulen innerhalb einer Anwendung und der Einsatz unabhängiger Dienste sind unterschiedliche Entscheidungen.

Ich erwäge Microservices, wenn die funktionalen Grenzen klar sind und ein echter Vorteil darin besteht, die Teile unabhängig weiterzuentwickeln oder zu veröffentlichen. Diese Autonomie hat ihren Preis: Netzwerkkommunikation, Fehlerbehandlung, Überwachung und Release-Koordination. In anderen Kontexten kann eine Anwendung mit gut definierten Modulen geeigneter sein.

Ich habe an Projekten gearbeitet, die Azure Service Bus oder RabbitMQ für die asynchrone Kommunikation genutzt haben. Eine Komponente kann ein Ereignis veröffentlichen, und andere können darauf reagieren, ohne dass der Produzent jeden Empfänger direkt aufruft. Diese Entkopplung erfordert Sorgfalt bei Verzögerungen, doppelten Nachrichten, Fehlern und Nachverfolgbarkeit. In bestehenden Produkten habe ich bei Integrationen geholfen und Abläufe diagnostiziert.

FrontendBenutzeroberfläche
API-GatewayAPI-Eingangspunkt
KundenserviceKundenKunden-Datenbank
BestelldienstBestellungenBestell-Datenbank
BestelldienstVeröffentlicht OrderCreated
NachrichtenbusAsynchrone Lieferung
AbrechnungsdienstReagiert auf das Ereignis
Beispielhafte Darstellung: Dienste haben separate Verantwortlichkeiten und Daten. Einige Abläufe nutzen APIs, während andere asynchrone Nachrichten verwenden. Diese Aufteilung erfordert klare funktionale Grenzen und angemessene Betriebsabläufe.
BestelldienstVeröffentlicht OrderCreated
NachrichtenbrokerAzure Service Bus oder RabbitMQ
AbrechnungBereitet Abrechnung vor
LagerAktualisiert Inventar
BenachrichtigungSendet eine Nachricht
Ereignisgesteuerte Kommunikation, beispielhafte Darstellung: Der Produzent veröffentlicht einmal und interessierte Verbraucher antworten unabhängig. Jeder Verbraucher muss auch Verzögerungen, Duplikate und Ausfälle verarbeiten.

Fachdomäne und Tests

Wichtige Regeln sollten für Personen, die das Produkt kennen, verständlich sein und im Laufe der Zeit testbar bleiben.

Ich wende Domain-Driven Design-Ideen in dem Umfang an, den das Problem erfordert: eine gemeinsame Sprache mit Produktexperten, Grenzen zwischen funktionalen Bereichen und Geschäftsregeln, die dort platziert werden können, wo sie verstanden und getestet werden können. Nicht jede Anwendung benötigt jedes DDD-Muster.

Automatisierte Tests überprüfen das Verhalten und schützen zukünftige Änderungen. Ich wende TDD an, wenn das Schreiben eines Tests zuerst eine Regel oder einen Sonderfall eingrenzt; in anderen Situationen füge ich Tests während der Entwicklung hinzu oder reproduziere einen Fehler mit einem Test, bevor ich ihn behebe. Tests ermöglichen es auch, eine API zu überprüfen, bevor der Client bereit ist. Wo Tests für die Genehmigung eines Pull Requests erforderlich sind, folge ich den Anforderungen des Teams. Eine hohe Abdeckung garantiert jedoch allein keine guten Tests.

Komplexität im Verhältnis zum Problem

Die Architektur sollte einem Projekt helfen zu wachsen, anstatt ein Selbstzweck zu werden.

Ein System, das Ereignisse austauscht, benötigt nicht unbedingt Event Sourcing. Bei Event Sourcing bilden Ereignisse eine dauerhafte Geschichte, aus der der Zustand des Systems wiederhergestellt wird. Dies kann helfen, wenn die Entwicklung eines Prozesses erhalten bleiben muss, aber es erfordert Arbeit rund um die Versionierung, Projektionen und langfristiges Management. Ich würde es nur für einen konkreten Bedarf in Betracht ziehen.

Ich wende das gleiche Kriterium auf Schichten, Muster und verteilte Dienste an: Beginnen Sie mit einer angemessenen Struktur und entwickeln Sie sie weiter, wenn Regeln, Grenzen oder Lasten eine elaboriertere Lösung rechtfertigen. In Projekten, die ich leite, übernehme ich die Verantwortung für diese Entscheidungen; wenn ich an jemandes Produkt arbeite, helfe ich, es innerhalb der Verantwortlichkeiten und Einschränkungen des Teams zu verbessern.

Möchten Sie eine Anwendung entwerfen oder ein bestehendes Produkt weiterentwickeln?

Ausführlicher Lebenslauf und Referenzen auf Anfrage verfügbar.