对后端分层设计的思考

- 2 mins read

引言

为了追求代码的规范与整洁,这一块的内容我连着啃了好几天,翻了好几个项目的源码、与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等客户端。

结语

通过对不同后端框架分层架构的分析,我们可以看到,尽管具体实现有所不同,但核心思想是一致的:通过分层实现高内聚、低耦合,从而提升系统的可维护性、可测试性和可扩展性。

参考资料