# Technische Architektur

> C4-Modell (Context, Container, Component), Azure-Komponenten, Service-Schichten, Queue-Struktur und Connector-Hierarchie der dsyr.DataBridge.

---

Site [llms.txt](/llms.txt)

---

> **Zielgruppe**
>
> Diese Seite richtet sich an **Administratoren und technische Nutzer**, die den internen
> Aufbau der DataBridge verstehen oder Probleme diagnostizieren möchten.
> Für einen konzeptuellen Einstieg siehe [Wie es funktioniert](../wie-es-funktioniert/).
> Sequenzdiagramme, Bicep-Module, DI-Verdrahtung und Nachrichten-Schemata stehen in der
> Entwickler-Dokumentation unter [Architektur im Detail](/entwicklung/architektur/).



---

## Azure-Komponenten

Die DataBridge besteht aus mehreren Azure-Diensten, die in einer gemeinsamen
Ressourcengruppe betrieben werden:

| Komponente | Dienst | Rolle |
|---|---|---|
| **Function App** | Azure Functions (Isolated Worker, .NET 10) | Ausführungsumgebung für alle Funktionen |
| **App Service Plan** | `Microsoft.Web/serverfarms` | Compute-Plan: Y1 (Consumption, Standard) oder EP1–EP3 (Premium) |
| **Service Bus** | Azure Service Bus Namespace | Puffer zwischen Erkennung und Verarbeitung |
| **Table Storage** | Azure Storage Account | Laufzeit-Konfiguration (Connections, Jobs, Mappings, Watermark) |
| **Application Insights** | Azure Monitor | Logging, Telemetrie, Alerting; Traces und ausgehende Aufrufe werden gesampelt |
| **Grafana-Dashboard** | `Microsoft.Dashboard/dashboards` | Vorkonfiguriertes Betriebsdashboard im Application-Insights-Blade (seit v2.1, wird immer mitdeployt) |
| **Metric Alerts + Action Group** | Azure Monitor | Dead-Letter- und Queue-Längen-Alerts, optional über `enableAlerts` |

> **Eigene Infrastruktur**
>
> Alle Komponenten laufen im **eigenen Azure-Abonnement** des Kunden. Die DataBridge nutzt
> keinen geteilten Dienst im Datenpfad — Datenverkehr und Konfiguration verlassen das
> Kunden-Abonnement nicht. Beim containerisierten Betrieb wird lediglich das Container-Image
> aus der WYSA-Registry bezogen.



---

## Architektur nach dem C4-Modell

Die folgenden drei Diagramme folgen dem [C4-Modell](https://c4model.com/) (Context →
Container → Component) — von außen nach innen, jede Ebene zoomt weiter in die
DataBridge hinein.

### Ebene 1 — System Context

Wer nutzt die DataBridge, und mit welchen Systemen tauscht sie Daten aus?

```mermaid
flowchart TB
    admin(["👤 Administrator"])
    ki(["🤖 KI-Assistent<br/><small>z. B. Claude Code</small>"])

    databridge["dsyr.DataBridge<br/><small>Synchronisiert Datensätze zwischen<br/>Quell- und Zielsystemen</small>"]

    bc["D365 Business Central<br/><small>ERP — Quelle/Ziel</small>"]
    ce["D365 CE / Dataverse<br/><small>CRM — Quelle/Ziel</small>"]
    nav["D365 NAV<br/><small>ERP — Quelle/Ziel</small>"]
    sql[("SQL-Datenbanken<br/><small>nur Quelle</small>")]
    rest["Beliebige REST-API<br/><small>über OpenAPI-Spec</small>"]

    admin -->|"konfiguriert, überwacht"| databridge
    ki -->|"MCP über HTTPS"| databridge
    databridge <-->|"OData v4, OAuth2/API-Key"| bc
    databridge <-->|"Dataverse SDK"| ce
    databridge <-->|"OData, Basic Auth"| nav
    databridge -->|"ADO.NET"| sql
    databridge <-->|"REST, aus Spec aufgelöst"| rest

    style databridge fill:#1565c0,color:#fff,stroke:#0d47a1,stroke-width:2
    style admin fill:#08427b,color:#fff,stroke:#052e56
    style ki fill:#08427b,color:#fff,stroke:#052e56
    style bc fill:#8b8b8b,color:#fff,stroke:#666
    style ce fill:#8b8b8b,color:#fff,stroke:#666
    style nav fill:#8b8b8b,color:#fff,stroke:#666
    style sql fill:#8b8b8b,color:#fff,stroke:#666
    style rest fill:#8b8b8b,color:#fff,stroke:#666
```

