Jak jsem vytvořil SaaS s 100 000+ uživateli – case study fakturujzdarma.cz
Tohle není článek o tom, jak vytvořit SaaS za víkend. Je to příběh systému, který dnes zpracovává faktury pro více než 100 000 uživatelů — a co všechno muselo fungovat, aby se to povedlo.
Jmenuju se Jakub Dostál a fakturujzdarma.cz je jeden z projektů, na kterém pracuji jako fullstack vývojář a technický architekt. V tomhle článku rozeberu celý projekt od začátku — jaký byl problém, jak jsem přistoupil k řešení, jaké technologie jsem zvolil a proč, a co jsem se naučil z provozu systému v produkci.
Výchozí situace — co a proč
Fakturujzdarma.cz je online fakturační systém. Uživatelé v něm vystavují faktury, spravují klienty, sledují platby a řeší základní účetní agendu. To zní jednoduše — ale realita je složitější.
Klíčové požadavky na systém od začátku:
- Nulová bariéra vstupu — registrace zdarma, bez kreditky, okamžité použití
- Spolehlivost — faktura musí odejít včas, data nesmí zmizet
- Výkon — systém musí zvládat tisíce současných uživatelů bez degradace
- Mobilní přístup — podnikatelé fakturují z telefonu, ne jen z desktopu
- Automatizace — upomínky, pravidelné faktury, notifikace po splatnosti
Z pohledu vývoje na míru šlo o typický případ, kdy hotová řešení na trhu buď nestačila funkčně, nebo měla nevyhovující cenový model.
Volba technologického stacku
Tohle je rozhodnutí, které ovlivní všechno další. U SaaS systému, kde očekáváte desítky tisíc uživatelů, nemůžete sáhnout po čemkoli.
Backend: Go (Golang) + Fiber
Go jsem zvolil z několika konkrétních důvodů:
- Výkon — Go je kompilovaný jazyk. Jeden API server zvládne tisíce requestů za sekundu s minimální spotřebou paměti. U fakturačního systému, kde se generují PDF, počítají DPH a odesílají emaily, je to zásadní.
- Jednoduchost — Go nemá dědičnost, generika přišla pozdě, a to je vlastně výhoda. Kód je čitelný i po dvou letech. Žádná magie, žádné frameworkové obskurity.
- Concurrency — Goroutiny a channels. Když potřebujete odeslat 500 upomínek najednou, nemusíte řešit thread pool management.
- Deployment — Výstup je jeden binární soubor. Žádné runtime závislosti, žádný node_modules.
Jako HTTP framework jsem použil Fiber v2 — výkonný, s dobrou middleware podporou a Express-like API, které zrychluje vývoj.
Frontend: SvelteKit
SvelteKit jsem zvolil proto, že:
- Rychlost renderingu — Svelte generuje vanilla JavaScript, žádný virtual DOM overhead. Pro SaaS aplikaci, kde uživatel tráví hodiny, je rychlost UI kritická.
- SSR + SPA hybrid — Server-side rendering pro SEO stránek (landing pages, nápověda), SPA režim pro samotnou aplikaci.
- Bundle size — SvelteKit produkuje výrazně menší bundles než React nebo Vue. To se projeví hlavně na mobilu.
- Developer experience — Reaktivita je přirozená, ne přilepená. Méně boilerplate = rychlejší iterace.
Databáze: PostgreSQL + Redis
PostgreSQL jako primární databáze. U finančních dat není prostor pro experimenty s NoSQL — potřebujete ACID transakce, foreign keys a spolehlivé zálohy.
Redis pro:
- Session management
- Rate limiting
- Cache frekventovaných dotazů (seznamy klientů, šablony faktur)
- Job queue pro asynchronní úlohy (odesílání emailů, generování PDF)
Mobilní aplikace: Capacitor
Nativní iOS a Android aplikace přes Capacitor. SvelteKit frontend zabalený do nativního shellu s přístupem k systémovým API (push notifikace, kamera pro sken dokladů). Jeden codebase pro web i mobil.
Architektura systému
Architektura je navržená pro horizontální škálovatelnost a oddělení zodpovědností:
┌─────────────┐ ┌──────────────┐ ┌─────────────┐
│ SvelteKit │────▶│ Go / Fiber │────▶│ PostgreSQL │
│ Frontend │ │ REST API │ │ │
└─────────────┘ └──────┬───────┘ └─────────────┘
│
┌──────┴───────┐
│ Redis │
│ Cache + Jobs │
└──────────────┘ API design
REST API s konzistentní strukturou. Každý endpoint vrací standardní formát:
{
"data": { ... },
"meta": { "page": 1, "total": 342 }
} Autentizace přes HTTP-only cookies s tokenem. Žádné JWT v localStorage — bezpečnost je u finančních dat priorita.
Klíčové moduly
Systém je rozdělen do modulů, které spolu komunikují přes interní API:
| Modul | Zodpovědnost |
|---|---|
| Users | Registrace, autentizace, profily, nastavení |
| Invoicing | Vytváření, editace, odesílání a správa faktur |
| Clients | Databáze klientů, IČO lookup, automatické doplňování |
| Payments | Sledování plateb, párování s fakturami |
| Notifications | Email notifikace, upomínky po splatnosti, push |
| PDF Engine | Generování PDF faktur z šablon |
| Admin | Správa uživatelů, audit log, monitoring, ban systém |
| Scheduler | Pravidelné faktury, automatické upomínky, cron úlohy |
Problémy, které jsem musel vyřešit
Žádný projekt v produkci nejde hladce. Tady jsou problémy, které jsem řešil — a jak.
1. Generování PDF ve velkém
Tisíce faktur denně potřebuje robustní PDF engine. První verze používala HTML-to-PDF konverzi přes headless Chrome. Fungovalo to — ale bylo to pomalé a paměťově náročné.
Řešení: Přepsal jsem generování na nativní Go knihovnu, která staví PDF přímo z dat. Žádný browser, žádný rendering engine. Výsledek: 10× rychlejší generování, 5× menší paměťová stopa.
2. Upomínky a automatizace po splatnosti
Systém automaticky odesílá upomínky, když faktura není zaplacena po splatnosti. Zní to jednoduše, ale:
- Co když je email nedoručitelný?
- Co když uživatel mezitím fakturu zrušil?
- Co když server restartuje uprostřed odesílání 200 upomínek?
Řešení: Redis-based job queue s at-least-once delivery. Každá upomínka je idempotentní úloha — i kdyby se spustila dvakrát, výsledek je stejný. Stav se zapisuje do PostgreSQL jako audit trail.
3. Vyhledávání a filtrování
100 000+ uživatelů, miliony faktur. Full-text search přes PostgreSQL tsvector s GIN indexy. Pro seznamy klientů a faktur jsem implementoval cursor-based pagination místo OFFSET — s rostoucím datasetem je to řádově rychlejší.
4. Multi-tenant bezpečnost
Každý uživatel vidí jen svá data. Zní to samozřejmě, ale u SaaS na míru je to nejkritičtější aspekt celého systému. Každý databázový dotaz prochází middleware, který automaticky přidává WHERE user_id = ?. Žádný endpoint nemůže vrátit data jiného uživatele.
5. Rate limiting a ochrana proti zneužití
Veřejně přístupný SaaS systém přitahuje boty, scrapery a brute-force útoky. Redis-based rate limiter na úrovni IP i uživatele. Admin dashboard s přehledem podezřelých loginů, IP blacklist a možností blokace účtů.
Admin dashboard
Backend admin systém jsem navrhl jako samostatný modul s vlastní autentizací. Klíčové funkce:
- Dashboard se statistikami — počty uživatelů, faktur, denní grafy aktivity
- Správa uživatelů — vyhledávání, detail, historie aktivity, ban/unban
- Audit log — kdo co kdy udělal, veškeré administrátorské akce
- Monitoring loginů — neúspěšné pokusy, locked účty, IP analýza
- Impersonace — možnost přihlásit se jako uživatel pro debugging (s audit logem)
Admin autentizace je oddělená od běžných uživatelů — vlastní session cookie s 24h expirací, sledování neúspěšných pokusů a automatický lock po opakovaném selhání.
Výsledky a čísla
Po několika měsících provozu systém dosáhl:
| Metrika | Hodnota |
|---|---|
| Registrovaní uživatelé | 100 000+ |
| Dostupnost | 99.9%+ |
| Průměrná odezva API | pod 50 ms |
| Generování PDF | pod 200 ms / faktura |
| Velikost frontendu | pod 150 kB gzip (initial load) |
Infrastruktura běží na VPS s Dockerem. Žádný Kubernetes, žádné over-engineering. Pro aktuální zátěž je to dostatečné a provozní náklady zůstávají nízké.
Co jsem se naučil
Několik lekcí z vývoje a provozu production SaaS systému:
1. Začněte jednoduše, škálujte až musíte. Původní architektura byla jednoduchá — jeden Go server, jeden PostgreSQL, jeden Redis. Microservices by v počáteční fázi přidaly komplexitu bez benefitu.
2. Automatizace šetří čas vám i uživatelům. Automatické upomínky, pravidelné faktury, IČO lookup — každá automatizovaná funkce snižuje support tikety a zvyšuje retenci. Investice do automatizace se vždy vrátí.
3. Bezpečnost není feature, je to základ. U finančních dat nemůžete řešit bezpečnost „potom”. HTTP-only cookies, rate limiting, audit log, oddělená admin autentizace — to všechno musí být od první verze.
4. Monitoring > testování. Testy zachytí známé problémy. Monitoring zachytí neznámé. V produkci s 100 000 uživateli se dějí věci, které jste nepředpokládali. Logování, metriky a alerting jsou důležitější než 100% code coverage.
5. Výběr technologií rozhoduje. Go + SvelteKit + PostgreSQL je stack, který škáluje bez bolesti. Kdybych začínal dnes, zvolil bych to znovu. Důvody jsem popsal v článku Jak vybrat technologie pro startup.
Pro koho má smysl podobný přístup
Vývoj SaaS na míru není pro každého. Má smysl, pokud:
- Máte validovaný produkt nebo silný trh
- Potřebujete plnou kontrolu nad daty a funkcemi
- Hotová řešení vás omezují (funkčně nebo cenově)
- Plánujete růst a potřebujete škálovatelnou architekturu
Pokud zvažujete vlastní SaaS projekt a chcete si promluvit o architektuře, technologiích nebo celkovém přístupu — ozvěte se mi. Rád proberu vaši situaci a řeknu, jestli vývoj na míru dává smysl, nebo jestli existuje jednodušší cesta.