forming / System Design Archive

Projection Clients

Projection Clients 把 Web、PWA/TWA、Android Native、Search、CLI 和 AI Agent 都定义为同一套 Runtime/Object Graph 的不同投影;允许 UI 不同,但禁止复制业务逻辑、authority、解析、搜索和同步规则。

MyBlog Runtime APIKnowledgeObject GraphAstro Web ProjectionPWA / TWAAndroid Compose ClientLocal Runtime Cache
Inspiration

借鉴对象

  • Immich mobile architecture
  • Mihon source/runtime model
  • Read You feed client
  • EhViewer runtime client patterns
  • Notion multi-client API
Rejected

拒绝的方案

  • 把 Android 做成 WebView 套壳博客,因为它会把 App 降级成网页容器,无法承担离线、缓存、下载和原生阅读。
  • 让 Android 重新实现 OpenList、metadata、search、book existence、graph 和 sync 逻辑,因为这会制造第二套业务真相。
  • 在 Native App 初期直接重写全部 UI,因为当前 Web Runtime 和 Reader 仍在快速演化,过早分叉会拖慢 authority 收敛。
  • 把 PWA/TWA 写成终点,因为它只是低成本安装面,不替代后续真正的 native runtime client。
Runtime

运行方式

  • 唯一真源层仍是 OpenList/COS、MySQL、Directus target、Meilisearch target、Immich target 和 KnowledgeObject graph。
  • Runtime API 只能有一套:/api/feed、/api/books、/api/visuals、/api/search、/api/graph、/api/runtime/*。Web 和 Android 都消费同一套合同。
  • Web 当前是 Astro Projection Shell;Android 未来只能是另一个 Projection Surface,不得拥有 book existence、metadata authority、OpenList parsing 或 search ranking authority。
  • DataBase Gateway access is centralized in apps/admin-next/lib/database-gateway-client.mjs: it loads @emptyinkpot/database-gateway-generated-client when present and otherwise preserves the same Gateway HTTP contract. MyBlog code must not scatter raw DataBase route strings outside this adapter.
  • Phase 1 已建立 apps/android-shell skeleton,并在 apps/web/public/manifest.webmanifest 与 apps/web/public/sw.js 提供 Web PWA surface:目标是 PWA + Bubblewrap / Trusted Web Activity,快速得到可安装 Android 包,更新仍随 Web Runtime 发布。
  • Android TWA 现在由 tools/generate-android-twa.mjs 自动生成:npm run android:twa:validate 校验本地和线上 PWA、service worker 与 Digital Asset Links,npm run android:twa:generate 生成 .runtime/android-twa,npm run android:twa:build 生成未签名 APK/AAB,npm run android:twa:build:test-signed 生成本机测试签名 APK。
  • apps/web/public/.well-known/assetlinks.json 是 TWA 信任声明;它必须匹配 apps/android-shell/twa.contract.json 的 packageId 和 SHA256 指纹,否则 Android 不能把 blog.tengokukk.com 交给可信 Web Activity。
  • Phase 2 是 Runtime API 化:把 Feed、Books、Visuals、Search、Graph 明确成稳定 API 和 schema。
  • Phase 3 才是 Kotlin + Compose Native Runtime Client:Compose UI、ViewModel + Flow、Ktor/Retrofit、Coil、Room、PdfRenderer / EPUB runtime、AppUpdater。
  • packages/runtime-contract 和 packages/object-model 是 Web、Android、Search、CLI 和 AI Agent 的共享合同入口;它们不是数据真源。
  • Native 允许拥有 local runtime cache、离线阅读、图片预加载、后台下载、系统分享和本地数据库,但缓存只能 mirror runtime,不能成为上游 truth。
  • PWA service worker 只缓存静态页面和 build assets,不拦截 /api/*、/openlist/*、/reader/openlist、/books/openlist 或 HTTP Range 请求,避免污染 Runtime API 与 EPUB/PDF reader bytes。
  • npm run check:pwa 校验 manifest、标准图标尺寸、service worker 边界、BaseLayout 注册和 apps/android-shell/twa.contract.json;它已接入 npm run check。
  • 自动更新路线优先 GitHub Releases + AppUpdater;F-Droid repo 可作为开源分发后续选项。
Tradeoff

取舍

  • PWA/TWA 速度最快,但原生能力有限;适合当前 Runtime 尚未完全 API 化的阶段。
  • Compose Native 体验上限更高,但只有在 Runtime API 稳定后才不会复制业务逻辑。
  • 本地 Room cache 能带来离线和速度,但必须有明确 invalidation 与 sync contract,避免 shadow authority。
  • 多 surface 会增加测试矩阵,但如果 Runtime 合同稳定,维护成本仍低于多套系统。
Future Direction

后续方向

  • 定义 /api/feed、/api/books、/api/visuals、/api/search、/api/graph 的 response schema 和 versioning。
  • 部署 Web PWA surface 后,用 Lighthouse / Chrome installability 检查线上 manifest、service worker scope、icon 和 standalone display。
  • 让 apps/android-shell 承接 Bubblewrap 配置,并在 installability 通过后生成 TWA 工程;CI 入口是 .github/workflows/android-twa.yml,生成物只作为 artifact 上传。
  • 建立 android-client target repo 或 apps/android 前,先冻结 Runtime API schema。
  • 为 Android native cache 定义 Room schema、sync watermark、etag/version 和 eviction policy。
  • 让 Web、Android、Search 和 AI Agent 都通过 KnowledgeObject projection 消费同一对象图。