架构边界改造大纲
本改造的目标不是减少项目数量,而是让每个入口的职责稳定下来。IAM 是认证中心,认证、授权、OAuth/OIDC、审计与会话管理必须有唯一的后端权威入口;Next.js 主要承担管理控制台、登录页、授权页和用户交互。
apps/ web/ # Next.js 前端应用:页面、组件、交互、轻量代理 server/ # Hono 后端应用:唯一 API/Auth/OIDC 入口 docs/ # Astro Starlight 文档站点
packages/ api/ # tRPC routers 与业务 API auth/ # Better Auth 服务端配置、插件、hooks、企业微信认证能力 db/ # Drizzle schema、db client、migrations env/ # 环境变量定义,区分 server/web redis/ # Redis/secondary storage 客户端 contracts/ # 前后端共享的纯类型、枚举、Zod schema config/ # 共享 TypeScript/工具配置apps/server
Section titled “apps/server”apps/server 是唯一后端入口,负责:
- 挂载 Better Auth:
/api/auth/* - 暴露 OAuth/OIDC Provider 相关端点,包括 well-known metadata
- 挂载 tRPC:
/trpc/* - 处理文件上传、企业微信回调、同步任务、内部运维接口
- 直接使用
@IAM/auth、@IAM/db、@IAM/env/server
apps/web
Section titled “apps/web”apps/web 是前端应用,负责:
- 页面路由、布局、管理后台、登录/授权 UI
- 使用
authClient调用 Better Auth 客户端插件能力 - 使用 tRPC client 调用业务 API
- 通过
/api/*和/trpc/*代理到apps/server
apps/web 不应直接 import:
@IAM/auth@IAM/db@IAM/env/server
如果前端需要共享类型,应从 @IAM/contracts 引入纯类型,不从服务端包中借类型。
packages/auth
Section titled “packages/auth”packages/auth 只提供服务端认证能力,负责:
- Better Auth 实例配置
- Better Auth plugins、hooks、provider
- 企业微信 OAuth provider 与同步能力
- 调用 Drizzle adapter 与数据库 hooks
它可以依赖 @IAM/db、@IAM/env、@IAM/redis,但不应依赖 apps/web。
packages/api
Section titled “packages/api”packages/api 是业务 API 层,负责:
- tRPC router 定义
- 标准
protectedProcedure权限校验 - 无法直接用 Better Auth 原生客户端 API 满足的业务聚合
- IAM 管理后台需要的统计、搜索、OAuth 客户端扩展配置
优先使用 Better Auth 已有 API;只有跨表统计、深度筛选、OAuth 管理控制台扩展字段等场景才写自定义 tRPC/Drizzle 逻辑。
packages/contracts
Section titled “packages/contracts”packages/contracts 只放纯共享契约,负责:
- 前后端共用类型
- 枚举、常量、Zod 输入 schema
- 不访问数据库、不读取环境变量、不初始化 Better Auth
第一阶段:切断前端对服务端包的直接依赖 ✅
Section titled “第一阶段:切断前端对服务端包的直接依赖 ✅”- ✅ 新增
@IAM/contracts - ✅ 将
apps/web中仅为类型用途的@IAM/authimport 改为@IAM/contracts - ✅ 删除/迁移前端直连服务端包的遗留实现,
apps/web不再直接调用auth.api与 Drizzle - ✅
apps/web/package.json已移除@IAM/auth、@IAM/db、@IAM/env
第二阶段:统一 API 访问路径 ✅
Section titled “第二阶段:统一 API 访问路径 ✅”- ✅ 登录、授权、会话、管理员用户管理走
authClient - ✅ 自定义业务能力走 tRPC(
packages/api) - ✅ 特殊 multipart 上传保留在
apps/serverHono REST endpoint - ✅
apps/web/app/api/[...path]/route.ts与app/trpc/[...path]/route.ts仅作代理,不承载 IAM 写入业务
第三阶段:收紧 package 边界 ✅
Section titled “第三阶段:收紧 package 边界 ✅”- ✅
apps/web/.oxlintrc.json通过no-restricted-imports禁止前端引用@IAM/auth、@IAM/db、@IAM/env/server及其子路径 - ✅
@IAM/api在apps/web中只允许import type,防止 tRPC 类型推断之外的运行时引用 - ✅
OAuth客户端可见性、角色归一化等纯逻辑已从@IAM/auth迁入@IAM/contracts,原位置仅做向后兼容重导出 - ✅
completeProfileInputSchema、createOAuthClientInputSchema、updateOAuthClientInputSchema等前后端共用 Zod schema 统一收入@IAM/contracts,由packages/api与前端表单同时复用
第四阶段:文档与部署收敛 ✅
Section titled “第四阶段:文档与部署收敛 ✅”- ✅
AGENTS.md项目结构补充@IAM/contracts与 server-only 标记,并新增”包边界与依赖方向”小节 - ✅ 本文件更新各阶段完成状态
- 本地开发端口:
apps/web:3000,apps/server:8787;生产 upstream 由AUTH_UPSTREAM_URL(见turbo.jsonenv 列表)注入
当前依赖方向
Section titled “当前依赖方向”apps/web ──► @IAM/contracts ◄── @IAM/api ──► @IAM/auth ──► @IAM/db ──► @IAM/env ▲ └──────────── apps/server ────────────────────────────────►apps/web 在运行时只触达 @IAM/contracts;@IAM/api 仅以 import type { AppRouter } 形式参与编译期类型推断,运行时被 Tree-shaking 与 lint 双重保证不进入前端 bundle。
- IAM 权威状态只能由
apps/server管理。 - Next.js 可以全栈,但在本项目中不作为认证中心后端。
- packages 不是按技术随意拆分,而是按依赖方向拆分。
- 前端只依赖纯契约和客户端 SDK,不依赖 server-only 实现。
- 共享代码先问”能否放进
@IAM/contracts”,需要 DB/env/Better Auth 实例的统统放服务端。