Interne und externe Integrationen

Integrationen verbinden die von Lino generierte Anwendung mit anderen Modulen, internen Diensten und Drittsystemen. Die Wahl zwischen synchronem Aufruf, in-process-Integration, HTTP und Ereignissen sollte Kopplung, Latenz, Zuverlässigkeit und Dateneigentum berücksichtigen.

Das Ziel ist nicht nur, ein anderes System aufzurufen. Es geht darum, eine Grenze zu deklarieren: wer die Daten bereitstellt, wer sie konsumiert, welcher Vertrag öffentlich ist, was im Fehlerfall passiert und ob die Operation sofort erfolgen muss oder später verarbeitet werden kann.

Integrationen erstellen

Integrationen stehen für explizite Kommunikation mit einem anderen Kontext, einem internen Dienst oder einem externen System. Sie sollten einen Namen, einen Vertrag, Authentifizierung, Timeout, Fehlerstrategie und eine klare Verantwortung haben.

lino integration new --name <ServiceName>
lino integration list

Verwenden Sie integration list, um vorhandene Integrationen zu prüfen, bevor Sie eine weitere erstellen. Vermeiden Sie generische Integrationen wie CommonIntegration, weil sie dazu neigen, Verantwortlichkeiten ohne klare Grenze anzusammeln.

Definieren Sie vor dem Erstellen einer Integration, welches Problem sie löst: ein externes Register abfragen, eine Zahlung senden, ein Abonnement validieren, einen Benutzer synchronisieren, ein internes Modul konsumieren oder Daten an ein anderes System veröffentlichen. Wenn die Integration keine spezifische Absicht hat, ist sie wahrscheinlich noch nicht bereit, zu einem generierten Vertrag zu werden.

  • Nach integriertem Kontext benennen: bevorzugen Sie Billing, Identity, Catalog oder Shipping gegenüber generischen Namen.
  • Vertrag von Implementierung trennen: Domain und Anwendung sollten nicht direkt von Transportdetails abhängen.
  • Externe Fehler annehmen: jede Remote-Integration kann langsam werden, nicht verfügbar sein, einen Teilfehler zurückgeben oder ihren Vertrag ändern.

Integrationsresources

Resources stellen Objekte dar, die von einer Integration bereitgestellt oder konsumiert werden, wie customers, invoices, tenants, subscriptions, users oder documents.

lino integration resource new --service <ServiceName> --module <ModuleName> --entity <EntityName>
lino integration resource list --service <ServiceName> --module <ModuleName> --entity <EntityName>

Ein Integrationsresource muss nicht der Domain-Entität entsprechen. Häufig ist es ein Datenaustauschvertrag, eine externe Sicht oder die minimale Darstellung, die für die Kommunikation zwischen Kontexten notwendig ist. Die gesamte Entität in den externen Vertrag zu kopieren, legt oft interne Details offen und erhöht die Evolutionskosten.

  • Modellieren Sie den resource mit dem Vokabular des integrierten Systems.
  • Legen Sie interne Entitäten nicht als externen Vertrag offen.
  • Definieren Sie, welche Felder Identifikatoren, Filter und zurückgegebene Daten sind.
  • Dokumentieren Sie Pagination, Authentifizierung und Aufruflimits, wenn sie existieren.

Wenn der resource Daten aus einem anderen Modul darstellt, nehmen Sie nur das auf, was der Konsument benötigt. Dasselbe Prinzip erscheint bei shadow entities: Das konsumierende Modul hält eine kleine lokale Kopie, ausgerichtet auf seinen eigenen Use Case, statt vom vollständigen Modell des produzierenden Moduls abzuhängen.

Integrationsoperationen

Operationen beschreiben Aktionen, die auf einem Integrationsresource verfügbar sind: erstellen, abfragen, aktualisieren, abbrechen, senden, synchronisieren oder validieren.

lino integration operation new --service <ServiceName> --module <ModuleName> --entity <EntityName>
lino integration operation list --service <ServiceName> --module <ModuleName> --entity <EntityName>

Jede Operation sollte Absicht, Eingabe, Ausgabe, Aufrufmethode, Fehlerverhalten und die Frage definieren, ob sie sicher erneut ausgeführt werden kann.

  • Abfragen sollten einen Timeout und eine Behandlung für Nichtverfügbarkeit haben.
  • Remote Commands sollten nach Möglichkeit idempotent sein.
  • Externe Fehler sollten die Haupttransaktion nicht brechen, wenn das Business asynchrone Verarbeitung akzeptiert.
  • Verwenden Sie Logs und correlation id, um Aufrufe zwischen Systemen nachzuverfolgen.

