三服务后端拆分
上级:key-designs
三个可以分别独立部署的服务,按职责和语言拆分:
auth(Go/echo,约 2.7k 行代码)——只管身份。 这是一个独立的微服务,包装了 Appwrite:POST /api/v1/auth/{login,register,logout,guest,recovery,recovery/reset}、GET/PUT/DELETE /me,以及一个清理游客账号的定时任务(/cron/cleanup-guests,清理超过 24 小时的游客)。会话用 HttpOnly cookie(internal/cookie);编排逻辑把 Appwrite REST 调用和一次 MainBackend 用户清除调用组合在一起,所以删除账号会级联清除。它被刻意与领域 API 分开。
mainbackend(Go/echo + pgx + google/wire,约 12.6k 行代码)——领域 API 与持久化层。 15 个路由组:books、bookmarks、notes、vocabulary(外加处理全部词形的 PATCH /:id/forms)、reading_prefs、book_settings、catalog/catalog_bytes/covers、dictionary(L2 共享词条缓存)、review、client_logs、internal_purge、health。它是离线优先 outbox 的服务端对应物(offline-first-outbox)——对重复写入是幂等的,所以被重放的 outbox 条目是安全的——并且它拥有 SRS:SM-2 调度器就在这里(internal/domain/review/srs.go),而不在客户端,这样 web/desktop 之间的复习计划才能保持一致、无法被作弊(vocabulary-loop)。
nlp-api(Python/FastAPI + spaCy,约 3.6k 行代码)——确定性的语言学处理 + 重型摄取。 /tokenize、/lookup、/structure(依存句法分析语法)、/translate 引擎(linguistic-two-engines)——再加上处理客户端处理不了的扫描版书籍的 PDF/OCR 摄取路由(/parse-pdf、/ocr-images,用 Tesseract)(upload-ingestion)。
为什么这样拆分: 身份认证的安全性与生命周期需求和领域逻辑不同(所以独立成服务);语言学处理需要 Python 的 NLP 生态(spaCy、Tesseract、NLLB),没法放进 Go 层;这样 Go 领域 API 就能保持是一个干净的、用 wire 做依赖注入的持久化层。@lucerna/core 中的依赖反转(cross-platform-seams)意味着各个 shell 只把这三者看成基于 HTTP 的仓储接口。
(i18n 说明:产品发布了 11 种语言—— en, de, es, fr, ja, ko, nl, pt, zh, zh-HK ——作为 core/src/i18n/ 中的完整翻译词典,通过 core 以 UI 无关的方式被使用;机制上很简单,但覆盖面很广。)