Event-driven bez chaosu
Event-driven architecture obiecuje luźne sprzężenie. W praktyce bez dyscypliny zmienia się w niewidoczny bałagan asynchroniczny. Oto jak tego uniknąć.
Co się psuje
Zespół włącza kolejkę (Kafka, Rabbit, NATS) i nagle:
- nikt nie wie jaki jest kolejność zdażeń,
- debugowanie to "kto wysłał ten event o 2:14",
- połączone efekty uboczne — event A wyzwala B wyzwala C, nikt nie przewidział C,
- utracone wiadomości przy restarcie konsumenta.
Dyscyplina 1: kontrakty eventów
Każdy event to umowa. Traktuj ją jak API:
- schema w wersjonowanym registry (Avro/Protobuf/JSON Schema),
- wersjonowanie semantyczne (
user.created.v2), - consumer musi deklarować które wersje rozumie.
Bez schema registry event-driven to po prostu "JSON w przedziale".
Dyscyplina 2: idempotentność
Konsument musi być bezpieczny przy podwójnym dostawie. Dlaczego? Bo at-least-once to standard, a sieć bywa.
- klucz idempotentności (event_id) w bazie,
- UPSERT zamiast INSERT,
- outbox pattern — zapis stanu i eventu w jednej transakcji lokalnej.
Dyscyplina 3: widoczność
Asynchroniczność bez observability to ślepota. Minimum:
- correlation_id propagowany przez cały łańcuch,
- tracing (OpenTelemetry) — widzisz ścieżkę eventu,
- dead-letter queue — nic nie znika w nieskończoność.
Kiedy NIE używać event-driven
Dla prostych CRUD-ów i synchronicznych flowów (zapłać → potwierdź) event-driven to nadmiar. Używaj go gdy masz wiele niezależnych reakcji na ten sam fakt biznesowy.
Podsumowanie
Event-driven nie zwalnia z inżynierii. Dodaje warstwę, która wymaga kontraktów, idempotentności i observability — albo skończysz z systemem który "czasami cos zrobi, a nikt nie wie co".