企业级分层架构
构建可扩展的企业级应用架构,实现代码分层与模块化设计。
📚 学习目标
学完这篇文章后,你将能够:
- 理解三层架构的设计理念
- 掌握本项目的架构实现
- 学会分离关注点(Separation of Concerns)
- 了解各层的职责和最佳实践
- 掌握代码组织原则
前置知识
在开始学习之前,建议先阅读:
你需要了解:
- 模块化编程概念
- SOLID 原则(可选)
1️⃣ 三层架构概述
1.1 什么是三层架构?
三层架构是一种软件设计模式,将应用程序分为三个逻辑层:
1.2 各层职责
| 层级 | 目录 | 职责 | 示例 |
|---|---|---|---|
| 表现层 | app/api/ | 处理 HTTP 请求/响应 | 验证请求格式、返回响应 |
| 业务逻辑层 | app/services/ | 业务逻辑、数据验证 | 会话创建、聊天处理 |
| 数据访问层 | app/database/ | CRUD 操作 | 插入、更新、删除 |
1.3 架构优势
✅ 优势:
- 📦 职责清晰,易于维护
- 🧪 每层可独立测试
- 🔄 代码复用性高
- 🚀 团队协作效率高
❌ 劣势:
- 📝 初期代码量较多
- ⏱️ 简单场景可能过度设计
2️⃣ 本项目的三层架构
2.1 架构图
2.2 目录结构
app/
├── api/ # 表现层(API Routes)
│ ├── chat/
│ │ ├── route.ts # 聊天 API
│ │ └── sessions/
│ │ └── route.ts # 会话管理 API
│ └── artifacts/
│ └── [id]/
│ └── route.ts # 工件 API
│
├── services/ # 业务逻辑层
│ ├── chat.service.ts # 聊天服务
│ ├── session.service.ts # 会话服务
│ ├── artifact.service.ts # 工件服务
│ └── README.md # 服务层文档 ([app/services/README.md](https://github.com/loock-ai/langgraphjs-chat-app/blob/main/app/services/README.md))
│
└── database/ # 数据访问层
├── sessions.ts # 会话数据操作
├── artifacts.ts # 工件数据操作
└── messages.ts # 消息数据操作3️⃣ 表现层(Routes)
3.1 职责
表现层负责:
- ✅ 接收 HTTP 请求
- ✅ 验证请求格式
- ✅ 调用服务层
- ✅ 返回 HTTP 响应
- ✅ 处理错误和异常
3.2 示例:会话管理 API
查看实现:app/api/chat/sessions/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { sessionService } from '@/app/services';
import { withAuth } from '@/app/middleware/auth';
import type { SessionType } from '@/app/database/sessions';
/**
* GET /api/chat/sessions
* 获取当前用户的所有会话列表
* 支持按 type 过滤: ?type=chat|deepresearch
*/
export const GET = withAuth(async (request: NextRequest, auth) => {
try {
const { searchParams } = new URL(request.url);
const type = searchParams.get('type') as SessionType | undefined;
// 调用服务层
const sessions = await sessionService.getAllSessions(auth.client, type);
return NextResponse.json({ sessions });
} catch (e) {
return NextResponse.json(
{ error: '获取会话列表失败', detail: String(e) },
{ status: 500 },
);
}
});
/**
* POST /api/chat/sessions
* 创建新会话
*/
export const POST = withAuth(async (request: NextRequest, auth) => {
try {
const { name, type = 'chat' } = await request.json();
// 调用服务层
const result = await sessionService.createSession(
{ name, type },
auth.user.id,
auth.client,
);
return NextResponse.json(result);
} catch (e) {
return NextResponse.json(
{ error: '新建会话失败', detail: String(e) },
{ status: 500 },
);
}
});
/**
* DELETE /api/chat/sessions
* 删除会话
*/
export const DELETE = withAuth(async (request: NextRequest, auth) => {
try {
const { id } = await request.json();
if (!id) {
return NextResponse.json({ error: '缺少 id' }, { status: 400 });
}
// 调用服务层
await sessionService.deleteSession({ id });
return NextResponse.json({ success: true });
} catch (e) {
return NextResponse.json(
{ error: '删除会话失败', detail: String(e) },
{ status: 500 },
);
}
});
/**
* PATCH /api/chat/sessions
* 重命名会话
*/
export const PATCH = withAuth(async (request: NextRequest, auth) => {
try {
const { id, name } = await request.json();
if (!id || !name) {
return NextResponse.json({ error: '缺少参数' }, { status: 400 });
}
// 调用服务层
await sessionService.updateSessionName({ id, name });
return NextResponse.json({ success: true });
} catch (e) {
return NextResponse.json(
{ error: '更新会话失败', detail: String(e) },
{ status: 500 },
);
}
});代码分析:
- ✅ 只处理 HTTP 相关逻辑
- ✅ 参数验证(如 id、name)
- ✅ 错误捕获和统一响应
- ✅ 调用服务层,不直接操作数据库
4️⃣ 业务逻辑层(Services)
4.1 职责
业务逻辑层负责:
- ✅ 业务规则和逻辑
- ✅ 数据验证
- ✅ 调用数据访问层
- ✅ 事务管理
- ✅ 缓存处理
4.2 示例:会话服务
查看实现:app/services/session.service.ts
import * as sessionDb from '@/app/database/sessions';
import type { Session, SessionType } from '@/app/database/sessions';
/**
* 会话服务
* 负责会话的业务逻辑和验证
*/
export class SessionService {
/**
* 获取用户的所有会话
*/
async getAllSessions(client: any, type?: SessionType): Promise<Session[]> {
// 业务逻辑:过滤会话类型
return await sessionDb.findSessions(client, type);
}
/**
* 创建新会话
*/
async createSession(
data: { name?: string; type: SessionType },
userId: string,
client: any,
): Promise<Session> {
// 业务逻辑:设置默认值
const name = data.name || (data.type === 'chat' ? '新对话' : '新研究');
// 业务逻辑:验证
if (!userId) {
throw new Error('用户 ID 不能为空');
}
// 调用数据层
return await sessionDb.createSession(
{
name,
type: data.type,
user_id: userId,
},
client,
);
}
/**
* 删除会话
*/
async deleteSession({ id }: { id: string }): Promise<void> {
// 业务逻辑:验证 ID
if (!id) {
throw new Error('会话 ID 不能为空');
}
// 调用数据层
await sessionDb.deleteSession(id);
}
/**
* 更新会话名称
*/
async updateSessionName({
id,
name,
}: {
id: string;
name: string;
}): Promise<void> {
// 业务逻辑:验证参数
if (!id || !name) {
throw new Error('ID 和名称不能为空');
}
// 业务逻辑:名称长度验证
if (name.length > 100) {
throw new Error('名称不能超过 100 个字符');
}
// 调用数据层
await sessionDb.updateSession({ id, name });
}
}
// 导出单例
export const sessionService = new SessionService();代码分析:
- ✅ 业务规则(默认名称、长度验证)
- ✅ 参数验证(userId、name)
- ✅ 调用数据层
- ✅ 导出单例模式
5️⃣ 数据访问层(Database)
5.1 职责
数据访问层负责:
- ✅ CRUD 操作
- ✅ 数据库查询
- ✅ 数据格式转换
- ❌ 不包含业务逻辑
- ❌ 不包含验证逻辑
5.2 示例:会话数据操作
import type { Session, SessionType } from '@/app/database/sessions';
/**
* 查询所有会话
*/
export async function findSessions(
client: any,
type?: SessionType,
): Promise<Session[]> {
let query = client
.from('sessions')
.select('*')
.order('created_at', { ascending: false });
if (type) {
query = query.eq('type', type);
}
const { data, error } = await query;
if (error) throw error;
return data || [];
}
/**
* 创建会话
*/
export async function createSession(
data: {
name: string;
type: SessionType;
user_id: string;
},
client: any,
): Promise<Session> {
const { data: session, error } = await client
.from('sessions')
.insert({
name: data.name,
type: data.type,
user_id: data.user_id,
})
.select()
.single();
if (error) throw error;
return session;
}
/**
* 删除会话
*/
export async function deleteSession(id: string, client: any): Promise<void> {
const { error } = await client.from('sessions').delete().eq('id', id);
if (error) throw error;
}
/**
* 更新会话
*/
export async function updateSession(
{ id, name }: { id: string; name: string },
client: any,
): Promise<void> {
const { error } = await client.from('sessions').update({ name }).eq('id', id);
if (error) throw error;
}代码分析:
- ✅ 纯 CRUD 操作
- ✅ 使用 Supabase Client
- ✅ 返回数据库格式
- ❌ 无业务逻辑
- ❌ 无参数验证
6️⃣ 架构原则与最佳实践
6.1 硬性规则
根据项目文档 AGENTS.md:
Routes (app/api/**/route.ts)
↓ calls
Services (app/services/*.service.ts)
↓ calls
Database (app/database/*.ts)绝对禁止:
- ❌ Route 直接调用 Database(跳过 Service 层)
- ❌ Service 层没有业务逻辑(直接透传)
- ❌ Database 层包含业务逻辑
示例 - 错误:
// ❌ 错误:Route 直接调用 Database
export const POST = withAuth(async (request, auth) => {
const { name } = await request.json();
// 直接操作数据库
const { data } = await auth.client
.from('sessions')
.insert({ name, user_id: auth.user.id });
return NextResponse.json({ session: data });
});示例 - 正确:
// ✅ 正确:Route → Service → Database
export const POST = withAuth(async (request, auth) => {
const { name } = await request.json();
// 调用服务层
const session = await sessionService.createSession(
{ name },
auth.user.id,
auth.client,
);
return NextResponse.json({ session });
});6.2 职责分离
| 层级 | 职责 | 示例 |
|---|---|---|
| Routes | HTTP 处理 | 解析请求、返回响应 |
| Services | 业务逻辑 | 验证、规则、事务 |
| Database | 数据操作 | CRUD 查询 |
6.3 代码组织
命名规范:
// Services: *.service.ts
chat.service.ts;
session.service.ts;
artifact.service.ts;
// Database: *.ts(无后缀)
sessions.ts;
artifacts.ts;
messages.ts;
// Routes: route.ts
app / api / chat / route.ts;
app / api / sessions / route.ts;导出模式:
// Services: 单例模式
export const sessionService = new SessionService();
// Database: 导出函数
export async function createSession(data, client) { ... }
export async function findSessions(client) { ... }7️⃣ 实战案例:完整的 CRUD 流程
7.1 需求
创建一个任务管理功能:
- GET
/api/tasks- 获取任务列表 - POST
/api/tasks- 创建任务 - PUT
/api/tasks/[id]- 更新任务 - DELETE
/api/tasks/[id]- 删除任务
7.2 实现
Routes 层:
// app/api/tasks/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { withAuth } from '@/app/middleware/auth';
import { taskService } from '@/app/services';
export const GET = withAuth(async (request, auth) => {
try {
const tasks = await taskService.getAllTasks(auth.client);
return NextResponse.json({ tasks });
} catch (e) {
return NextResponse.json({ error: '获取任务失败' }, { status: 500 });
}
});
export const POST = withAuth(async (request, auth) => {
try {
const { title, description } = await request.json();
if (!title) {
return NextResponse.json({ error: '标题不能为空' }, { status: 400 });
}
const task = await taskService.createTask(
{ title, description },
auth.user.id,
auth.client,
);
return NextResponse.json({ task });
} catch (e) {
return NextResponse.json({ error: '创建任务失败' }, { status: 500 });
}
});Services 层:
// app/services/task.service.ts
import * as taskDb from '@/app/database/tasks';
export class TaskService {
async getAllTasks(client: any) {
return await taskDb.findTasks(client);
}
async createTask(
data: { title: string; description?: string },
userId: string,
client: any,
) {
if (!userId) {
throw new Error('用户 ID 不能为空');
}
return await taskDb.createTask(
{
title: data.title,
description: data.description,
user_id: userId,
},
client,
);
}
async updateTask(id: string, data: Partial<Task>, client: any) {
if (!id) {
throw new Error('任务 ID 不能为空');
}
return await taskDb.updateTask(id, data, client);
}
async deleteTask(id: string, client: any) {
if (!id) {
throw new Error('任务 ID 不能为空');
}
await taskDb.deleteTask(id, client);
}
}
export const taskService = new TaskService();Database 层:
// app/database/tasks.ts
export async function findTasks(client: any) {
const { data, error } = await client
.from('tasks')
.select('*')
.order('created_at', { ascending: false });
if (error) throw error;
return data || [];
}
export async function createTask(data: any, client: any) {
const { data: task, error } = await client
.from('tasks')
.insert(data)
.select()
.single();
if (error) throw error;
return task;
}
export async function updateTask(id: string, data: any, client: any) {
const { error } = await client.from('tasks').update(data).eq('id', id);
if (error) throw error;
}
export async function deleteTask(id: string, client: any) {
const { error } = await client.from('tasks').delete().eq('id', id);
if (error) throw error;
}💡 练习题
-
选择题:三层架构中,业务逻辑层应该在哪个目录?
- A. app/api/
- B. app/services/
- C. app/database/
- D. app/components/
-
分析题:查看本项目的 app/api/chat/sessions/route.ts,说明它如何遵循三层架构。
-
代码题:指出以下代码的问题并修正:
export const POST = withAuth(async (request, auth) => { const { name } = await request.json(); const { data } = await auth.client.from('sessions').insert({ name }); return NextResponse.json({ session: data }); }); -
实践题:实现一个完整的博客系统(文章、评论、标签),遵循三层架构。
📚 参考资源
项目文档
相关文件
✅ 总结
三层架构:
- Routes(表现层)- HTTP 处理
- Services(业务逻辑层)- 业务规则
- Database(数据访问层)- CRUD 操作
硬性规则:
- ✅ Route → Service → Database
- ❌ Route 直接调用 Database
- ❌ Service 没有业务逻辑
- ❌ Database 包含业务逻辑
职责分离:
- Routes: HTTP 请求/响应
- Services: 验证、规则、事务
- Database: CRUD 查询
最佳实践:
- 单例模式导出 Services
- 函数导出 Database 操作
- 统一的错误处理
- 清晰的命名规范
下一步:阅读最后一篇文章《完整项目功能复盘》,总结整个项目的技术要点。
登录以继续阅读
解锁完整文档、代码示例及更多高级功能。