ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Actual 项目结构深度解析:基于 Yarn Workspaces 的 Monorepo 架构与十个核心包详解

Actual 项目结构深度解析:基于 Yarn Workspaces 的 Monorepo 架构与十个核心包详解 Actual 项目结构深度解析基于 Yarn Workspaces 的 Monorepo 架构与十个核心包详解【免费下载链接】actualA local-first personal finance app项目地址: https://gitcode.com/GitHub_Trending/ac/actualActual 是一个本地优先local-first的个人财务管理应用其代码库被组织为基于 Yarn Workspaces 的 monorepo由loot-core跨平台核心逻辑、desktop-clientReact 前端、sync-server多设备同步服务等十个包协同构成。本文以官方项目结构文档为主线结合各包的package.json与源码目录逐包拆解它们的职责边界、依赖关系与协作方式帮助你快速定位改某个功能该去哪个包并掌握跨包开发的常用命令。Monorepo 组织方式Yarn Workspaces 与根脚本Actual 的所有代码都存放在同一个仓库中由根目录 package.json 统一管理。其 workspaces 配置将所有packages/*目录注册为工作区workspaces: { packages: [ packages/* ] }这意味着yarn install会在仓库根目录统一安装依赖各包之间通过workspace:*协议互相引用无需发布到 npm 即可共享代码详见后文 sync-server 一节。仓库根目录还定义了一批面向整个 monorepo 的常用脚本直接体现了各包在实际运行中的分工start:server: yarn workspace actual-app/sync-server start, start:browser: npm-run-all --parallel start:browser-* start:service-plugins, build:server: yarn build:browser yarn workspace actual-app/sync-server build, build:browser: ./bin/package-browser, build:api: yarn build --scopeactual-app/api, start:docs: yarn workspace docs start, test: lage test --continue, lint: oxfmt --check . oxlint --type-aware --quiet值得注意的是仓库当前的 Node/Yarn 版本约束为node 22.18.0、yarn ^4.9.1packageManager: yarn4.17.1构建与类型检查体系则基于lage、vitest与 TypeScript 的 project references。在开始任何开发之前请先确认本地环境满足这些版本要求。1. loot-coreactual-app/core跨平台核心应用逻辑对应目录packages/loot-coreloot-core 是整个 Actual 的引擎承载业务逻辑、数据库操作与预算计算并刻意保持平台无关——同一份代码既能运行在浏览器环境也能运行在 Node.js 环境。该包在 npm 上的实际名称为actual-app/core其 package.json 通过条件导出conditional exports为不同运行环境提供不同的底层实现例如#platform/server/sqlite: { electron: ./src/platform/server/sqlite/index.electron.ts, api: ./src/platform/server/sqlite/index.api.ts, default: ./src/platform/server/sqlite/index.ts }这种模式在整个包中大量使用——fs文件系统、fetch、connection连接、asyncStorage等模块都针对electron与api环境提供了各自的实现而默认实现则面向浏览器。这是 loot-core 能在浏览器通过 IndexedDB absurd-sql、Electron 桌面端和 Node.js API 中复用同一套核心逻辑的关键机制。从源码结构看其关键目录为src/platform/平台适配层隔离文件系统、SQLite、网络等环境差异src/server/服务端核心逻辑包括db数据库、aql查询语言、budget、importers导入器、rules规则引擎、sync同步、transactions、encryption加密等模块src/client/客户端侧核心逻辑如 Redux store、queries、undo撤销等src/shared/跨端共享的工具与常量src/types/TypeScript 类型定义migrations/数据库迁移文件以 SQL/JS 时间戳命名例如1787013118115_add_account_groups.sql由于 loot-core 被桌面端、浏览器端和 API 三种环境共同消费它也是理解 Actual 一次编写、多端运行架构的入口。2. desktop-clientactual-app/web浏览器与桌面共用的 React 前端对应目录packages/desktop-client尽管包名带有 desktopdesktop-client实际上是 Actual 的 Web 前端——你在浏览器中加载 Actual 时看到的界面就来自这个包因此它被别名注册为actual-app/web。桌面应用也复用了同一套 UI。其技术栈为 React函数式编程风格配合 Vite 构建关键目录包括src/components/React 组件预算表、账户、报表、交易录入等共 500 组件文件src/hooks/自定义 React hooks如useSpreadsheet、useSelected等e2e/基于 Playwright 的端到端测试该包的 package.json 提供了多种启动与构建模式start开发服务器默认端口 3001、start:browser浏览器模式、build供桌面端使用的构建产物。桌面客户端通过actual-app/web的构建产物渲染界面而数据层则来自 loot-core。3. desktop-electronElectron 桌面应用外壳对应目录packages/desktop-electrondesktop-electron是 Actual 桌面应用的 Electron 外壳负责窗口管理、原生系统集成与本地文件访问让 Actual Web App 可以在没有互联网、也没有独立 sync-server 的情况下稳定地在本地运行内置本地同步能力。从 desktop-electron 的 package.json 可以看到它的核心依赖是actual-app/sync-server与better-sqlite3——即桌面端实际上内嵌了一个本地同步服务。其 electron-builder 配置覆盖了 macOSdmgx64/arm64、Linuxflatpak/AppImage与 Windowsappx/nsis三大平台并设置了 Electron Fuses 等安全选项。正如官方文档所言除非你正在处理 Electron 专属特性如窗口、托盘、原生菜单、自动更新否则通常不需要改动这个包。4. apiactual-app/api程序化访问 Actual 数据的公共 API对应目录packages/apiapi包面向集成与自动化场景自定义导入器、数据导出、自动化脚本等。它以 Node.js API 的形式提供对 Actual 预算文件的操作能力如初始化、添加账户/交易、运行规则、查询数据是第三方生态接入 Actual 的官方通道。其 package.json 在exports中同时暴露了 Node 与浏览器入口browser条件导出并直接依赖actual-app/core与actual-app/crdt——这印证了 API 层复用 loot-core 逻辑、并依赖 CRDT 进行数据同步的设计。仓库中的 methods.ts 与 methods.test.ts 展示了 API 方法集合及其测试覆盖。5. sync-serveractual-app/sync-server多设备同步服务对应目录packages/sync-serversync-server 负责在多个设备之间同步预算数据让本地优先的 Actual 具备多设备协作能力。该服务基于 Express当前为 v5并正处于从 JavaScript 向 TypeScript 迁移的过程中源码目录中同时存在.js与.ts文件。它通过convict管理服务端配置并集成了app-gocardless、app-simplefin、app-pluggyai、app-enablebanking、app-akahu等银行同步Bank Sync提供方模块。官方文档特别强调了一个关键依赖关系sync-server 依赖actual-app/web即 desktop-client 包。这一点在 sync-server 的 package.json 中得到证实dependencies: { actual-app/crdt: workspace:*, actual-app/web: workspace:*, express: ^5.2.1, better-sqlite3: ^12.11.1 }workspace:*协议意味着当你部署 Actual Server 并执行yarn build:server后执行yarn install时Actual 客户端会作为 sync-server 的依赖一并安装对应的安装命令是根目录的yarn install:server即yarn workspaces focus actual-app/sync-server --production。这一设计保证了对actual-app/web的修改会反映到你的服务器部署中。如果发现部署产物与代码不一致重新执行yarn build:server即可编译最新版本。6. component-libraryactual-app/components可复用 React UI 组件库对应目录packages/component-librarycomponent-library提供了 Actual 全产品线的共享 UI 基础件Button、Input、Menu、Toggle、Select、Popover、Tooltip、DateRangePicker、MonthPicker、ColorPicker、View、Text等。其特点包括主题系统与设计令牌src/themes/下提供light.css、dark.css、midnight.css、palette.css等主题文件配合 tokens.ts 与 theme.ts 实现统一的视觉规范图标体系src/icons/包含多套自动生成的 SVG/TSX 图标v0/v1/v2 多版本共存文档记载总量 375通过 svgr 从 SVG 生成组件这些图标文件是自动生成的不应手动编辑——修改图标应通过yarn workspace actual-app/components generate:icons重新生成Storybook 文档每个组件都配有.stories.tsx示例可通过start:storybook在 6006 端口浏览组件文档7. crdtactual-app/crdt冲突自由复制数据类型实现对应目录packages/crdtcrdt包实现了 CRDTConflict-free Replicated Data Type这是 Actual 多设备同步的核心算法基础它让不同设备上的并发编辑如同一笔交易的修改在最终合并时保持一致无需中心化的冲突裁决。从 crdt 的 package.json 可以看到其技术选型使用 Protocol Buffersbufbuild/protobufbufbuild/protoc-gen-es进行序列化相关定义位于 packages/crdt/src/proto/sync.proto生成的sync_pb.ts同目录存放并依赖murmurhash用于 Merkle 树哈希与uuid。其核心实现与测试分别在 merkle.ts、timestamp.ts 及对应的.test.ts文件中。sync-server 正是借助 crdt 包实现冲突自由的同步而 loot-core 与 api 也都直接依赖它。8. plugins-service插件/扩展服务对应目录packages/plugins-serviceplugins-service负责 Actual 的插件/扩展能力。从源码结构看其核心入口是 plugin-service-worker.ts一个 Service Worker 形态的插件宿主根目录的start:service-plugins脚本yarn workspace plugins-service watch会在浏览器模式下并行启动它。桌面端构建流程desktop-dependencies也会先执行build:plugins-service说明插件服务是浏览器与桌面端共用的扩展机制。9. eslint-plugin-actualActual 专属 ESLint 规则对应目录packages/eslint-plugin-actual该包以自定义 ESLint 插件的形式固化 Actual 的编码规范。官方文档列出的核心规则如下规则名作用no-untranslated-strings强制使用 i18n禁止硬编码未翻译字符串prefer-trans-over-t优先使用Trans组件而非t()函数prefer-logger-over-console强制使用 logger 而非consoletypography排版相关规则prefer-if-statement偏好显式if语句从 packages/eslint-plugin-actual/lib/rules 目录看实际规则文件共 13 个除上述 5 条外还包括enforce-boundaries强制模块边界、no-anchor-tag、no-enum、no-extraneous-dependencies、no-react-default-import、object-shorthand-properties、prefer-const、prefer-subpath-imports等覆盖了模块边界、依赖管理、React 使用习惯等多个维度。10. docs基于 Docusaurus 的文档站对应目录packages/docsdocs包构建 Actual 的官方文档网站包含本篇文章所在的 contributing 目录。其 package.json 显示它基于 Docusaurus 3docusaurus/corev3.x并集成了easyops-cn/docusaurus-search-local本地搜索、Mermaid 图表支持docusaurus/theme-mermaid、图片缩放插件r74tech/docusaurus-plugin-panzoom等。本地预览文档站使用yarn start:docs构建则使用yarn build:docs。在包之间执行命令yarn workspace 用法由于 monorepo 中的所有包共用一个依赖树对某个具体包执行脚本的标准方式是yarn workspace workspace-name run command。仓库中的实际示例包括# 启动同步服务器 yarn workspace actual-app/sync-server start # 启动前端开发服务器浏览器模式 yarn workspace actual-app/web start:browser # 运行组件库测试 yarn workspace actual-app/components test # 构建文档站 yarn workspace docs build也可以直接在仓库根目录使用更简短的聚合命令如yarn start:browser并行启动前端与插件服务、yarn start:desktop先构建原生依赖再并行启动各桌面子进程、yarn build:server构建浏览器端并编译 sync-server。小结一张图理解包间依赖整体来看Actual 的依赖方向是清晰分层的crdt是最底层的基础算法库被loot-core、api、sync-server共同依赖loot-coreactual-app/core是跨平台核心被前端与 API 复用component-libraryactual-app/components与eslint-plugin-actual是横切支撑件服务于所有 UI 与代码质量场景desktop-clientactual-app/web是唯一的前端界面包被浏览器、桌面端与 sync-server 三处消费sync-server与desktop-electron分别以服务和外壳的形式把上述包组装成可部署、可运行的完整应用plugins-service、api、docs则分别承担扩展能力、程序化访问与文档生态。对于新接触 Actual 的开发者官方还提供了更完整的开发环境搭建指南Development Setup Guide其中覆盖了环境准备、首次启动与常见开发工作流的详细步骤。【免费下载链接】actualA local-first personal finance app项目地址: https://gitcode.com/GitHub_Trending/ac/actual创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进