Events / Webhooks
Status: Entwurf · Spec-Kandidat: ja
Architektur: Outbox als verbindliche Schnittstelle
Externe Eventzustellung läuft über die Tabelle mdm.AA_OUTBOX_EVENT (Transactional-Outbox-Pattern). Service schreibt fachliche Mutation, AA_AUDIT_LOG und AA_OUTBOX_EVENT in einer Transaktion. Ein separater Worker liest pending-Events und stellt sie an Konsumenten zu.
Damit liegt die Garantie-Grenze in der Datenbank, nicht im Service-Code: kein Event ohne committed Daten, kein commit ohne Event.
Konsumenten-Mechanismen
V1 (gesetzt): Webhooks
V1 stellt Events ausschließlich per HTTP-Webhook an registrierte URLs zu. Begründung: kein Broker-Setup nötig, deckt initiale Integrationsbedarfe (CRM-Sync, Reporting-Trigger, externer Suchindex via eigenem Adapter) ab und reicht für die erwartete Eventrate. Der Worker liest AA_OUTBOX_EVENT, sendet HTTP, behandelt Retries via available_at + Exponential Backoff und schreibt dispatched_at nach Konsumenten-2xx.
V2+ (deferred): Message Broker
Wahl eines Brokers (Kafka / RabbitMQ / NATS JetStream) ist nicht für V1 entschieden und wird erst getroffen, wenn konkrete Anforderungen anliegen (Throughput jenseits HTTP, Multi-Consumer-Fan-out mit Replay, harte Reihenfolge-Garantien über mehrere Konsumenten). Outbox-Architektur bleibt unverändert — Broker tritt als zweiter Worker neben den Webhook-Worker, beide lesen dieselbe AA_OUTBOX_EVENT-Tabelle.
Externe Suchindexe
Wenn nötig, als dedizierter Worker neben dem Webhook-Worker (liest dieselbe Outbox, materialisiert in OpenSearch / Elastic). Auch das ist V2+.
Konsumenten dürfen nicht direkt aus AA_AUDIT_LOG ziehen — AA_AUDIT_LOG ist langfristige Audit-Wahrheit, AA_OUTBOX_EVENT ist die operative Eventquelle mit Retention-Policy.
Garantien
- Atomicity: Outbox-Eintrag und fachliche Änderung sind in derselben Transaktion oder gar nicht.
- At-least-once: Bei Crash zwischen Konsumenten-ACK und
dispatched_at-Schreiben liefert der Worker erneut. Exactly-once wird nicht angeboten — Aufwand (2PC oder Consumer-getriebener Cursor) lohnt für die Zielgrößenordnung nicht. - Reihenfolge je Aggregat: Worker stellt Events mit gleicher
aggregate_idstrikt inid-Reihenfolge zu. Globale Reihenfolge ist nicht garantiert. - Idempotenz: Konsumenten nutzen
AA_OUTBOX_EVENT.idals Event-Schlüssel. Pflicht: persistenter Dedup-Speicher (z. B. Tabelleprocessed_event(id PK, processed_at)) oder idempotente Handler. Doppel-Zustellung muss tolerierbar sein. - Schema-Versionierung: jede Payload trägt
schema_version. Breaking changes erhöhen die Version, alte Konsumenten konsumieren ihren Major weiter.
Sicherheit
- Webhooks werden mit HMAC signiert (Schlüssel pro Konsument). Konsument verifiziert Signatur und
idfür Replay-Schutz. - Optional mTLS bei Webhook-Zielen mit hohen Anforderungen.
- Konsumenten-Registrierung ist eine konfigurierte Liste, kein offener Endpoint.
Naming
event_type-Konvention: <objekt>.<typ?>.<verb>. Beispiele:
entity.customer.created,entity.customer.updated,entity.customer.archivedentity_relation.customer_has_service.createdpermission.granted,role.assigned
Offen
- Konkrete Wahl Worker-Implementierung für V1-Webhook (eigener Service vs. CDC via Debezium).
- Retention der Outbox: Wie lange
dispatched-Events behalten? - Konsumenten-Registry: dedizierte Tabelle vs. Konfiguration.
- Dead-Letter-Strategie nach
attempts >= N. Webhook vs. Broker für V1.Gelöst: Webhook für V1, Broker-Wahl deferred bis konkrete Anforderung anliegt (siehe OP-22).