Zum Inhalt springen

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_id strikt in id-Reihenfolge zu. Globale Reihenfolge ist nicht garantiert.
  • Idempotenz: Konsumenten nutzen AA_OUTBOX_EVENT.id als Event-Schlüssel. Pflicht: persistenter Dedup-Speicher (z. B. Tabelle processed_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 id fü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.archived
  • entity_relation.customer_has_service.created
  • permission.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).

Verwandte Dokumente