使用 Kotlin Compose Multiplatform 实现的全平台同步 Reminder App
Sundial 把待办拆成四个自然步骤:先捕获到收件箱,再安排到今天或未来,随后在工作台按压力分组推进,最后用分析页复盘完成趋势与精力输出。
- 工作台:默认展示所有未完成事项,并按逾期、今天、未来、无日期、待整理分组展开,第一屏就是当前压力总览。
- 列表:用于维护真正的清单边界,每个列表都有自己的待办分布和后续分析入口。
- 收件箱:只负责快速捕获临时事项,提醒用户之后归类或安排,不再混作顶层工作流。
- 分析:使用图表呈现完成趋势、连续记录、完成率与精力输出,让复盘成为推进动力。
- 同步:本地优先,可切换 Supabase 多端同步,桌面和移动端共享同一套数据模型。
| 层 | 技术 |
|---|---|
| UI | Compose Multiplatform(共享 UI) |
| 架构 | 分层架构 + Arrow typed effects + 窄端口 use-case 层 |
| 数据库 | SQLDelight(SQLite),本地优先 |
| 同步 | supabase-kt(PostgREST + Realtime),SyncClient 端口可插拔 |
| 日期 | kotlinx-datetime |
| 依赖注入 | 手写 AppGraph 懒加载图(无框架) |
flowchart TB
subgraph UI["UI 层"]
Screens["Compose 屏幕"]
ViewModels["ViewModel / 路由状态机"]
end
subgraph Domain["领域层"]
RepoPort["TodoQueries / TodoCommands"]
MorePorts["ListCommands / SyncStore / SettingsStore"]
SyncPort["SyncClient 端口"]
UseCases["用例"]
end
subgraph Data["数据层"]
Impl["SQLDelight 双写 outbox"]
LWW["SQL 层 LWW"]
Noop["NoopSyncClient"]
Supa["SupabaseSyncClient"]
Server["Sundial-Server(预留)"]
end
DB[("本地 SQLite")]
Supabase["Supabase(PostgREST + Realtime)"]
Screens --> ViewModels
ViewModels --> UseCases
ViewModels --> RepoPort
UseCases --> RepoPort
UseCases --> MorePorts
RepoPort --> Impl
MorePorts --> Impl
Impl --> LWW
Impl --> DB
SyncPort --> Noop
SyncPort --> Supa
SyncPort --> Server
Supa <--> Supabase
Domain -. 依赖注入 .-> Data
架构边界简述:
- 核心工作流写入规则通过 use case 调用窄端口,例如工作台/主列表完成切换、日程安排、列表保存、设置保存;简单详情编辑与子任务编辑仍可直接调用命令端口。
- 查询仍保持
Flow,服务 Compose 的响应式状态;写命令使用 ArrowEither表达可见错误。 - 同步编排依赖
SyncStore+SyncClient,运行时生命周期由 Resource/lease 风格模块管理。
同步核心设计:
- 每次本地写入在同一事务内更新数据行并追加一条 outbox 记录(完整行 JSON 快照)
SyncCoordinator批量推送 outbox,按seq水位线清理——不丢、不重- 远端变更通过 SQL 层 LWW(
updated_at大者胜)应用,自身设备回声自动过滤 SyncClient端口是未来的 seam:新增自建服务器只需实现一个适配器,其余代码零改动
- JDK 17+
- Android Studio(Android / iOS 构建)
- Xcode(iOS 构建)
./gradlew :desktopApp:run./gradlew :androidApp:assembleDebug./gradlew :shared:desktopTest :androidApp:assembleDebug- 在 Supabase 创建项目
- 在 SQL Editor 执行 docs/sync-setup.md 中的初始化脚本
- 应用内「设置 → Supabase 云端」填入项目 URL 与 anon key,保存即可
⚠️ 当前为无登录模型,RLS 对 anon 全开,仅适合个人自用。安全边界详见 docs/sync-setup.md。
shared/ 共享代码(UI + 领域 + 数据)
src/commonMain/kotlin/
com/myapplication/shared/
domain/ 领域模型、仓库端口、同步端口、用例
data/ 仓库实现、SQLDelight、同步适配器与引擎
ui/ Compose 屏幕、组件、主题、ViewModel
util/ 日期解析与格式化
src/commonMain/sqldelight/ SQLDelight schema 与迁移
src/{android,desktop,ios}Main/ 平台驱动与入口
desktopApp/ 桌面应用入口
androidApp/ Android 应用入口
docs/ 架构决策(ADR)、Supabase 配置指南、Logo
TODO.md 已知问题与待办
- 行级 LWW 在双端同时编辑同一行时可能丢更新(个人使用可接受)
- 无墓碑机制:晚到的旧写入可能复活已删除的行
- 拉取为全量(无增量水位线),数据量增大后需换
since增量
Copyright © 2026 Somhairle H. Marisol
