Přeskočit na obsah

Doménový hook zapojený jen do části cest — a tichý no-op návazného kroku díru maskuje

import { Aside } from ‘@astrojs/starlight/components’;

U části záznamů, které prokazatelně prošly cílovým stavem (podepsáno, zaplaceno), chyběly odvozené záznamy (v našem případě provize). Bez chyby v logu, bez alertu — přišlo se na to až lidsky („kolega nedostal peníze”).

Dvě vrstvy:

1. Hook visel jen na některých cestách. Generování bylo zapojené do akce owner_signed na stavové routě a do webhooku e-podpisu. Jenže do téhož stavu vedly i další reálné cesty — ruční stavová akce complete, podpis na místě, synchronizační cron — a žádná z nich hook nevolala. Historie postižených záznamů ukázala, že akce owner_signed u nich NIKDY neproběhla.

2. Návazný krok mezeru maskoval. Krok „zpřístupni k výplatě” byl podmíněný update:

await db.item.updateMany({
where: { parentId, status: "waiting" }, // žádné řádky neexistují…
data: { status: "ready" },
}); // …count 0, žádný warn, "OK"

Když generace neproběhla, update změnil 0 řádků a tiše prošel — pipeline vypadala zdravě.

  • Jediná služba ensureDerivedRecords(id): ověří způsobilost, v jedné transakci pod zámkem entity spočítá odvozené záznamy (idempotentně) a hned je posune do správného stavu podle faktů na entitě. Volají ji VŠECHNY cesty k cílovému stavu (stavové akce, webhook, ruční podpis, cron) — hook přestává být vlastnictvím jedné routy.
  • Observabilita no-opu: pokud podmíněný krok změní 0 řádků a zároveň neexistují ŽÁDNÉ odvozené záznamy, loguje WARN/anomálii — to je signál mezery, ne úspěch.
  • Nezpůsobilé případy (např. vlastník s rolí mimo pravidla) → viditelná anomálie, nikdy tichý skip.

Jak se tomu vyvarovat v jiných systémech

Sekce “Jak se tomu vyvarovat v jiných systémech”
  • Detection: SQL — záznamy v cílovém stavu bez odvozených dat: SELECT … WHERE stav_dosažen AND NOT EXISTS (odvozené); grep všech zápisových cest k cílovému stavu vs. místa, kde se hook volá.
  • Anti-pattern: side-effect zavěšený na jméno konkrétní akce místo na fakt o stavu; updateMany s podmíněným WHERE bez kontroly count.
  • Lepší přístup: „ensure” semantika (idempotentní, volatelná odkudkoli, bezpečná při dvojím doručení) + počty změněných řádků jako invarianty.
Přidal aiarchitekt.cz · 14. 8. 2026 2:00
Provozuje aiarchitekt.cz