> **Notation**
>
> Dunkelblau = Personen, Blau = die DataBridge selbst, Grau = externe Systeme — dieselbe
> Farbsprache wie im [C4-Modell](https://c4model.com/) üblich, hier als Flowchart statt als
> natives Mermaid-`C4Context`-Diagramm umgesetzt, weil dessen Layout-Engine bei mehr als
> drei-vier Beziehungen zu überkreuzenden Linien neigt.



### Ebene 2 — Container

Innerhalb des eigenen Azure-Abonnements des Kunden besteht die DataBridge aus fünf
Containern (im C4-Sinn: separat betreibbare/skalierbare Einheiten):

```mermaid
flowchart TB
    admin(["👤 Administrator"])
    ki(["🤖 KI-Assistent"])

    subgraph RG["Ressourcengruppe des Kunden"]
        func["Function App<br/><small>.NET 10, Isolated Worker</small>"]
        sb[["Service Bus<br/><small>7 Queues</small>"]]
        ts[("Table Storage<br/><small>connections/jobs/<br/>mappings/watermark</small>")]
        appins["Application Insights"]
        graf["Grafana-Dashboard"]

        func <-->|"Nachrichten"| sb
        func <-->|"Konfiguration & Watermark"| ts
        func -->|"Telemetrie"| appins
        appins --> graf
    end

    systems["Quell-/Zielsysteme<br/><small>D365 BC/CE/NAV, SQL, REST</small>"]

    admin -->|"konfiguriert (Table Storage)"| func
    ki -->|"ruft MCP-Tools auf"| func
    func <-->|"liest/schreibt Datensätze"| systems

    style func fill:#1565c0,color:#fff,stroke:#0d47a1,stroke-width:2
    style sb fill:#1565c0,color:#fff,stroke:#0d47a1
    style ts fill:#1565c0,color:#fff,stroke:#0d47a1
    style appins fill:#1565c0,color:#fff,stroke:#0d47a1
    style graf fill:#1565c0,color:#fff,stroke:#0d47a1
    style admin fill:#08427b,color:#fff,stroke:#052e56
    style ki fill:#08427b,color:#fff,stroke:#052e56
    style systems fill:#8b8b8b,color:#fff,stroke:#666
    style RG fill:#f5f5f5,stroke:#999,color:#111111,stroke-dasharray: 4 2
```

> **Grafana-Dashboard und Alerts sind optional**
>
> Das Grafana-Dashboard wird immer mitdeployt (seit v2.1). Metric Alerts + Action Group sind
> zusätzlich optional über den Bicep-Parameter `enableAlerts` steuerbar und daher hier nicht
> als eigener Container dargestellt — siehe [Monitoring & Alerts](../../betrieb/monitoring/).



### Ebene 3 — Component

Innerhalb des Containers **Function App** sind die Funktionen (Trigger-Ebene) von der
Service-Schicht (Logik-Ebene) getrennt. Um das Diagramm lesbar zu halten, wird die
Service-Schicht hier als **ein** Component dargestellt und direkt im Anschluss
(Abschnitt [Service-Schicht](#service-schicht)) sowie in der
[Connector-Hierarchie](#connector-hierarchie) weiter aufgeschlüsselt — das C4-Modell ist
für genau dieses schrittweise Hineinzoomen gedacht.

```mermaid
flowchart TB
    subgraph FA["Function App"]
        getchanges["GetChanges<br/><small>Timer</small>"]
        processq["Process*QueueItems<br/><small>Service-Bus-Trigger (3x)</small>"]
        executejob["ExecuteJob / ExecuteHttpRequest<br/><small>HTTP</small>"]
        diagapi["CheckList / Validate / JobApi /<br/>MappingApi<br/><small>HTTP, read-only</small>"]
        mcp["McpApi<br/><small>MCP-Server für KI-Assistenten</small>"]
        orch["Orchestrator<br/><small>Durable, alternativ zu GetChanges</small>"]

        services["Service-Schicht<br/><small>InitializationService + 8 Services<br/>— siehe Detail-Diagramm unten</small>"]
        connectors["Connector-Schicht<br/><small>IConnector-Implementierungen<br/>— siehe Connector-Hierarchie</small>"]

        getchanges --> services
        processq --> services
        executejob --> services
        diagapi --> services
        mcp --> services
        orch --> services
        services --> connectors
    end

    style services fill:#1565c0,color:#fff,stroke:#0d47a1,stroke-width:2
    style connectors fill:#1565c0,color:#fff,stroke:#0d47a1,stroke-width:2
    style FA fill:#f5f5f5,stroke:#999,color:#111111,stroke-dasharray: 4 2
```

---

## Die Azure Functions

Alle Funktionen laufen in derselben Function App und teilen sich eine gemeinsame
Service-Schicht:

| Funktion | Trigger | Aufgabe |
|---|---|---|
| **GetChanges** | Timer (konfigurierbar über `GetChangesTimerExpression`) | Fragt alle fälligen Quellsysteme nach Änderungen und schreibt Nachrichten in die Service Bus Queues |
| **ProcessStandardQueueItems** | Service Bus `sbqexport` | Verarbeitet Nachrichten mit Standard-Priorität |
| **ProcessHighPriorityQueueItems** | Service Bus `sbqhighexport` | Verarbeitet Nachrichten mit hoher Priorität |
| **ProcessLowPriorityQueueItems** | Service Bus `sbqlowexport` | Verarbeitet Nachrichten mit niedriger Priorität |
| **ExecuteJob** | HTTP POST | Führt einen einzelnen Job manuell und sofort aus |
| **ExecuteHttpRequest** | HTTP POST | Diagnose: führt eine ad-hoc HTTP-GET-Anfrage gegen eine beliebige URL aus, unabhängig von den konfigurierten Connectoren |
| **GetInfo** | HTTP GET | Gibt die Versionsnummer der Function App zurück |
| **CheckList** | HTTP GET | Diagnose: prüft Mappings gegen die Attribute der beteiligten Systeme |
| **Validate** | HTTP GET | Diagnose: validiert die hinterlegte Konfiguration |
| **JobApi** (`GET /job`, `GET /job/{rowKey}`) | HTTP GET | Diagnose: gibt die eingelesene `jobs`-Konfiguration (alle bzw. ein einzelner Job) zurück |
| **MappingApi** (`GET /mapping`, `GET /mapping/{name}`) | HTTP GET | Diagnose: gibt die eingelesene `mappings`-Konfiguration (alle bzw. ein einzelnes Mapping anhand seines Namens) zurück |
| **McpApi** (`POST /mcp`) | HTTP POST | Read-only [MCP-Server](../../mcp-server/) für KI-Assistenten — bündelt Jobs/Mappings/Queues/Connector-Metadaten als Tools |

Zusätzlich existiert eine **optionale Durable-Orchestrierung** als Alternative zum
Timer-Pfad (`StartGetChangesOrchestration`, `GetChangesOrchestrator`, `GetChangesActivity`
und die Durable Entity `JobState`). Sie wird ausschließlich per HTTP gestartet und ist im
Regelbetrieb nicht aktiv.

Alle Funktionen lassen sich einzeln per App-Setting deaktivieren
(`AzureWebJobs.<Funktionsname>.Disabled`), ohne die Function App neu zu deployen.

---

## Queue-Struktur im Service Bus

Der Service Bus Namespace enthält sieben Queues:

```mermaid
flowchart TD
    GC[GetChanges] -->|Priorität: Hoch| QH[sbqhighexport]
    GC -->|Priorität: Standard| QS[sbqexport]
    GC -->|Priorität: Niedrig| QL[sbqlowexport]

    QH --> PH[ProcessHighPriorityQueueItems]
    QS --> PS[ProcessStandardQueueItems]
    QL --> PL[ProcessLowPriorityQueueItems]

    PH -->|"Konflikt im Ziel<br/>(HTTP 409)"| TH[sbqtransitivehigh]
    PS -->|"Konflikt im Ziel<br/>(HTTP 409)"| TS[sbqtransitivestandart]
    PL -->|"Konflikt im Ziel<br/>(HTTP 409)"| TL[sbqtransitivelow]

    PH -->|"SkipOutdatedMessages"| IGN[sbqignored]
    PS --> IGN
    PL --> IGN

    PH -->|"max. Zustellversuche<br/>überschritten"| DLQ[("Dead-Letter-Subqueue<br/>je Queue")]
    PS --> DLQ
    PL --> DLQ

    style QH fill:#fce4ec,stroke:#c62828,color:#111111
    style QS fill:#fff3e0,stroke:#e65100,color:#111111
    style QL fill:#e8f5e9,stroke:#2e7d32,color:#111111
    style TH fill:#fce4ec,stroke:#c62828,color:#111111,stroke-dasharray: 4 2
    style TS fill:#fff3e0,stroke:#e65100,color:#111111,stroke-dasharray: 4 2
    style TL fill:#e8f5e9,stroke:#2e7d32,color:#111111,stroke-dasharray: 4 2
    style IGN fill:#f3e5f5,stroke:#6a1b9a,color:#111111
    style DLQ fill:#b0bec5,stroke:#37474f,color:#111111
```

| Queue | Zweck |
|---|---|
| `sbqhighexport` / `sbqexport` / `sbqlowexport` | Primäre Verarbeitungs-Queues nach Priorität |
| `sbqtransitivehigh` / `sbqtransitivestandart` / `sbqtransitivelow` | Ziel-Queues bei Konflikten im Zielsystem (HTTP 409 / Duplikat-Datensatz), damit die Nachricht den Hauptpfad nicht blockiert |
| `sbqignored` | Veraltete Nachrichten (Quelldatum < Zieldatum bei aktivem `SkipOutdatedMessages`) |
| Dead-Letter-Subqueue | Jede Queue besitzt eine eigene Dead-Letter-Subqueue (`<queuename>/$deadletterqueue`) für Nachrichten, die nach `maxDeliveryCount` Versuchen nicht verarbeitet werden konnten |

> **Transitive- und Ignored-Queues werden nicht automatisch weiterverarbeitet**
>
> Auf `sbqtransitive*` und `sbqignored` liegt **kein** Trigger. Nachrichten werden dorthin
> ausschließlich geschrieben und verbleiben dort bis zum Ablauf der Time-to-live (14 Tage).
> Sie dienen der Isolation und der manuellen Analyse; ein Requeue erfolgt bei Bedarf über
> das Werkzeug `DeadLetterMover`.



> **Schreibweise `sbqtransitivestandart`**
>
> Der Name der Standard-Transitive-Queue endet auf `-standart`. So wird sie vom Deployment
> angelegt und so sucht die DataBridge sie — die Schreibweise darf nicht „korrigiert" werden.



---

## Service-Schicht

Alle Funktionen teilen eine gemeinsame **Service-Schicht**. Die Services selbst sind
DI-Singletons; lazy und thread-sicher nachgeladen wird die **Konfiguration** beim ersten
Aufruf. Das folgende Diagramm zoomt gegenüber dem C4-Component-Diagramm oben gezielt auf
den `InitializationService`-Teilbaum und dessen Initialisierungsreihenfolge. `InitializationService`
(bis v2.3 `CoreService`) ist reduziert auf Initialisierung und Ready/Error-Zustand — er ist
kein Service-Locator: `ProcessJobsForRetrievalInOrderService` und `ProcessQueueMessageService`
werden von den Functions, die sie brauchen (`GetChanges`/`ExecuteJob` bzw. `Process*QueueItems`),
direkt injiziert, nicht über ihn geroutet:

```mermaid
flowchart TB
    IS["InitializationService<br/>Lazy Konfigurations-Laden<br/>thread-sicher"]

    IS --> STORE["StorageService<br/>Table-Storage-CRUD"]
    IS --> CONS["ConnectionService<br/>Verwaltet Connector-Instanzen<br/>je Connection-Name"]
    IS --> MS["MappingService<br/>Gruppiert FieldMappings<br/>je Mapping-Name"]
    IS --> JS["JobService<br/>Prüft Schedules<br/>validiert Job-Konfiguration"]
    IS --> CHK["CheckListService<br/>Diagnose"]

    PJRS["ProcessJobsForRetrievalInOrderService<br/>Erkennungs-Pfad"] --> WS["WatermarkService"]
    PJRS --> SBS["ServiceBusService"]
    PQS["ProcessQueueMessageService<br/>Verarbeitet einzelne<br/>Nachrichten End-to-End"] --> SBS
    PQS --> WS
    JS --> WS
    WS --> STORE

    style IS fill:#e8f5f1,stroke:#18b28d,color:#111111,stroke-width:2
```

Daneben stehen Querschnitts-Services, die nicht **unter** dem `InitializationService`-Teilbaum hängen:

- **SettingsService** — liest App-Settings und Connection-Strings aus der Konfiguration
  (nicht aus Table Storage)
- **MetricsService** — sendet vorab aggregierte Custom Metrics an Application Insights
  (z. B. `ChangesDetected` je Job)
- **McpToolService** — bündelt die Tool-Logik hinter dem [MCP-Server](../../mcp-server/):
  ruft `JobService`, `MappingService`, `ServiceBusService` und `ConnectionService` auf und
  stößt darüber (wie `JobApi`/`MappingApi`) selbst einmalig `InitializationService.EnsureInitializedAsync`
  an — hängt also nicht im `InitializationService`-Baum, sondern **davor**

**Initialisierungsreihenfolge:**

1. `StorageService` prüft die vier Tabellen und legt fehlende an
2. `ConnectionService` liest `connections`, deserialisiert die JSON-Verbindungsdaten und
   führt je System einen Verbindungstest aus
3. `MappingService` liest `mappings` und gruppiert die Einträge nach Mapping-Name
4. `JobService` liest `jobs` und validiert sie: existieren Verbindungen und Mappings?
   Ist die Cron-Expression gültig?

Schlägt ein Verbindungstest fehl, wird der Fehler protokolliert und der **laufende Aufruf
mit einer Exception beendet**. Die Initialisierung wird zurückgesetzt und beim nächsten
Timer-Aufruf bzw. bei der nächsten Queue-Nachricht erneut versucht (Self-Repair). Bei
Queue-Nachrichten bedeutet das: die Nachricht wird abandoned und zählt gegen
`maxDeliveryCount`.

---

## Connector-Hierarchie

Detailansicht der Komponente **Connector-Schicht** aus dem C4-Component-Diagramm oben —
als UML-artiges Klassendiagramm, da C4 Vererbungshierarchien nicht sauber abbilden kann.
Jedes Quell- und Zielsystem wird durch einen **Konnektor** abstrahiert. Alle Konnektoren
implementieren dasselbe Interface und sind damit gegeneinander austauschbar:

```mermaid
flowchart TD
    IC["«interface»<br/>IConnector"]

    CB["ConnectorBase<br/>Serialisierung, Caching,<br/>Filter-Templating,<br/>Key-Vault-Integration"]

    HCB["HttpConnectorBase<br/>OAuth2 / Basic Auth,<br/>Token-Lifecycle,<br/>HTTP-Client-Management"]

    SCB["SqlConnectorBase<br/>ADO.NET, Schema-Caching,<br/>Paging (OFFSET / FETCH)"]

    IC --> CB
    CB --> HCB
    CB --> SCB
    CB --> CE["CeConnector<br/>Dataverse SDK"]

    HCB --> BC["BcConnector<br/>OData v4 API"]
    HCB --> NAV["NavConnector<br/>OData / Basic Auth"]
    HCB --> OA["OpenApiConnector<br/>Generische REST-API,<br/>Pfade aus OpenAPI-Spec"]

    SCB --> SQL[MsSqlConnector]
    SCB --> PG[PostgreSqlConnector]
    SCB --> MY[MySqlConnector]
    SCB --> OR[OracleConnector]

    style IC fill:#e3f2fd,stroke:#1565c0,color:#111111
    style CB fill:#e8f5f1,stroke:#18b28d,color:#111111
    style HCB fill:#fff3e0,stroke:#e65100,color:#111111
    style SCB fill:#fce4ec,stroke:#c62828,color:#111111
```

> **CeConnector nutzt kein HTTP-Basisverhalten**
>
> Der Dataverse-Konnektor erbt direkt von `ConnectorBase`, nicht von `HttpConnectorBase` —
> er kommuniziert über den Dataverse `ServiceClient` statt über einen eigenen HTTP-Client.



Für die Typumwandlung greifen zwei Bausteine ineinander:

- **TargetValueMapper** — wählt anhand des Zielsystem-Metadatentyps (EDM bei BC/NAV,
  `AttributeMetadata` bei Dataverse) den passenden Resolver aus. Existiert je Connector.
- **AttributeResolver** — führt die eigentliche Typumwandlung durch (Boolean, Date,
  DateTime, Decimal, Guid, Integer, String; bei Dataverse zusätzlich Picklist, Lookup,
  Money, State/Status, MultiSelect). Liegt als Resolver-Familie je Systemtyp vor.

> **OpenApiConnector — Metadaten aus der Spezifikation statt fest verdrahtet**
>
> Anders als bei den übrigen HTTP-Konnektoren stammen Pfade, Wrapper-Schlüssel
> (`{"clients":[...]}` vs. `{"client":{...}}`) und Feldtypen beim OpenAPI-Konnektor nicht aus
> connectorspezifischem Code, sondern werden zur Laufzeit aus einer OpenAPI-Spezifikation
> (JSON oder YAML) aufgelöst und pro Prozess zwischengespeichert. Details:
> [Konnektoren → OpenAPI](../../konnektoren/openapi/).



`IConnector` stellt zusätzlich `GetTableMetadata(tableName, apiPath)` bereit: liefert Typ,
Nullable, maximale Länge und Primary-Key-Information je Spalte direkt aus dem Zielsystem —
implementiert für alle Connector-Typen, seit v2.3 auch für die SQL-Familie. Dient vor allem dem
[MCP-Server](../../mcp-server/) zur Validierung von Mappings gegen das echte Schema.

---

## Änderungserkennung pro Connector-Typ

Die Methode, mit der Änderungen erkannt werden, unterscheidet sich je nach System:

| Connector | Mechanismus | Watermark-Typ |
|---|---|---|
| **D365BC** | OData-`$filter` auf das im Job konfigurierte Feld `WatermarkField` (z. B. `lastModifiedDateTime`) | `datetime` |
| **D365CE / Dataverse** | Dataverse Change Tracking API (opaker Token) | `string` |
| **D365NAV** | OData-`$filter` auf das konfigurierte `WatermarkField` | `datetime` oder `date` |
| **SQL-Datenbanken** | SQL-`WHERE`-Klausel auf das konfigurierte `WatermarkField` | `date` oder `datetime` |
| **OpenAPI** | `WatermarkField` wird als JSON-Pfad auf jeden gelesenen Datensatz angewendet; `Filter` wird unverändert als Query-String an die API angehängt (API-abhängig) | `datetime` |

Der Watermark-Wert wird pro Job in der Tabelle `watermark` gespeichert und am Ende der
Erkennungsphase fortgeschrieben. Ein manuelles Leeren des Watermarks erzwingt einen
vollständigen Neulauf des jeweiligen Jobs.

---

## Über CRUD hinaus: Aktions-Jobs, job-eigene Pfade, eingebettete Kinder

Drei neuere Erweiterungen des Job-/Mapping-Modells betreffen nicht nur den OpenAPI-Konnektor,
sind dort aber am relevantesten:

- **InvokeAction** — ein Job mit einem `Action`-Wert, der keinem bekannten `JobActionType`
  entspricht, ruft statt eines CRUD-Vorgangs einen benannten, nicht-CRUD-Endpunkt auf (z. B.
  einen OpenAPI-`operationId` oder ein CE-`OrganizationRequest`). Details:
  [Jobs → InvokeAction](../../konfiguration/jobs/#invokeaction--aktions-jobs).
- **Job-eigene Source-/Target-Pfade** — ein Job kann über `Source`/`Target` die
  `SourceTable`/`TargetTable` seiner Mapping-Zeilen überschreiben (bzw. bei fehlendem Mapping
  ein synthetisches Mapping erzeugen), sodass eine Mapping-Gruppe für mehrere Ressourcen oder
  Aktionspfade wiederverwendet werden kann. Details:
  [Jobs → Source und Target](../../konfiguration/jobs/#source-und-target--job-eigene-ressourcenpfade).
- **EmbedChildren** — bettet die Datensätze eines referenzierten Kind-Jobs bereits beim Lesen
  in die Nachricht des Elterndatensatzes ein, sodass Eltern- und Kind-Datensätze gemeinsam als
  eine Nachricht verarbeitet werden (derzeit nur mit D365CE als Quellsystem). Details:
  [Mappings → EmbedChildren](../../konfiguration/mappings/#embedchildren).

---

## Retry- und Fehlerverhalten

| Szenario | Verhalten |
|---|---|
| Konflikt im Zielsystem (HTTP 409 / Duplikat) | Weiterleitung in die passende Transitive-Queue |
| Transientes Netzwerkproblem, Zielsystem temporär nicht erreichbar | Backoff (`DeliveryCount² · 1000 ms`), Abandon, erneute Zustellung nach Lock-Ablauf |
| Veraltete Nachricht (Quelle älter als Ziel) | Bei aktivem `SkipOutdatedMessages`: Weiterleitung in `sbqignored` |
| Maximale Zustellversuche überschritten | Weiterleitung in die Dead-Letter-Subqueue der jeweiligen Queue, Alert ausgelöst (wenn konfiguriert) |
| Initialisierungsfehler (Verbindung) | Fehler wird geloggt, laufender Aufruf bricht ab, nächster Aufruf versucht erneut |
