The three-service backend split
Parent: key-designs
Three separately-deployable services, split by concern and language:
auth (Go/echo, ~2.7k LOC) — identity only. A standalone microservice wrapping Appwrite: POST /api/v1/auth/{login,register,logout,guest,recovery,recovery/reset}, GET/PUT/DELETE /me, and a cron guest-reaper (/cron/cleanup-guests, reaps guests >24 h). HttpOnly-cookie sessions (internal/cookie); the orchestrator composes Appwrite REST + a MainBackend user-purge call, so deleting an account cascades. Deliberately kept apart from the domain API.
mainbackend (Go/echo + pgx + google/wire, ~12.6k LOC) — the domain API + persistence tier. 15 route groups: books, bookmarks, notes, vocabulary (+ all-forms PATCH /:id/forms), reading_prefs, book_settings, catalog/catalog_bytes/covers, dictionary (the L2 shared word cache), review, client_logs, internal_purge, health. It is the server counterpart of the offline-first outbox (offline-first-outbox) — idempotent on duplicate writes so replayed outbox entries are safe — and it owns the SRS: the SM-2 scheduler lives here (internal/domain/review/srs.go), not on the client, so schedules are consistent across web/desktop and ungameable (vocabulary-loop).
nlp-api (Python/FastAPI + spaCy, ~3.6k LOC) — deterministic linguistics + heavy ingestion. The /tokenize, /lookup, /structure (dep-parse grammar), /translate engine (linguistic-two-engines) — plus the PDF/OCR ingestion routers (/parse-pdf, /ocr-images, Tesseract) that handle scanned books the client can't (upload-ingestion).
Why split this way: identity has different security/lifecycle needs than the domain (own service); the linguistics need Python's NLP ecosystem (spaCy, Tesseract, NLLB) so they can't live in the Go tier; and the Go domain API stays a clean, wire-DI'd persistence surface. The dependency-inversion in @lucerna/core (cross-platform-seams) means the shells see all three only as repository interfaces over HTTP.
(i18n note: the product ships 11 locales — en, de, es, fr, ja, ko, nl, pt, zh, zh-HK — as full translation dictionaries in core/src/i18n/, consumed UI-agnostically through the core; mechanically simple but broad.)