1 - Wie es funktioniert
Der Datenfluss der dsyr.DataBridge in fünf Schritten — verständlich erklärt ohne technischen Ballast.
Das Grundprinzip
Die dsyr.DataBridge arbeitet wie ein kontinuierlicher Datenstrom zwischen zwei Systemen.
Sie fragt regelmäßig: „Was hat sich seit dem letzten Mal geändert?" — und überträgt
genau diese Änderungen ins Zielsystem. Nichts mehr, nichts weniger.
Kein manueller Export. Kein Doppelpflegen. Keine Vollsynchronisation, die alles neu
schreibt. Nur die Differenz, automatisch, im Minutentakt.
Der Datenfluss in fünf Schritten
flowchart LR
A([Quelldaten<br/>ändern sich]) -->|"alle 15 Sekunden<br/>prüfen"| B[DataBridge<br/>erkennt Änderung]
B -->|"Datensätze<br/>puffern"| C[(Service Bus<br/>Queue)]
C -->|"Nachricht<br/>verarbeiten"| D[Felder<br/>mappen & wandeln]
D -->|"schreiben"| E([Zielsystem<br/>aktualisiert])
E -->|"Stand merken"| F[(Watermark<br/>gespeichert)]
F -.->|"nächster Lauf<br/>startet hier"| B
style A fill:#e8f5f1,stroke:#18b28d,color:#111111
style E fill:#e8f5f1,stroke:#18b28d,color:#111111
style C fill:#fff3e0,stroke:#f57c00,color:#111111
style F fill:#e3f2fd,stroke:#1565c0,color:#111111Schritt 1 — Änderungen erkennen
Alle 15 Sekunden (konfigurierbar über GetChangesTimerExpression) startet ein Timer. Die DataBridge fragt das Quellsystem:
„Gib mir alle Datensätze, die seit meinem letzten Stand geändert wurden."
Dafür nutzt sie den gespeicherten Watermark — einen Zeitstempel oder einen
Änderungstoken, der beim letzten erfolgreichen Lauf hinterlegt wurde.
Schritt 2 — Änderungen puffern
Gefundene Datensätze landen nicht sofort im Zielsystem, sondern zunächst in einer
Azure Service Bus Queue. Das entkoppelt Erkennung und Verarbeitung: Selbst wenn das
Zielsystem kurz nicht erreichbar ist, gehen keine Daten verloren. Die Queue speichert
die Nachrichten bis zur erfolgreichen Verarbeitung.
Schritt 3 — Felder mappen und wandeln
Jede Nachricht aus der Queue wird einzeln verarbeitet. Die DataBridge liest die
konfigurierten Mappings und weiß: Quellfeld A geht in Zielfeld B, Wert X wird
zu Wert Y übersetzt, ein fehlendes Feld bekommt einen Standardwert.
Dieser Schritt ist vollständig konfigurierbar — ohne Programmierung.
Schritt 4 — Ins Zielsystem schreiben
Der gemappte Datensatz wird ins Zielsystem geschrieben. Die DataBridge prüft dabei
anhand eines konfigurierten Schlüssels, ob der Datensatz dort bereits existiert:
- Existiert: Update des vorhandenen Datensatzes
- Existiert nicht: Neuanlage
Schritt 5 — Watermark aktualisieren
Nach erfolgreichem Durchlauf speichert die DataBridge den neuen Stand als Watermark.
Beim nächsten Timer-Aufruf dient dieser Wert als Startpunkt — so werden Datensätze
weder doppelt übertragen noch übersprungen.
Bidirektionale Synchronisation
Die fünf Schritte gelten für eine Richtung der Synchronisation. Wenn Daten in
beide Richtungen fließen sollen (z. B. BC → Dataverse und Dataverse → BC), werden
zwei unabhängige Jobs konfiguriert — jeder mit eigenem Quell- und Zielsystem,
eigenem Mapping und eigenem Watermark.
Prioritäten und Parallelverarbeitung
Nicht alle Datensätze sind gleich wichtig. Die DataBridge kennt drei Prioritätsstufen:
| Priorität | Typischer Einsatz |
|---|
| Hoch | Zeitkritische Stammdaten (z. B. Preise, Kundendaten) |
| Standard | Normale Synchronisation (Bestellungen, Kontakte) |
| Niedrig | Historische Daten, große Datenmengen ohne Zeitdruck |
Nachrichten mit hoher Priorität werden bevorzugt verarbeitet, ohne dass niedrigere
Prioritäten blockiert werden.
Was passiert bei Fehlern?
Schlägt die Verarbeitung einer Nachricht fehl, gibt es mehrere Sicherheitsnetze:
- Automatischer Wiederholungsversuch — die Nachricht wird nach kurzer Wartezeit
erneut verarbeitet (bis zu einer konfigurierten Maximalanzahl).
- Transitive Queue — meldet das Zielsystem einen Konflikt (z. B. ein Duplikat),
wird die Nachricht in eine separate Queue abgelegt, damit sie den Hauptpfad nicht
blockiert. Dort wird sie nicht automatisch erneut verarbeitet, sondern bei Bedarf
manuell zurückgestellt.
- Dead-Letter Queue — dauerhaft nicht verarbeitbare Nachrichten werden dort
gesammelt und können analysiert oder manuell re-queued werden.
In keinem dieser Fälle gehen Daten still verloren. Das Monitoring und die konfigurierbaren
Alerts informieren aktiv, wenn die Dead-Letter Queue Einträge enthält.
2 - Technische Architektur
C4-Modell (Context, Container, Component), Azure-Komponenten, Service-Schichten, Queue-Struktur und Connector-Hierarchie der dsyr.DataBridge.
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.
Sequenzdiagramme, Bicep-Module, DI-Verdrahtung und Nachrichten-Schemata stehen in der
Entwickler-Dokumentation unter Architektur im Detail.
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 (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?
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:#666Notation
Dunkelblau = Personen, Blau = die DataBridge selbst, Grau = externe Systeme — dieselbe
Farbsprache wie im C4-Modell ü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):
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 2Grafana-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.
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) sowie in der
Connector-Hierarchie weiter aufgeschlüsselt — das C4-Modell ist
für genau dieses schrittweise Hineinzoomen gedacht.
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 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:
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:
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:2Daneben 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:
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:
StorageService prüft die vier Tabellen und legt fehlende anConnectionService liest connections, deserialisiert die JSON-Verbindungsdaten und
führt je System einen Verbindungstest ausMappingService liest mappings und gruppiert die Einträge nach Mapping-NameJobService 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:
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:#111111CeConnector 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.
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 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. - 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. - 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.
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 |