想去哪里?

首页 页面 关于 页面 项目 页面 简历 页面 技术 页面 随笔 页面 联系 页面 隐私政策 页面 Terminal Spike 隐私政策 页面 用 Astro 构建文档与职业个人网站 技术 通过 tmux、Mosh、Termius 与 Tailscale 随处使用 Codex 技术 用 Langfuse 观察 Codex 的 MCP 工具调用 技术 产品应用中的本地化 技术 把 MCP 作为安全的 AI 集成边界 技术 用 Zod、OpenAPI 与 Swagger 维护 API 契约 技术 用 pg-boss 运行持久后台任务 技术 通过真实知识工作流理解 pgvector 与 RAG 技术 Pi Edge:让 Pi 待在 Obsidian 笔记旁 技术 为真实排序任务设计拖放交互 技术 Prisma 与 PostgreSQL:产品状态的事实来源 技术 把 Ralph Loop 当作计划队列 技术 用 React Router 组织全栈产品工作流 技术 把 shadcn 风格组件变成自己的产品系统 技术 用表格与编辑器承载密集业务操作 技术 Terminal Spike:原生 Android 终端 技术 在 Linux 上用 fcitx 输入带声调的拼音 技术 在 Linux 上配置 DEFT Pro 轨迹球 技术 在 KOReader 上使用 Pocket 稍后读 技术 为 Vercel AI SDK 设置明确的工具边界 技术 垂直切片架构与 dependency-cruiser 技术 用 Vitest 与 Playwright 测试产品工作流 技术 我的随处工作流:Herdr、code-server、Obsidian 与 Pi Edge 技术 Zod 不只是校验 技术 《卡拉马佐夫兄弟》是我看过最好的小说 随笔 基督徒口述1960劳改历史 随笔 圣经中的“道”与言语 随笔 我们爱,因为神先爱我们,而不是空洞的“自爱” 随笔 妈妈,让我来承担爸爸的过错吧 随笔 我与神的故事 随笔 世界远离上帝,接近中国 随笔 “爱的五种语言”和内外向,只存在于语言中 随笔 人格类型不存在,你在世界而非概念中生活 随笔 危险的词语:为什么心理学是不可能的 随笔 2000 年,一个深圳打工者的故事 随笔 阿联酋Ras al Khaimah见闻录 随笔 沙甸:1975 年的一场冲突 随笔 关于中国的纪录片 随笔
← 技术/工程实践

垂直切片架构与 dependency-cruiser

通过功能切片和依赖规则,让产品工作流更容易修改。

我喜欢垂直切片,因为功能更容易删除、移动或审查。价值不在目录名,而在于一个工作流的代码不再散在十个无关位置。

一个用户任务往往跨越 UI、校验、服务、数据访问和测试。只按技术层分目录时,小改动也要从 components 跳到 schemas、services 和 repositories,再猜哪些属于同一功能。

垂直切片让功能规则靠近功能本身:

app/features/imports/
  components/
  imports.schema.ts
  imports.service.ts
  imports.repo.ts
  imports.test.ts

这只是通用例子,可以是导入、通知设置或审核步骤。关键是经常一起变化的代码放在一起。

共享代码仍有位置。按钮、对话框基础组件、日期格式、数据库客户端和认证 helper,不应复制到每个切片。但功能专属规则,也不应只因为今天两个文件用到,就搬进全局 helper。

架构难处在这里:起初无害的 helper,后来可能成为不相关流程共享隐藏行为的地方,改一个流程就意外影响另一个。

我的原则是:共享 UI 基础组件和基础设施可以共享;产品决策在真正通用之前,应留在所属流程。

文字约定忙起来容易忘。dependency-cruiser 能把一部分边界变成 CI 反馈。例如功能可以引用共享代码,共享代码不能反过来引用功能:

{
  name: "shared-must-not-import-features",
  from: { path: "^app/shared" },
  to: { path: "^app/features" },
  severity: "error"
}

这条规则并不聪明,只是在普通错误成为惯例前抓住它。

规则也有维护成本。太严格,工程师会忙着对抗架构;太松,则保护不了什么。我不认为它们是永久法律,而应描述仓库当前需要的结构,产品变化时规则也可能变化。

很小的应用未必值得严格切片,库的使用方式也不一定适合功能目录。但有多个工作流的产品,值得明确问:“这个用户任务究竟拥有哪些代码?”这比争论文件夹名字有用。