对后端分层设计的思考
引言
为了追求代码的规范与整洁,这一块的内容我连着啃了好几天,翻了好几个项目的源码、与GPT和Gemini大战了三百回合,总算是理清了一些思路。在这里把一些思考写下来,和大家分享一下。
为什么要做分层架构?
一言以蔽之,也就是老生常谈的六个字:高内聚,低耦合。
我们总是喜欢偷懒。在系统设计中,大到各种LLM,小到一个逻辑门,我们总是习惯于把它看做一个黑盒子,只关心它的输入和输出,而不去管它的内部实现细节——这是一种非常自然的思维方式,也是一种高效而聪明的偷懒手段。
实际上,作为系统设计者的我们一直在干大黑箱套小黑箱的事——一个系统由多个子系统组成,每个子系统又由多个模块组成,每个模块又由多个函数组成,而每个函数又由多行代码组成——而我们也一直在从黑箱思维带来的便利中获益。
所以,我们总是把系统拆分成一个个黑盒子,让每个模块只关心自己的职责,而不去管其他模块的实现细节。A模块只需知道B模块的输入输出接口就可以调用它,而不需要了解B模块的任何技术细节——这也是接口(Interface)的用武之地。
就这样,每个模块专注于自己的职责,这样就实现了高内聚;而模块之间只通过接口进行通信,避免了直接依赖,实现了低耦合。
这种设计带来的好处是显而易见的:
- 职责单一:每个模块只做一件事,代码更容易维护,可读性也更强。
- 可测试性:业务逻辑(核心)独立出来,可以单独进行单元测试。
- 可替换性:如果在迭代时需要更换某个模块的技术细节,对其他模块也不会产生影响。
经典分层架构分析
我分析了三种后端框架的分层架构设计,分别是Spring Boot(Java)、Django REST Framework (Python) 和 Kratos (Golang),并对它们的分层结构和职责进行了对比分析。
以下是一些AI帮我总结出来的内容:
1. Spring Boot(Java)的四层结构
这是最传统的后端分层模式,结构清晰,易于理解。
- Controller(控制层/接口层)
- 职责: 接收 HTTP 请求、参数校验、调用 Service 层、格式化 HTTP 响应。
- 特点: 不包含任何业务逻辑。
- Service(业务逻辑层)
- 职责: 实现核心业务逻辑、处理事务、组织和协调 Repository 层的数据。
- 特点: 业务代码的主体。
- Repository(数据访问层/持久层)
- 职责: 直接与数据库交互(CRUD操作)、封装底层数据访问细节。
- 特点: 数据库访问的唯一入口。
- Model/Entity(模型层)
- 职责: 定义数据结构,通常与数据库表结构一一对应。
2. Django REST Framework (Python) 的分层与角色
DRF 基于 Django 的 MVT(Model-View-Template)模式,但引入了 Serializer 角色。
- Models(模型/数据层)
- 职责: 使用 Django ORM 定义数据库结构和关系、提供基础的数据查询方法。
- 对应传统层: Model/Repository。
- Serializers(序列化器)
- 职责: DRF 最核心的特性。实现数据校验(从外部输入到内部模型)、序列化(从内部模型到外部响应,如 JSON)。
- 对应传统层: 部分参数校验(控制层)、数据格式转换。
- Views/ViewSet(视图/控制层)
- 职责: 接收 HTTP 请求、调用 Serializers 进行校验和序列化、协调 Models 层进行数据存取。
- 特点: 业务逻辑通常分散在 Views 或 Serializers 中。
- 对应传统层: Controller。
3. Kratos (Golang) 的 Clean Architecture 实践
其实这种分层架构并不是 Kratos 独有的——在 Golang 生态中,类似的 Clean Architecture 设计也被广泛采用。Kratos 只是将这种设计模式很好地集成到了它的框架中。
- Protobuf/API(契约层)
- 职责: 使用 Protobuf 定义接口(gRPC 或 HTTP)和所有数据传输结构(DTO)。
- 特点: 约定了系统内外数据交互的契约。
- Server(接口适配层/控制层)
- 职责: 接收 gRPC/HTTP 请求、将请求参数转换为 Service 层的结构 、调用 Service 层、将 Service 返回值转换为 Protobuf 响应。
- 对应传统层: Controller。
- Service(应用服务层)
- 职责: 负责跨 Biz 层的业务编排、事务控制,以及参数的转换。
- 特点: 连接 Server 和 Biz,作为应用入口。
- Biz(业务/领域层)
- 职责: 核心! 实现纯粹的领域业务逻辑、不依赖任何基础设施(数据库、缓存等)。
- 特点: 业务逻辑的独立核心,通过接口(Interface)与 Data 层解耦。
- 对应传统层: Service (核心部分)。
- Data(基础设施层)
- 职责: 实现 Biz 层定义的接口,负责连接和操作数据源,如数据库、缓存、第三方服务(如CAS登录、OAuth、LLM的API等)。
- 特点: 封装所有外部 I/O 细节。
- 对应传统层: Repository。
4. 跨框架分层对比与功能对应(核心对比表)
| 功能角色 | Spring Boot | DRF | Kratos | 职责核心 |
|---|---|---|---|---|
| 接口接入/控制 | Controller | Views/ViewSet | Server (gRPC/HTTP) | 接收请求,调用业务层,返回响应 |
| 业务逻辑处理 | Service | (分散在 Views/Serializers) | Biz | 负责核心业务规则和流程 |
| 数据校验/转换 | Controller/DTO | Serializers | Protobuf/DTO 转换 | 校验输入,格式化输出 |
| 数据持久化 | Repository | Models (ORM) | Data | 负责与数据库、缓存等外部 I/O 交互 |
| 数据结构定义 | Model/Entity | Models | Protobuf (Schema) | 定义数据结构和契约 |
5. 总结:分层的选择与架构的演进
- 分层架构的本质是隔离变化:将变化频繁的 I/O 层和稳定不变的 Biz 层隔离开。
- DRF 适用于: 快速开发、轻量级业务,Python生态友好。
- Spring Boot 适用于: 企业级应用、功能稳定、团队习惯 Java 规范。
- Kratos 适用于: 大型微服务、强调领域模型和解耦、追求高可维护性和测试性。
针对 Kratos 分层架构的详解
作为我正在使用的框架,我想再针对 Kratos 的四层架构详细说明一下每一层的职责和功能。
Server | 服务器层
主要负责处理网络请求和响应,管理协议的解析和封装。这一层相对独立,通常不包含任何业务逻辑。
Server 层不关心具体的业务逻辑(Biz, 下层),只负责请求的接收与分发
主要功能
- gRPC、HTTP 等服务器的初始化与配置
- 请求的路由和分发
- 中间件的集成(如认证、限流等)
- 错误处理和响应格式化
依赖关系
服务器层通常依赖服务 (Service) 层提供的业务逻辑接口。
Service | 服务层
类似于控制器 (Controller) 层,负责处理来自客户端的请求,调用业务逻辑层 (Biz) 的接口来执行业务操作,并将结果返回给客户端。
这一层一般直接透传来自下层 Biz 层的错误,不进行包装处理,但可以在这一层进行 ERROR / WARN 级日志记录等。
Service 层并不关心业务逻辑(Biz, 下层)的具体实现,也不关心请求的具体协议(Server, 上层),只负责根据请求调用相应的业务逻辑
主要功能
- 解析请求参数
- 调用业务逻辑层接口
- 组装响应结果
依赖关系
服务层依赖于业务逻辑 (Biz) 层提供的业务接口。
Biz | 业务逻辑层
主要负责实现具体的业务逻辑,包括数据处理、规则校验、流程控制等。
错误包装、INFO / WARN 级日志记录等一般在这一层出口处进行。
Biz 层不关心数据的具体存取方式(Data, 下层),也不关心业务逻辑为何被调用(Service, 上层),只负责实现具体的业务逻辑
主要功能
- 处理业务逻辑
- 调用数据层接口
- 组装响应结果
依赖关系
业务逻辑层通常依赖数据访问层 (Data) 提供的数据接口。
Data | 数据层
主要负责与数据库、缓存或外部服务(如CAS服务器、OAuth、LLM的API) 等数据源进行交互,提供数据的存取和管理功能。
这一层返回的错误通常是底层数据源的错误(原生Go error),需要在 Biz 层进行包装和处理,以归类错误类型,同时也负责 DEBUG 级日志记录等。
Data 层不关心业务逻辑(Biz, 上层)的具体实现,只负责针对具体来源的数据存取和管理
主要功能
- 数据库模型 / 外部数据模型定义
- 数据库 / 外部数据操作封装
- 数据库连接管理
- 数据缓存处理
依赖关系
数据层通常依赖于数据库驱动、ORM框架等。若涉及外部服务调用,还可能依赖于HTTP、gRPC等客户端。
结语
通过对不同后端框架分层架构的分析,我们可以看到,尽管具体实现有所不同,但核心思想是一致的:通过分层实现高内聚、低耦合,从而提升系统的可维护性、可测试性和可扩展性。