RODO mówi: musisz prowadzić rejestr tego, co robisz z danymi ludzi. Kiedy ktoś zakłada konto, kiedy weryfikuje adres, kiedy prosi o usunięcie danych — to powinno być gdzieś zapisane.
Pierwsze, co przyszło mi do głowy: zapisuj log razem z samą operacją. Jedno nie idzie bez drugiego. Proste, bezpieczne.
Ale zanim to wdrożyłem, zdałem sobie sprawę, że coś jest nie tak.
Dlaczego to złe powiązanie
Wyobraź sobie rejestrację użytkownika jako dwie czynności wykonywane razem: najpierw zapis danych konta, a zaraz potem wpis do rejestru RODO.
Co się stanie, jeśli rejestr RODO jest w tej chwili niedostępny — choćby przez kilka sekund przerwy technicznej? Cała rejestracja się nie uda. Użytkownik dostanie komunikat o błędzie. Nie z powodu problemu z kontem — z powodu logu.
To odwrócona ważność. Rejestracja jest krytyczna. Log audytu jest ważny — ale nie powinien jej blokować.
Log jako efekt uboczny
Podejście, które wybrałem:
Rejestracja konta kończy się sukcesem i wysyła sygnał: „użytkownik zarejestrowany”. Ten sygnał trafia do kolejki zadań. Osobny proces odbiera go z kolejki i zapisuje wpis do rejestru audytu — chwilę później, niezależnie od samej rejestracji.
Jeśli ten osobny proces padnie, sygnał czeka w kolejce i zostanie przetworzony przy kolejnej próbie. Nie znika.
A co jeśli sygnał zginie zanim dotrze do kolejki?
To słuszne pytanie. Serwer kolejki może stracić dane przy restarcie — nie jest tak trwały jak baza danych.
W listopadzie 2025 zostawiłem tę lukę otwartą. Pierwsza wersja wysyłała sygnał prosto do kolejki, więc teoretycznie mógł zniknąć przy awarii. Wiedziałem o tym i świadomie odłożyłem — przy zerze użytkowników to było ryzyko na papierze.
Domknąłem to dopiero kilka miesięcy później, już w 2026: zanim sygnał trafi do kolejki, ląduje najpierw w bazie danych — w tej samej operacji co rejestracja. Osobny proces pobiera wpisy z tej tabeli i przekazuje je do kolejki. Nawet jeśli serwer kolejki padnie, wpis w bazie przetrwa i log w końcu wpadnie.
To stary wzorzec — prostszy niż brzmi: rób dwie rzeczy razem tylko tam, gdzie to konieczne, i oddaj resztę kolejce.
Wzorzec transactional outbox i BullMQ
Domknięcie luki (już w 2026) to klasyczny wzorzec „transactional outbox”: zapis do tabeli pośredniej (outbox) w tej samej transakcji bazodanowej co operacja domenowa, następnie osobny proces (relay) pobiera wpisy i publikuje do BullMQ (Redis). W listopadzie 2025 działała tylko sama kolejka — bez tej tabeli pośredniej.
Gwarantuje „at-least-once delivery” nawet przy awarii kolejki. BullMQ po stronie konsumenta daje retry z eksponencjalnym backoffem i dead letter queue dla wiadomości, których nie da się przetworzyć.