Skip to content

3.3 目录结构

学习目标

完成本节后,你将能够:

  1. 按运行职责而不是文件夹名字理解顶层目录。
  2. 知道做不同任务时应该从哪个入口开始追链路。
  3. 识别哪些目录是源码、哪些是契约源、哪些是生成物或构建产物。

3.3.1 目录树只是地图,不是架构

当前 repo 的顶层结构大致是:

text
langfuse/
├── web/                     # Next.js app:UI + tRPC + Public REST + ingestion HTTP
├── worker/                  # BullMQ consumers、后台任务、批量写入和副作用
├── packages/shared/         # 领域模型、队列契约、DB client、query builder、repository
├── ee/                      # Enterprise package,被 web 消费
├── generated/               # 生成的 API clients,不手改
├── fern/                    # Public API 定义源
├── scripts/                 # repo 维护脚本
└── docs/                    # 本书站点和中文架构图

但是这棵树不能直接等同于架构。更准确的读法是:

  • web 是同步入口层,包含浏览器 UI、tRPC、Public API、ingestion HTTP 接收;
  • worker 是异步执行层,消费 Redis/BullMQ job;
  • packages/shared 是跨运行时契约层,让 webworker 对同一 payload、schema、query 语义保持一致;
  • fern 是外部 API contract 的源头;
  • generated 是生成结果,只读;
  • Postgres、ClickHouse、Redis、S3 不在目录树里,但它们是架构的一部分。

3.3.2 web 目录怎么读

web 不是纯前端。它同时承担 UI 和同步服务端入口。

子区域角色先看文件
web/src/pagesNext.js Pages Router,包含 public API file routesweb/src/pages/api/public/**
web/src/server/apitRPC 工厂、context、middleware、router 聚合root.tstrpc.ts
web/src/features/*以产品能力组织 UI、hooks、server router、servicefeatures/events/server/eventsRouter.ts
web/src/components跨 feature UI 组件和表格抽象components/table/**
web/src/stylesTailwind v4 theme、CSS variables、颜色 tokenglobals.css
web/src/features/public-apiPublic API wrapper、auth、rate limit、typesserver/createAuthedProjectAPIRoute.ts
web/src/features/search-barv4 search grammar、adapter、URL/filter contractREADME.md

web 时要先判断入口类型:

这三条入口的鉴权、错误格式和稳定性都不同。不要把 Public API route 当成 tRPC 的另一种写法。

3.3.3 packages/shared 目录怎么读

packages/shared 的名字容易误导。它不是“公共 helper 集合”,而是系统契约中心。

子区域角色为什么重要
src/domain/**Trace、Observation、Score、Prompt 等领域 schemaUI、API、worker、tests 都需要同一语义。
src/server/queues.tsQueueName、QueueJobs、payload Zod schema、job type mapproducer/consumer 跨进程共享协议。
src/server/redis/**BullMQ queue classes、sharding、Redis helpersproducer 创建 typed queue,worker 注册对应 queue。
src/server/ingestion/**request-side ingestion validation、S3 path、batch enqueueSDK event 进入数据面的第一层 contract。
src/server/queries/**ClickHouse query builders、filter lowering、field mapsUI filter 和 Public API 查询不能手写散落 SQL。
src/server/repositories/**Postgres/ClickHouse repository 和 record schema写入/读取同构,处理 DB row 到 domain 的转换。
prisma/schema.prismaPostgres schema 源头事务状态和关系模型。
clickhouse/migrations/**ClickHouse schema 源头高吞吐事件和分析表。

判断一个新类型是否该进 shared,核心问题是:它是否被两个运行时共同理解?

如果只是一个 React component 的 props,留在 web。如果它会进入 Redis job、S3 path、ClickHouse row、Public API response、FilterState,就要按 contract 对待。

3.3.4 worker 目录怎么读

worker 的入口不是 HTTP route,而是 worker/src/app.ts 中的队列注册。

子区域角色先看文件
worker/src/app.ts根据 env 开关注册 BullMQ worker看哪些 queue 会在当前部署消费。
worker/src/queues/**每类 job 的 processor看 job 如何解析 payload、调用 service、处理失败。
worker/src/services/IngestionServiceingestion 业务转换核心trace/observation/score/dataset_run_item 的 merge、enrich、validate。
worker/src/services/ClickhouseWriterClickHouse 批量写入策略按表缓冲、flush、retry、截断、dropped metrics。
worker/src/features/**后台清理、retention、metrics runner 等长期任务看启动条件和失败语义。

worker/src/app.ts 的工作模式是:

所以读 worker job 时,顺序应该是:

  1. packages/shared/src/server/queues.ts:job payload contract;
  2. producer:谁调用 .add(QueueJobs...)
  3. worker/src/queues/**:consumer 怎么处理;
  4. worker/src/app.ts:是否注册、concurrency 和 limiter 是多少;
  5. service:真正业务是否被抽出来复用;
  6. failure semantics:throw、retry、secondary queue、业务失败状态分别怎么处理。

3.3.5 ferngeneratedee 怎么看

目录角色修改规则
fern/apis/**Public API 定义源对外 API 变更要从这里同步。
generated/**API client 或生成产物不手改。改 contract 后重新生成。
ee/**Enterprise packageweb 消费,仍依赖 @langfuse/shared

Public API 不是只改 web/src/pages/api/public/**。如果请求、响应、错误或分页行为是外部用户可见的,就要同步 Fern 和生成物。

3.3.6 任务驱动的打开顺序

任务第一入口第二入口第三入口
看 UI 列表查询web/src/features/events/server/eventsRouter.tseventsService.tsevent-query-builder.ts
看 Public APIweb/src/pages/api/public/**withMiddlewares.ts / createAuthedProjectAPIRoute.tsfern/apis/**
看 ingestion 写入ingestion routeprocessEventBatch.tsworker/src/queues/ingestionQueue.ts
看 worker jobpackages/shared/src/server/queues.tsproducer .add(...)worker/src/app.ts + processor
看筛选系统web/src/features/search-bar/README.mdpackages/shared/src/interfaces/filters.tsquery builder / filter lowering
看颜色系统web/src/styles/globals.csscomponent class usagedocs theme if改本书站点

3.3.7 不要手改这些目录

路径原因
generated/*生成物。
web/.next/*web/.next-check/*Next.js 构建产物。
*/dist/*package 构建产物。
packages/shared/prisma/generated/*Prisma 生成物。
.vitepress/dist/*本书站点构建产物。

这些路径可以被构建命令重建。手改只会制造不可追踪差异。

3.3.8 本节总结

目录结构的正确读法是:

text
入口在哪里 -> 契约在哪里 -> 执行器在哪里 -> 状态在哪里 -> 失败如何恢复

只有把目录放回运行链路里,才能看出 websharedworker 如何组合成一个系统。

下一节

依赖方向