MemoFlow · Electron Desktop 开发笔记
按版本记录 Nx Monorepo、Electron IPC、本地持久化与独立 Profile 的架构设计。
Desktop 模块目标
我想在 DailyUse 的 Nx Monorepo 中实现 Electron Desktop,实践一种以多个业务 Package 拼装应用的方式。Desktop 不再是重新写一套业务代码的独立项目,而是提供窗口、文件系统、本地数据库和 IPC 等宿主能力的容器。
V1.0.0
先确定一个原则:业务能力放在可复用的 Package,Electron 主要负责把这些能力装配起来。
以创建 Goal 为例,流程是:
- Vue 页面打开创建表单并做输入校验。
- 客户端 Application 组织创建请求,通过基础设施层调用 IPC。
- Electron 主进程接收请求,再调用服务端 Application。
- 服务端应用服务使用 Goal 领域规则和 Repository 完成校验与持久化。
- 结果沿 IPC 返回,客户端更新界面。
这与把数据库操作直接写在 Vue 组件里不同。Presentation 只关注交互,Application 管用例,Domain 管业务规则,Infrastructure 负责连接 IPC、数据库等技术细节。
v1.1.0
Desktop 需要在离线时也能工作,因此本地持久化先选择 SQLite + better-sqlite3。
SQLite 不需要单独部署服务器,适合桌面端的本地事务;better-sqlite3 则提供 Node/Electron 主进程中的数据库访问能力。数据库连接与各模块的 Schema 由主进程统一装配,渲染层不能直接写 SQL。
同时要注意 Electron 的两个环境不是一回事:渲染进程运行网页 UI,主进程拥有 Node/OS 能力。跨过这条边界的请求需要明确的 IPC Contract、校验和错误传递。不能因为“都在一台电脑上”,就把文件系统和数据库权限全部暴露给渲染层。
V2.0.0
现在准备让 Web 和 Desktop 复用更多业务代码。目标不是让两端都使用 Electron IPC,而是让它们共享领域模型、用例和输入输出契约,再在宿主层选择不同 Adapter。
例如同一项业务在 Desktop 走 IPC 与本地 Repository,在 Web 走 HTTP 与服务端 Repository;客户端业务组件尽量不需要知道底下是哪一种 Transport。
这样能够减少同一个 Goal、Task 规则在两端出现两套实现。但抽象也不能过度:主进程文件访问、系统通知、窗口管理等就是 Desktop 专有能力,不需要伪造一个在 Web 里永远不可用的同等实现。
v2.1.0
多个账号开始使用 Desktop 后,光有“当前登录用户”还不够。需要设计稳定的 Local Profile,让不同用户的数据库、Vault、配置和运行态互相隔离。
Guest 也应该能在自己的 Profile 中离线使用业务;登录云端时,只建立明确的关联,不自动替换原本的本地容器。主进程负责 Profile 路径与数据库装配,渲染层按当前 Profile 读取业务,不能在账号切换后继续沿用上一个用户的状态。
v2.2.0
当 Desktop 要打包发布时,还得专门处理运行环境与开发环境的区别。
开发时能工作的依赖,在 Electron 打包后未必有相同的文件路径和加载方式。尤其是 better-sqlite3 这样的原生模块,需要关注 Electron ABI、构建产物位置以及 ESM/CommonJS 边界。
因此主进程启动、数据库初始化、用户数据路径、更新和打包后的基础业务流程都需要单独验证。Desktop 是宿主容器,但绝不是一个只负责打开网页的空壳。