Idempotenz ist besonders wichtig bei Schreiboperationen. Wenn ein Versuch fehlschlägt, nachdem das externe System die Aktion ausgeführt hat, kann ein neuer Versuch eine Zahlung doppelt auslösen, einen Datensatz erneut erstellen oder eine Nachricht zweimal senden. Verwenden Sie nach Möglichkeit Idempotenzschlüssel, externe Identifikatoren oder dokumentierte Regeln für erneute Ausführung.

Integrationen konsumieren

Nachdem Integration, resource und Operation modelliert wurden, verwenden Sie den Konsum, um die Anwendung mit dem generierten Vertrag zu verbinden. Ziel ist, dass der Use Case von einer benannten Abstraktion mit vorhersehbarer Eingabe und Ausgabe abhängt, statt HTTP-Aufrufe, URL-Aufbau, headers, Response-Parsing und Fehlerbehandlung über mehrere handlers zu verteilen.

lino integration consume

Use Cases sollten von Abstraktionen abhängen, nicht von im Code verstreuten HTTP-Details. Das erleichtert Tests, Austausch der Implementierung und einheitliche Fehlerbehandlung. Es verhindert auch, dass jeder endpoint der Anwendung Timeout, Authentifizierung, correlation id, Serialisierung und Fehlermapping anders neu erfindet.

  • Validieren Sie Daten, bevor Sie einen externen Dienst aufrufen.
  • Verwenden Sie cancellation token und timeout.
  • Ordnen Sie externe Antworten vorhersehbaren internen Fehlern zu.
  • Speichern Sie keine rohe externe payload als Domain-Regel ohne Transformation.

HTTP vs. in-process

In-process-Integration ist einfach und schnell, wenn Module gemeinsam laufen. HTTP schafft einen expliziteren Vertrag zwischen Prozessen, fügt aber Latenz, Netzwerkfehler und den Bedarf an Resilienz hinzu.

In-process-Integration darf nicht freien Zugriff auf alles bedeuten. Selbst innerhalb derselben runtime sollte der Konsument über einen Vertrag kommunizieren, nicht über eine interne Entität, DbContext oder ein Repository eines anderen Moduls. Die Netzwerkkosten sind geringer, aber das Kopplungsrisiko bleibt bestehen.

SzenarioÜbliche WahlHinweis
Module im selben modularen MonolithenIn-process oder interner VertragBewahren Sie die Grenze; vermeiden Sie das Teilen von Entität und Persistenz.
Separater Dienst mit eigenem DeploymentHTTP oder MessagingBehandeln Sie Netzwerk, Authentifizierung, Versionierung und Nichtverfügbarkeit.
Asynchroner und verzögerungstoleranter ProzessIntegrationsereignisEntwerfen Sie idempotente und nachvollziehbare Konsumenten.

HTTP ist besser geeignet, wenn Produzent und Konsument getrennte runtimes haben oder wenn Sie die Grenze operativ explizit machen wollen. In-process ist geeignet, wenn dieselbe Anwendung die Kontexte hostet und die Antwort sofort benötigt wird, solange die Abhängigkeit weiterhin durch einen Vertrag deklariert bleibt.

Ereignisse vs. synchrone Integrationen

Verwenden Sie synchrone Integration, wenn die Antwort erforderlich ist, um die aktuelle Operation abzuschließen. Verwenden Sie ein Ereignis, wenn der Konsument später reagieren kann und eventual consistency akzeptabel ist.

Diese Wahl ist zuerst eine Business-Entscheidung und erst danach eine technische Entscheidung. Wenn eine Bestellung erst erstellt werden darf, nachdem der Zahlungsdienst die Autorisierung bestätigt hat, kann der synchrone Aufruf ein wesentlicher Teil des Use Case sein. Wenn die Erstellung der Bestellung nur eine Benachrichtigung, eine Projektionsaktualisierung oder eine Synchronisierung zu einem anderen Modul auslösen muss, ist ein Ereignis meist passender.

BedarfAnsatzTechnische Vorsicht
Verpflichtende sofortige AntwortSynchrone IntegrationTimeout, fallback, vorhersehbarer Fehler und stabiler Vertrag.
Späterer Effekt, der Verzögerung toleriertIntegrationsereignisOutbox, Idempotenz, retries und Korrelationslogs.
Häufiges Lesen weniger externer DatenShadow Entity oder lokale ProjektionSynchronisierung, spätere Aktualisierung und minimales Modell.
Ein unbehandelter Fehler ist aufgetreten. Aktualisieren 🗙