Skip to content

架构边界改造大纲

本改造的目标不是减少项目数量,而是让每个入口的职责稳定下来。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 是唯一后端入口,负责:

  • 挂载 Better Auth:/api/auth/*
  • 暴露 OAuth/OIDC Provider 相关端点,包括 well-known metadata
  • 挂载 tRPC:/trpc/*
  • 处理文件上传、企业微信回调、同步任务、内部运维接口
  • 直接使用 @IAM/auth@IAM/db@IAM/env/server

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 只提供服务端认证能力,负责:

  • Better Auth 实例配置
  • Better Auth plugins、hooks、provider
  • 企业微信 OAuth provider 与同步能力
  • 调用 Drizzle adapter 与数据库 hooks

它可以依赖 @IAM/db@IAM/env@IAM/redis,但不应依赖 apps/web

packages/api 是业务 API 层,负责:

  • tRPC router 定义
  • 标准 protectedProcedure 权限校验
  • 无法直接用 Better Auth 原生客户端 API 满足的业务聚合
  • IAM 管理后台需要的统计、搜索、OAuth 客户端扩展配置

优先使用 Better Auth 已有 API;只有跨表统计、深度筛选、OAuth 管理控制台扩展字段等场景才写自定义 tRPC/Drizzle 逻辑。

packages/contracts 只放纯共享契约,负责:

  • 前后端共用类型
  • 枚举、常量、Zod 输入 schema
  • 不访问数据库、不读取环境变量、不初始化 Better Auth

第一阶段:切断前端对服务端包的直接依赖 ✅

Section titled “第一阶段:切断前端对服务端包的直接依赖 ✅”
  • ✅ 新增 @IAM/contracts
  • ✅ 将 apps/web 中仅为类型用途的 @IAM/auth import 改为 @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/server Hono REST endpoint
  • apps/web/app/api/[...path]/route.tsapp/trpc/[...path]/route.ts 仅作代理,不承载 IAM 写入业务
  • apps/web/.oxlintrc.json 通过 no-restricted-imports 禁止前端引用 @IAM/auth@IAM/db@IAM/env/server 及其子路径
  • @IAM/apiapps/web 中只允许 import type,防止 tRPC 类型推断之外的运行时引用
  • OAuth 客户端可见性、角色归一化等纯逻辑已从 @IAM/auth 迁入 @IAM/contracts,原位置仅做向后兼容重导出
  • completeProfileInputSchemacreateOAuthClientInputSchemaupdateOAuthClientInputSchema 等前后端共用 Zod schema 统一收入 @IAM/contracts,由 packages/api 与前端表单同时复用
  • AGENTS.md 项目结构补充 @IAM/contracts 与 server-only 标记,并新增”包边界与依赖方向”小节
  • ✅ 本文件更新各阶段完成状态
  • 本地开发端口:apps/web :3000apps/server :8787;生产 upstream 由 AUTH_UPSTREAM_URL(见 turbo.json env 列表)注入
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 实例的统统放服务端。