---
layout: 'page'
uri: '/framework/overview'
position: 10
slug: 'framework-overview'
parent: 'framework-background'
navTitle: 'Overview'
title: 'When to use what'
description: 'Jak vybrat mezi command, doménovým eventem, fire-and-forget runem, durable runem a schedulerem — a proč background práce běží mimo transakci (jinak by dlouhá práce zamkla celou SQLite).'
---

# When to use what

gokick má pět způsobů, jak něco „udělat". Vyber podle dvou otázek: **běží to hned v requestu, nebo zvlášť na pozadí?** a (pro práci na pozadí) **potřebuje to po pádu pokračovat od posledního kroku?** Špatná volba buď ztratí práci (něco, co mělo přežít restart, běželo jen tak), nebo **zamkne celou databázi** (dlouhá práce nebo volání ven v transakci drží zámek na zápis).

![Co kdy použít na práci na pozadí](../files/background-work.svg)

> **Fire-and-forget a durable run jsou jeden engine** (tabulka `runs`, jeden worker) ve dvou tvarech: **fire-and-forget run** = bez checkpointu (`FireAndForget`), **durable run** = s checkpointem a resume (`Durable`). Oba běží **mimo transakci**. Detailní toky: [Command](/framework/command) · [Events](/framework/events) · [Fire-and-forget](/framework/fire-and-forget) · [Durable run](/framework/durable-run) · [Scheduler](/framework/scheduler). Návody: `/gk-commands`, `/gk-domain-events`, `/gk-runs`, `/gk-scheduler`.


## Rychlá volba

- Potřebuješ **uložit/upravit data** v reakci na akci uživatele, **vše naráz nebo nic**? → **command**.
- Chceš, aby na něco **zareagoval někdo další** („stalo se X"), aniž to ten command musí vědět? → **doménový event**.
- Máš background práci, co musí **přežít restart**, ale **nepotřebuje si pamatovat postup** — krátké volání ven (e-mail/SMTP, webhook, jedno API volání) nebo rychlý přepočet? → **fire-and-forget run** (běží mimo transakci).
- Máš **dlouhou** práci (velký import, velký report), co po pádu **nesmí začít od nuly**? → **durable run** (běží mimo transakci, po každém kroku checkpoint → pokračuje od posledního).
- Chceš něco spouštět **opakovaně po čase** (úklid, synchronizace)? → **scheduler**.


## Přehledová tabulka

| Způsob | Kdy běží | V transakci? | Použij na |
|---|---|---|---|
| **Command** | hned v requestu | ✅ ano | ulož/uprav/smaž data (vše naráz, nebo nic) |
| **Doménový event** | hned po uložení dat | ❌ ne (po uložení) | „stalo se X" → reakce, kterou command nezná |
| **Fire-and-forget run** (`FireAndForget`) | zvlášť na pozadí | ❌ **NE** | krátká fire-and-forget práce (mail/API/webhook, přepočet) — **bez checkpointu** |
| **Durable run** (`Durable`) | zvlášť, **dlouho** | ❌ **NE** | dlouhá práce (import, report), co po pádu **pokračuje od posledního kroku** |
| **Scheduler** | opakovaně po čase | ❌ ne | úklid, synchronizace; velkou práci → zařaď jako run |

Fire-and-forget a durable run se liší jedinou otázkou: **potřebuje to po pádu pokračovat od posledního kroku?** Ano → durable run (checkpoint), ne → fire-and-forget run. Jinak je to ten samý engine.


## Proč „v transakci, nebo ne" tolik záleží

SQLite umí **psát jen z jednoho místa naráz**. Transakce si ten zámek na zápis vezme **hned na začátku** a drží ho až do konce. Z toho plyne jedno tvrdé pravidlo:

> **Uvnitř transakce NIKDY nevolej ven** (síť, e-mail/SMTP, cizí API). Drželo by to zámek na zápis po celou dobu toho volání — SMTP může viset, API request klidně 5 minut → celá databáze mezitím nemůže nic zapsat. Volání ven patří **mimo transakci** (run).

- **Command transakci chce** — je **krátký a sahá jen do vlastní DB**. Drží zámek jen chvilku a za to získá jistotu, že se uloží buď všechno, nebo nic.
- **Fire-and-forget i durable run transakci mít nesmějí** — běží na pozadí mimo request a **buď volají ven, nebo trvají dlouho**. Kdyby držely zámek na zápis po dobu volání (fire-and-forget run) nebo minuty/hodiny (durable run), **nikdo jiný by mezitím nemohl nic zapsat** — celá appka by zamrzla. Proto běží mimo transakci; atomicitu „práce + hotovo" nahrazuje **idempotence** (fire-and-forget run se po pádu zopakuje, dokončení je samostatný zápis) a u durable runu navíc **checkpoint** (krátké zápisy postupu mezi kroky).
- **Doménový event** běží **až po uložení dat** (po commitu), takže už není v transakci toho commandu. Běží ale pořád v requestu — **pomalý event brzdí odpověď uživateli**, takže těžkou navazující práci radši **zařaď jako run**.
- **Scheduler** běží jen tak na pozadí, bez transakce. Krátký úklid v transakci je v pohodě; **velkou práci zařaď jako run**, ať dlouhá transakce nezamkne databázi.

### Že žádný run nesmí otevřít transakci, hlídá framework

Aby background práce omylem nezamkla databázi, je to pravidlo **vynucené**, ne jen napsané (platí pro oba tvary — fire-and-forget i durable run):

1. **Za běhu** — když se uvnitř handleru transakce otevře **omylem** (třeba implicitně přes repo/command volání), **selže to s jasnou chybou** (framework označí ten běh jako „bez transakce" a implicitní otevření odmítne). Vědomá krátká transakce jde jedině přes `shared.WithTx`.
2. **Při buildu** — test projde kód run enginu (worker + run vrstva aplikace) a **shodí build**, kdyby transakci otvíral on sám; handlery hlídá ta běhová pojistka z bodu 1.

Postup tedy run ukládá přes `Checkpointer`; když opravdu potřebuješ zapsat víc řádků atomicky, použij `shared.WithTx` — **krátkou transakci, kterou si handler sám ohraničí** (zapiš pár řádků, commit, pokračuj). I pro ni platí tvrdé pravidlo výš: žádné volání ven a žádná dlouhá práce uvnitř — přesně jako v command handleru.


## Související

- Toky: [Command](/framework/command), [Events](/framework/events), [Fire-and-forget](/framework/fire-and-forget), [Durable run](/framework/durable-run), [Scheduler](/framework/scheduler).
- Skilly: `/gk-commands`, `/gk-domain-events`, `/gk-runs`, `/gk-scheduler`, `/gk-bus`.

---

[← Background](/framework/background.md) | [Fire-and-forget →](/framework/fire-and-forget.md)