Architekturentscheidungen vor der Codegenerierung
Lino generiert schnell viel Code, aber die QualitĂ€t des Ergebnisses hĂ€ngt von den Entscheidungen ab, die vor der Generierung getroffen werden. Durch die Auswahl von Grenzwerten, Projekttyp, Kommunikationsstrategie und akzeptabler technischer Verschuldung wird verhindert, dass anfĂ€ngliche ProduktivitĂ€t in Nacharbeit mĂŒndet.
Verwenden Sie diese Seite als Architektur-Checkliste, um Ihr Team, Ihre DomÀne und Ihren Betrieb aufeinander abzustimmen, bevor Sie erweiterte Services, Module und Funktionen erstellen.
Projekttyp
Vor dem Laufen lino project new, definieren Sie das Ziel des Systems. Ein internes Verwaltungsprojekt, ein mandantenfÀhiges SaaS, ein öffentliches API und eine verteilte Plattform haben unterschiedliche Anforderungen.
- Einfache Anwendung: wenige Kontexte, einmalige Bereitstellung und geringer Bedarf an betrieblicher UnabhÀngigkeit.
- Modularer Monolith: mehrere Module im selben Prozess, mit klaren Grenzen und geringeren Betriebskosten.
- Mikrodienste: separate Services, unabhĂ€ngige Bereitstellung, asynchrone Kommunikation und gröĂere betriebliche KomplexitĂ€t.
- SaaS: erfordert Mieterisolierung, starke Sicherheit, Secrets, Tarifbegrenzung, Beobachtbarkeit und klare Abrechnungs-/Betriebsentscheidungen.
In diesem Beschluss wird auch festgelegt, welche Anliegen am ersten Tag auftreten sollen. Ein SaaS kann den Mieter nicht als verspĂ€tetes Detail behandeln; ein öffentlicher API kann nicht ohne Tarifbegrenzung und klare VertrĂ€ge entstehen; Eine Lösung mit Ereignissen muss ĂŒber Outbox, Idempotenz und Beobachtbarkeit nachdenken. Eine Anwendung mit mehreren Kulturen sollte von Anfang an verstreute Zeichenfolgen vermeiden.
Notieren Sie beim Erstellen der Grundlage die wichtigsten Auswahlmöglichkeiten: unterstĂŒtzte Kulturen, verteilter Cache, asynchrone Kommunikation, Code-Analyzers, Datenbanktyp, Web-App-PrĂ€senz, Authentifizierung, Tenant und Worker. Diese Optionen betreffen Packages, Templates, AppHost, Parameter, Secrets, generierte Projekte und den Aufwand, der fĂŒr eine spĂ€tere Ănderung der Architektur erforderlich ist.
Modulare vs. modulare Monolith-Microservices
Der modulare Monolith ermöglicht es Ihnen, die DomĂ€ne in Module mit klaren internen VertrĂ€gen zu organisieren, wodurch Build, Debug und Bereitstellung einfacher bleiben. Microservices lohnen sich nur, wenn es triftige GrĂŒnde gibt: unabhĂ€ngige Skalierung, separate Teams, stabile GeschĂ€ftsgrenzen oder unterschiedliche betriebliche Anforderungen.
Mit einem modularen Monolithen zu beginnen bedeutet nicht, die Architektur zu ignorieren. Es bedeutet, eine einfache Bereitstellung beizubehalten, wĂ€hrend die DomĂ€ne noch entdeckt wird, aber interne Grenzen zu schaffen, die verhindern, dass alles zu einer einzigen Masse von EntitĂ€ten, Servicesn und gemeinsam genutzten Tabellen wird. Das ist der Unterschied zwischen âeinfach beginnenâ und âohne Struktur beginnenâ.
| Kriterium | Modularer Monolith | Mikrodienste |
|---|---|---|
| Einsetzen | Am einfachsten, normalerweise einzigartig | UnabhÀngig vom Service |
| Konsistenz | Einfachere Verwaltung lokaler Transaktionen | Erfordert Ereignisse, Outbox und eventuelle Konsistenz |
| Betrieb | Geringere Infrastrukturkosten | Mehr Beobachtbarkeit, Warteschlangen, Wiederholungsversuche und Automatisierung |
| Refactoring | Am Anfang gĂŒnstiger | Schwieriger wird es, wenn VertrĂ€ge bereits veröffentlicht sind |
Lino unterstĂŒtzt beide Pfade. Die Entscheidung muss sich an der DomĂ€ne und der operativen KapazitĂ€t des Teams orientieren, nicht nur an den technischen Vorlieben. Microservices erfordern Bereitstellungsautomatisierung, verteilte Observability, Vertragsversionierung, Netzwerkfehlertoleranz, Messaging, Wiederverarbeitung und Datenverwaltung. Wenn das Team dies noch nicht sicher betreiben kann, erhöht eine zu frĂŒhe Verteilung oft das Risiko.
Verwenden Sie modulare Monolithen, wenn es vorrangig darum geht, die DomĂ€ne zu erlernen, das Produkt zu validieren und den Betrieb kontrollierbar zu halten. Nutzen Sie Microservices, wenn betriebliche UnabhĂ€ngigkeit bereits ein konkreter Bedarf ist: separater MaĂstab, Teams mit echten EigentĂŒmern, unabhĂ€ngiger Release-Zyklus oder inkompatible Infrastrukturanforderungen zwischen Teilen des Systems.
Leistungs- und Modulgrenzen
Ein gutes Limit reduziert die Kopplung und macht klar, wer der EigentĂŒmer jeder Regel ist. Vermeiden Sie die Erstellung von Servicesn pro Tabelle oder pro Bildschirm. Services und Module mĂŒssen GeschĂ€ftsfĂ€higkeiten darstellen.
- Katalog: Produkte, Kategorien, kommerzielle Informationen und vom Vertrieb konsultierte Daten.
- VerkĂ€ufe: Bestellungen, Warenkorb, angewendete Preise, SchnappschĂŒsse und kommerzieller Ablauf.
- Aktie: VerfĂŒgbarkeit, Reservierungen und Bewegungen.
- Sicherheit: Benutzer, Rollen, Berechtigungen, Authentifizierung und Token.
Ein guter Grenzwert hat seine eigene Sprache, Regeln, die sich gemeinsam Ă€ndern, Daten unter eindeutigem Eigentum und explizite VertrĂ€ge zur Kommunikation mit anderen Kontexten. Ein fehlerhafter Grenzwert erscheint normalerweise als âgemeinsamesâ Modul, als eine Einheit, die von mehreren Bereichen gemeinsam genutzt wird, als eine von allen konsultierte Tabelle oder als ein Dienst, der nur erstellt wurde, weil es einen neuen Bildschirm gab.
Wenn ein Modul Daten von einem anderen benötigt, wĂ€hlen Sie zwischen API, Integration Event, Shadow Entity oder Neumodellierung der Grenze. Der direkte Zugriff auf die Datenbank eines anderen Moduls ist ein Zeichen fĂŒr eine strukturelle Kopplung. Selbst wenn zwei Module im selben Prozess ausgefĂŒhrt werden und dieselbe physische Datenbank verwenden, mĂŒssen das Schema, die Persistence-Projekte und die VertrĂ€ge die GeschĂ€ftsgrenze schĂŒtzen.
- Gutes Grenzwertzeichen: Das Modul kann seine EntitÀten und Migrationen weiterentwickeln, ohne dass ein anderes Modul aufgrund interner Details eine Neukompilierung erzwingen muss.
- Schwaches Schwellensignal: Eine einfache Ănderung an einer EntitĂ€t erfordert die ĂberprĂŒfung von Bildschirmen, Abfragen und Modulhandlern, die diese EntitĂ€t nicht kennen sollten.
- Verstecktes AbhĂ€ngigkeitszeichen: Der Consumer muss âzu viel wissenâ ĂŒber Tabellen, technische Bereiche oder die internen Regeln des Producers.
Bewusste technische Schulden
Technische Schulden sind nicht irgendeine Unvollkommenheit. Es ist eine Entscheidung, die QualitĂ€t im Wissen um die zukĂŒnftigen Kosten aufzuschieben. Das Problem besteht darin, Schulden ohne Namen, ohne EigentĂŒmer und ohne Zahlungsplan zu schaffen.
In einem SaaS oder modularen System ist die gefÀhrlichste technische Schuld nicht nur eine lange Methode. Es sind die Schulden, die die Evolution einschrÀnken: Module, die sich nicht separat Àndern können, Tenantendaten ohne zuverlÀssige Isolierung, Secrets innerhalb des Repositorys, sensible Endpunkte ohne Grenzen, Ereignisse ohne erneute Verarbeitung oder Integrationen ohne klaren Vertrag.
- Dokumentieren Sie AbkĂŒrzungen, die wĂ€hrend der Generierung und Anpassung genommen wurden.
- FĂŒhren Sie Build und Test aus, bevor Sie mit der nĂ€chsten Generationsstufe fortfahren.
- Vermeiden Sie die Behebung von Problemen, die durch wiederholte lokale Hacks entstehen. Passen Sie das Muster oder die Vorlage an, wenn das Muster falsch ist.
Behandeln Sie jede architektonische Entscheidung als ĂŒberprĂŒfbar. Wenn Sie die NachrichtenĂŒbermittlung nicht aktivieren möchten, beachten Sie, dass aktuelle AblĂ€ufe nicht auf asynchroner Verarbeitung basieren. Wenn Sie eine Spalte pro Tenant auswĂ€hlen, stellen Sie Filter und Tests sicher. Wenn Sie sich fĂŒr die synchrone Integration entscheiden, gehen Sie beim Entwurf von ZeitĂŒberschreitungen, NichtverfĂŒgbarkeit und Vertragsfehlern aus.
Kommunikationsstrategien
WĂ€hlen Sie die Kommunikation entsprechend dem tatsĂ€chlichen Bedarf an Konsistenz, Latenz, RĂŒckverfolgbarkeit und Kopplung. Die Hauptfrage lautet nicht âWelche Technologie soll verwendet werden?â, sondern âDer Producer braucht jetzt die Antwort, sollte der Consumer den Vorgang nicht abbrechen und wem gehören die Daten?â
In modularen Systemen besteht der hĂ€ufigste Fehler darin, die Kommunikation in eine versteckte AbhĂ€ngigkeit umzuwandeln: Ein Modul fragt die Tabelle eines anderen ab, verwendet eine interne EntitĂ€t wieder, fĂŒgt ein externes Repository ein oder erstellt einen synchronen Aufruf, ohne Fehler zu behandeln. Das erscheint zunĂ€chst produktiv, macht aber jede weitere Ănderung riskanter, da die Grenze nicht mehr eindeutig ist.
- Synchroner Aufruf: Gut fĂŒr sofortige Abfragen und akzeptable AbhĂ€ngigkeit. Vermeiden Sie lange Kaskaden.
- Domain-Ereignis: interne Reaktion innerhalb der gleichen Anwendungsgrenze.
- Integration Event: Kommunikation zwischen Modulen, Servicesn oder Systemen, normalerweise mit Outbox.
- Shadow Entity: Mindestens lokale Kopie fĂŒr Konsultation und Validierung, ohne direkt vom Producer abhĂ€ngig zu sein.
- Externe HTTP-Integration: Geeignet fĂŒr Services von Drittanbietern, mit Timeout, Wiederholungsversuchen, Protokollen und Fehlerbehandlung.
Wenn die Regel eine sofortige Aktualisierung erfordert, verwenden Sie synchrones Streaming oder ĂŒberarbeiten Sie den Schwellenwert. Wenn Sie eventuelle Konsistenz akzeptieren, bevorzugen Sie Ereignisse, um die Kopplung zu reduzieren. Wenn der Consumer nur eine kleine Teilmenge von Daten aus einem anderen Kontext lesen muss, ist eine Shadow Entity möglicherweise sicherer als die Offenlegung der gesamten ursprĂŒnglichen EntitĂ€t.
| Frage | Empfohlene Richtung |
|---|---|
| Ist die Antwort erforderlich, um den Use Case abzuschlieĂen? | Explizite synchrone Integration mit Timeout, vorhersehbarem Fehler und klarem Vertrag. |
| Sollte der Producer die Transaktion auch dann bestÀtigen, wenn der Consumer abwesend ist? | Integration Event mit Outbox und asynchroner Verarbeitung. |
| Muss der Consumer nur minimale Daten validieren oder konsultieren? | Durch Event oder Integration unterstĂŒtzte Shadow Entity mit lokalem Ownership des Lesemodells. |
| Erfolgt die Ănderung innerhalb desselben Aggregates oder bounded context? | Domain Event oder domĂ€nenlokale Logik, ohne unnötige externe VertrĂ€ge zu veröffentlichen. |
