AA_OUTBOX_EVENT
Status: Entwurf · Spec-Kandidat: ja
Zweck
Transactional-Outbox-Tabelle. Jeder Service-Aufruf, der Daten ändert, schreibt in derselben Transaktion einen AA_OUTBOX_EVENT. Ein separater Worker liest pending-Einträge und stellt sie an Konsumenten zu (Webhooks, Queue, externe Suchengine).
Damit gilt:
- Atomicity: Kein Event ohne committed DB-Zustand, kein DB-Zustand ohne Event.
- At-least-once: Worker quittiert nach erfolgreicher Zustellung; bei Crash wird wiederholt. Exactly-once ist nicht das Ziel.
- Reihenfolge je Aggregat: Worker stellt Events mit gleicher
aggregate_idstrikt inid-Reihenfolge zu. - Idempotenz: Konsumenten verwenden
idals Event-Key und müssen Doppel-Zustellung deduplizieren (persistenter Dedup-Speicher oder idempotente Handler).
Felder
| Feld | Typ | Pflicht | Hinweise |
|---|---|---|---|
id | bigserial | ja | PK, monoton steigend → Reihenfolge-Anker |
tenant_id | uuid | nein | NULL für plattformweite Events |
event_type | text | ja | stabiler Name, Konvention <objekt>.<typ?>.<verb> (z. B. entity.customer.updated) |
schema_version | integer | ja | default 1, für breaking-changes der Payload |
aggregate_type | text | ja | logische Tabelle (z. B. DDM_ENTITY, DDM_ENTITY_RELATION, AA_ROLE) |
aggregate_id | uuid | nein | ID des betroffenen Datensatzes (NULL für aggregat-lose Events) |
audit_log_id | bigint | nein | FK → AA_AUDIT_LOG — Verbindung zur fachlichen Audit-Zeile |
correlation_id | uuid | nein | gruppiert mehrere Events einer Service-Operation |
payload | jsonb | ja | versionsabhängige Eventdaten, JSON-Objekt-Constraint |
status | mdm.outbox_status | ja | pending / dispatched / failed / skipped |
attempts | integer | ja | default 0, ≥ 0 |
last_error | text | nein | letzte Fehlermeldung des Workers |
available_at | timestamptz | ja | default now(); Worker rückt failed-Events mit Backoff in die Zukunft |
dispatched_at | timestamptz | nein | Zeitpunkt der erfolgreichen Zustellung |
created_at / created_by | – | – | bei INSERT gesetzt |
Constraints
outbox_event_payload_is_object_chkoutbox_event_attempts_chk(attempts >= 0)
Trigger
- Keine. Tabelle ist append-only durch Service. Status-Updates nur durch den Worker.
Indizes
ix_outbox_event_pendingpartial auf(status, available_at, id) WHERE status='pending'— Worker-Hot-Path.ix_outbox_event_aggregateauf(aggregate_type, aggregate_id, id)— Reihenfolge-Lookup je Aggregat.ix_outbox_event_correlationpartial aufcorrelation_id.
Verhalten
- Erzeugung: Service schreibt
AA_OUTBOX_EVENTin derselben Transaktion wie die fachliche Mutation.audit_log_idundcorrelation_idwerden mitgegeben, damit ein Konsument den Audit-Kontext rekonstruieren kann. - Zustellung: Worker liest pending-Events in batch, sortiert per
(aggregate_id, id), ruft Konsumenten auf, setzt bei Erfolgstatus='dispatched'unddispatched_at. Bei Fehlerattempts++,last_error, exponential-backoff überavailable_at. - Retention (OP-22, gesetzt 2026-04-29): Erfolgreich zugestellte Events (
status='dispatched') werden 10 Tage nachdispatched_atperAA_JOB.job_kind='outbox_compact'hart gelöscht.failed/skippedEvents bleiben bis manuell quittiert.AA_AUDIT_LOGbleibt die langfristige Wahrheit (Retention 10 Jahre, OP-06). - Webhooks und Queues sind Konsumenten der Outbox, keine eigenständigen Schreibpfade.
Beispiel-Payload
{ "tenant_key": "public", "entity_type": "customer", "entity_id": "9c2…", "code": "C-1001", "version": 5, "before": { "status": "draft" }, "after": { "status": "active" }, "actor": { "principal_type": "user", "principal_id": "1f3…" }}