357 lines
14 KiB
Markdown
357 lines
14 KiB
Markdown
|
|
# 分层架构设计
|
|||
|
|
|
|||
|
|
<cite>
|
|||
|
|
**本文档引用的文件**
|
|||
|
|
- [main.go](file://cmd/server/main.go)
|
|||
|
|
- [router.go](file://internal/router/router.go)
|
|||
|
|
- [config.go](file://internal/config/config.go)
|
|||
|
|
- [database.go](file://internal/config/database.go)
|
|||
|
|
- [meilisearch_config.go](file://internal/config/meilisearch.go)
|
|||
|
|
- [redis_config.go](file://internal/config/redis.go)
|
|||
|
|
- [mysql.go](file://internal/database/mysql.go)
|
|||
|
|
- [redis.go](file://internal/cache/redis.go)
|
|||
|
|
- [meilisearch.go](file://internal/search/meilisearch.go)
|
|||
|
|
- [health.go](file://internal/handler/health.go)
|
|||
|
|
- [brand.go](file://internal/handler/brand.go)
|
|||
|
|
- [model.go](file://internal/handler/model.go)
|
|||
|
|
- [brand_repository.go](file://internal/repository/brand.go)
|
|||
|
|
- [brand_model.go](file://internal/model/brand.go)
|
|||
|
|
- [logger_middleware.go](file://internal/middleware/logger.go)
|
|||
|
|
</cite>
|
|||
|
|
|
|||
|
|
## 目录
|
|||
|
|
1. [引言](#引言)
|
|||
|
|
2. [项目结构](#项目结构)
|
|||
|
|
3. [核心组件](#核心组件)
|
|||
|
|
4. [架构总览](#架构总览)
|
|||
|
|
5. [详细组件分析](#详细组件分析)
|
|||
|
|
6. [依赖分析](#依赖分析)
|
|||
|
|
7. [性能考虑](#性能考虑)
|
|||
|
|
8. [故障排除指南](#故障排除指南)
|
|||
|
|
9. [结论](#结论)
|
|||
|
|
|
|||
|
|
## 引言
|
|||
|
|
本项目采用分层架构设计,围绕表现层(Router/Handler)、业务层(Repository)、数据访问层(Database/Cache/Search)与基础设施层(Middleware/Logger)进行职责分离。通过构造函数注入依赖的方式,实现清晰的依赖关系与可测试性。MVC 的变体实现体现在:表现层负责路由与请求响应;业务层封装领域逻辑与查询;数据访问层抽象数据库、缓存与搜索引擎;基础设施层提供横切关注点如日志、CORS、请求 ID 等。
|
|||
|
|
|
|||
|
|
## 项目结构
|
|||
|
|
项目按功能域分层组织,核心目录如下:
|
|||
|
|
- cmd/server:应用入口,负责初始化配置、连接外部服务、启动 HTTP 服务器
|
|||
|
|
- internal/config:配置加载与校验,支持环境变量覆盖
|
|||
|
|
- internal/router:路由注册与中间件装配
|
|||
|
|
- internal/handler:HTTP 处理器,面向路由端点,调用仓库与工具
|
|||
|
|
- internal/repository:业务仓储,封装数据库查询与映射
|
|||
|
|
- internal/database:数据库连接与池化配置
|
|||
|
|
- internal/cache:Redis 客户端初始化
|
|||
|
|
- internal/search:Meilisearch 客户端初始化与搜索接口
|
|||
|
|
- internal/middleware:Gin 中间件,统一处理日志、CORS、请求 ID
|
|||
|
|
- internal/model:领域模型定义
|
|||
|
|
- internal/response:统一响应封装
|
|||
|
|
- pkg/encode:编码工具(如 Base64)
|
|||
|
|
- pkg/logger:日志初始化
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
graph TB
|
|||
|
|
subgraph "表现层"
|
|||
|
|
Router["Router<br/>internal/router/router.go"]
|
|||
|
|
Handlers["Handlers<br/>internal/handler/*"]
|
|||
|
|
end
|
|||
|
|
subgraph "业务层"
|
|||
|
|
Repositories["Repositories<br/>internal/repository/*"]
|
|||
|
|
Models["Models<br/>internal/model/*"]
|
|||
|
|
end
|
|||
|
|
subgraph "数据访问层"
|
|||
|
|
DB["MySQL 连接<br/>internal/database/mysql.go"]
|
|||
|
|
Redis["Redis 客户端<br/>internal/cache/redis.go"]
|
|||
|
|
Search["Meilisearch 客户端<br/>internal/search/meilisearch.go"]
|
|||
|
|
end
|
|||
|
|
subgraph "基础设施层"
|
|||
|
|
Middleware["中间件<br/>internal/middleware/*"]
|
|||
|
|
LoggerPkg["日志包<br/>pkg/logger/*"]
|
|||
|
|
Encode["编码工具<br/>pkg/encode/*"]
|
|||
|
|
end
|
|||
|
|
subgraph "入口"
|
|||
|
|
Main["主程序<br/>cmd/server/main.go"]
|
|||
|
|
Config["配置<br/>internal/config/*"]
|
|||
|
|
end
|
|||
|
|
Main --> Config
|
|||
|
|
Main --> DB
|
|||
|
|
Main --> Redis
|
|||
|
|
Main --> Search
|
|||
|
|
Main --> Router
|
|||
|
|
Router --> Middleware
|
|||
|
|
Router --> Handlers
|
|||
|
|
Handlers --> Repositories
|
|||
|
|
Repositories --> DB
|
|||
|
|
Handlers --> Redis
|
|||
|
|
Handlers --> Search
|
|||
|
|
Handlers --> Encode
|
|||
|
|
Handlers --> LoggerPkg
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**图示来源**
|
|||
|
|
- [main.go:1-96](file://cmd/server/main.go#L1-L96)
|
|||
|
|
- [router.go:1-42](file://internal/router/router.go#L1-L42)
|
|||
|
|
- [mysql.go:1-47](file://internal/database/mysql.go#L1-L47)
|
|||
|
|
- [redis.go:1-17](file://internal/cache/redis.go#L1-L17)
|
|||
|
|
- [meilisearch.go:1-46](file://internal/search/meilisearch.go#L1-L46)
|
|||
|
|
- [config.go:1-64](file://internal/config/config.go#L1-L64)
|
|||
|
|
|
|||
|
|
**章节来源**
|
|||
|
|
- [main.go:1-96](file://cmd/server/main.go#L1-L96)
|
|||
|
|
- [router.go:1-42](file://internal/router/router.go#L1-L42)
|
|||
|
|
- [config.go:1-64](file://internal/config/config.go#L1-L64)
|
|||
|
|
|
|||
|
|
## 核心组件
|
|||
|
|
- 表现层(Router/Handler)
|
|||
|
|
- 路由器负责注册路由组与中间件,将 HTTP 请求映射到处理器
|
|||
|
|
- 处理器接收请求参数,调用仓库或外部服务,返回统一响应
|
|||
|
|
- 业务层(Repository)
|
|||
|
|
- 封装数据库查询逻辑,负责数据读取与映射
|
|||
|
|
- 数据访问层(Database/Cache/Search)
|
|||
|
|
- 提供数据库连接池、Redis 客户端与 Meilisearch 客户端
|
|||
|
|
- 基础设施层(Middleware/Logger)
|
|||
|
|
- 提供日志、CORS、请求 ID 等横切能力
|
|||
|
|
|
|||
|
|
**章节来源**
|
|||
|
|
- [router.go:14-41](file://internal/router/router.go#L14-L41)
|
|||
|
|
- [brand.go:19-24](file://internal/handler/brand.go#L19-L24)
|
|||
|
|
- [brand_repository.go:16-18](file://internal/repository/brand.go#L16-L18)
|
|||
|
|
- [mysql.go:14-46](file://internal/database/mysql.go#L14-L46)
|
|||
|
|
- [redis.go:10-16](file://internal/cache/redis.go#L10-L16)
|
|||
|
|
- [meilisearch.go:17-20](file://internal/search/meilisearch.go#L17-L20)
|
|||
|
|
- [logger_middleware.go:10-45](file://internal/middleware/logger.go#L10-L45)
|
|||
|
|
|
|||
|
|
## 架构总览
|
|||
|
|
下图展示了从入口到各层的完整调用链路与依赖方向:
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
sequenceDiagram
|
|||
|
|
participant Client as "客户端"
|
|||
|
|
participant Main as "主程序<br/>cmd/server/main.go"
|
|||
|
|
participant Router as "路由器<br/>internal/router/router.go"
|
|||
|
|
participant Handler as "处理器<br/>internal/handler/*"
|
|||
|
|
participant Repo as "仓库<br/>internal/repository/*"
|
|||
|
|
participant DB as "数据库<br/>internal/database/mysql.go"
|
|||
|
|
participant Redis as "Redis<br/>internal/cache/redis.go"
|
|||
|
|
participant Search as "Meilisearch<br/>internal/search/meilisearch.go"
|
|||
|
|
Client->>Main : 启动进程
|
|||
|
|
Main->>Main : 加载配置/初始化日志
|
|||
|
|
Main->>DB : 打开数据库连接
|
|||
|
|
Main->>Redis : 创建客户端
|
|||
|
|
Main->>Search : 创建客户端
|
|||
|
|
Main->>Router : 注册路由与中间件
|
|||
|
|
Client->>Router : 发起 HTTP 请求
|
|||
|
|
Router->>Handler : 调用对应处理器
|
|||
|
|
Handler->>Repo : 执行业务查询
|
|||
|
|
Repo->>DB : 执行 SQL 查询
|
|||
|
|
DB-->>Repo : 返回结果集
|
|||
|
|
Repo-->>Handler : 返回领域对象列表
|
|||
|
|
Handler-->>Client : 统一响应
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**图示来源**
|
|||
|
|
- [main.go:22-64](file://cmd/server/main.go#L22-L64)
|
|||
|
|
- [router.go:14-41](file://internal/router/router.go#L14-L41)
|
|||
|
|
- [brand.go:30-35](file://internal/handler/brand.go#L30-L35)
|
|||
|
|
- [brand_repository.go:31-50](file://internal/repository/brand.go#L31-L50)
|
|||
|
|
- [mysql.go:28-46](file://internal/database/mysql.go#L28-L46)
|
|||
|
|
|
|||
|
|
## 详细组件分析
|
|||
|
|
|
|||
|
|
### 表现层(Router/Handler)
|
|||
|
|
- 路由器在入口中被创建,并注入日志、数据库、搜索与 Redis 客户端
|
|||
|
|
- 注册健康检查、品牌、型号、设备等路由组与端点
|
|||
|
|
- 处理器通过构造函数注入数据库连接与日志实例,确保单一职责与可测试性
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
classDiagram
|
|||
|
|
class Router {
|
|||
|
|
+New(log, db, searchClient, redis) Engine
|
|||
|
|
}
|
|||
|
|
class HealthHandler {
|
|||
|
|
+Check(c)
|
|||
|
|
}
|
|||
|
|
class BrandHandler {
|
|||
|
|
-repo BrandRepository
|
|||
|
|
-log Logger
|
|||
|
|
+GetBrand(c)
|
|||
|
|
}
|
|||
|
|
class ModelHandler {
|
|||
|
|
-repo ModelRepository
|
|||
|
|
-log Logger
|
|||
|
|
+GetModel(c)
|
|||
|
|
}
|
|||
|
|
Router --> HealthHandler : "注册路由"
|
|||
|
|
Router --> BrandHandler : "注册路由"
|
|||
|
|
Router --> ModelHandler : "注册路由"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**图示来源**
|
|||
|
|
- [router.go:14-41](file://internal/router/router.go#L14-L41)
|
|||
|
|
- [health.go:8-18](file://internal/handler/health.go#L8-L18)
|
|||
|
|
- [brand.go:14-24](file://internal/handler/brand.go#L14-L24)
|
|||
|
|
- [model.go:14-24](file://internal/handler/model.go#L14-L24)
|
|||
|
|
|
|||
|
|
**章节来源**
|
|||
|
|
- [router.go:14-41](file://internal/router/router.go#L14-L41)
|
|||
|
|
- [health.go:8-18](file://internal/handler/health.go#L8-L18)
|
|||
|
|
- [brand.go:19-24](file://internal/handler/brand.go#L19-L24)
|
|||
|
|
- [model.go:19-24](file://internal/handler/model.go#L19-L24)
|
|||
|
|
|
|||
|
|
### 业务层(Repository)
|
|||
|
|
- 仓库封装数据库查询逻辑,使用上下文传递取消信号与超时控制
|
|||
|
|
- 支持条件查询与排序,将结果映射到领域模型
|
|||
|
|
- 通过构造函数注入数据库连接,保持与底层实现解耦
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
classDiagram
|
|||
|
|
class BrandRepository {
|
|||
|
|
-db sql.DB
|
|||
|
|
+NewBrandRepository(db)
|
|||
|
|
+List(ctx, brandName) []Brand
|
|||
|
|
}
|
|||
|
|
class BrandModel {
|
|||
|
|
+ID int
|
|||
|
|
+Name string
|
|||
|
|
}
|
|||
|
|
BrandRepository --> BrandModel : "返回领域对象"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**图示来源**
|
|||
|
|
- [brand_repository.go:12-50](file://internal/repository/brand.go#L12-L50)
|
|||
|
|
- [brand_model.go:3-6](file://internal/model/brand.go#L3-L6)
|
|||
|
|
|
|||
|
|
**章节来源**
|
|||
|
|
- [brand_repository.go:16-18](file://internal/repository/brand.go#L16-L18)
|
|||
|
|
- [brand_repository.go:20-50](file://internal/repository/brand.go#L20-L50)
|
|||
|
|
- [brand_model.go:3-6](file://internal/model/brand.go#L3-L6)
|
|||
|
|
|
|||
|
|
### 数据访问层(Database/Cache/Search)
|
|||
|
|
- 数据库连接通过 DSN 配置,设置连接池大小与生命周期,并进行健康检查
|
|||
|
|
- Redis 客户端基于配置创建,用于后续缓存操作
|
|||
|
|
- Meilisearch 客户端封装搜索请求,限制检索字段并解码结果
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
flowchart TD
|
|||
|
|
Start(["初始化数据访问层"]) --> DBInit["创建数据库连接<br/>internal/database/mysql.go"]
|
|||
|
|
DBInit --> DBPool["设置连接池参数"]
|
|||
|
|
DBPool --> DBPing["执行健康检查"]
|
|||
|
|
DBPing --> RedisInit["创建 Redis 客户端<br/>internal/cache/redis.go"]
|
|||
|
|
DBPing --> SearchInit["创建 Meilisearch 客户端<br/>internal/search/meilisearch.go"]
|
|||
|
|
RedisInit --> End(["完成"])
|
|||
|
|
SearchInit --> End
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**图示来源**
|
|||
|
|
- [mysql.go:14-46](file://internal/database/mysql.go#L14-L46)
|
|||
|
|
- [redis.go:10-16](file://internal/cache/redis.go#L10-L16)
|
|||
|
|
- [meilisearch.go:17-20](file://internal/search/meilisearch.go#L17-L20)
|
|||
|
|
|
|||
|
|
**章节来源**
|
|||
|
|
- [mysql.go:14-46](file://internal/database/mysql.go#L14-L46)
|
|||
|
|
- [redis.go:10-16](file://internal/cache/redis.go#L10-L16)
|
|||
|
|
- [meilisearch.go:17-20](file://internal/search/meilisearch.go#L17-L20)
|
|||
|
|
|
|||
|
|
### 基础设施层(Middleware/Logger)
|
|||
|
|
- 日志中间件记录状态码、路径、耗时、IP、请求 ID 等信息,并按级别输出
|
|||
|
|
- 请求 ID 中间件为每个请求生成唯一标识,便于追踪
|
|||
|
|
- CORS 中间件允许跨域访问
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
flowchart TD
|
|||
|
|
Req["请求进入"] --> ReqID["生成/获取请求ID"]
|
|||
|
|
ReqID --> LogMW["日志中间件记录"]
|
|||
|
|
LogMW --> CORS["CORS 中间件"]
|
|||
|
|
CORS --> Handler["处理器执行"]
|
|||
|
|
Handler --> Resp["响应返回"]
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**图示来源**
|
|||
|
|
- [logger_middleware.go:10-45](file://internal/middleware/logger.go#L10-L45)
|
|||
|
|
|
|||
|
|
**章节来源**
|
|||
|
|
- [logger_middleware.go:10-45](file://internal/middleware/logger.go#L10-L45)
|
|||
|
|
|
|||
|
|
### 配置管理(Config)
|
|||
|
|
- 配置加载支持环境变量覆盖,分别加载数据库、Meilisearch、Redis 的配置
|
|||
|
|
- 在生产环境与开发环境之间切换默认值
|
|||
|
|
- 提供地址格式化方法与环境变量读取辅助函数
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
flowchart TD
|
|||
|
|
Load["加载配置"] --> Env["解析环境变量"]
|
|||
|
|
Env --> DB["加载数据库配置"]
|
|||
|
|
Env --> MS["加载 Meilisearch 配置"]
|
|||
|
|
Env --> RD["加载 Redis 配置"]
|
|||
|
|
DB --> ValidateDB["校验数据库配置"]
|
|||
|
|
MS --> ValidateMS["校验 Meilisearch 配置"]
|
|||
|
|
RD --> ValidateRD["校验 Redis 配置"]
|
|||
|
|
ValidateDB --> Done["返回配置对象"]
|
|||
|
|
ValidateMS --> Done
|
|||
|
|
ValidateRD --> Done
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**图示来源**
|
|||
|
|
- [config.go:18-56](file://internal/config/config.go#L18-L56)
|
|||
|
|
- [database.go:17-71](file://internal/config/database.go#L17-L71)
|
|||
|
|
- [meilisearch_config.go:14-50](file://internal/config/meilisearch.go#L14-L50)
|
|||
|
|
- [redis_config.go:16-56](file://internal/config/redis.go#L16-L56)
|
|||
|
|
|
|||
|
|
**章节来源**
|
|||
|
|
- [config.go:18-56](file://internal/config/config.go#L18-L56)
|
|||
|
|
- [database.go:17-71](file://internal/config/database.go#L17-L71)
|
|||
|
|
- [meilisearch_config.go:14-50](file://internal/config/meilisearch.go#L14-L50)
|
|||
|
|
- [redis_config.go:16-56](file://internal/config/redis.go#L16-L56)
|
|||
|
|
|
|||
|
|
## 依赖分析
|
|||
|
|
- 入口依赖配置、数据库、缓存与搜索引擎,然后构建路由器
|
|||
|
|
- 路由器依赖中间件与处理器,处理器依赖仓库与日志
|
|||
|
|
- 仓库依赖数据库连接,处理器可选依赖缓存与搜索引擎
|
|||
|
|
- 配置模块独立于业务层,提供环境化参数
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
graph LR
|
|||
|
|
Main["cmd/server/main.go"] --> Config["internal/config/*"]
|
|||
|
|
Main --> DB["internal/database/mysql.go"]
|
|||
|
|
Main --> Redis["internal/cache/redis.go"]
|
|||
|
|
Main --> Search["internal/search/meilisearch.go"]
|
|||
|
|
Main --> Router["internal/router/router.go"]
|
|||
|
|
Router --> Middleware["internal/middleware/*"]
|
|||
|
|
Router --> Handlers["internal/handler/*"]
|
|||
|
|
Handlers --> Repositories["internal/repository/*"]
|
|||
|
|
Repositories --> DB
|
|||
|
|
Handlers --> Redis
|
|||
|
|
Handlers --> Search
|
|||
|
|
Handlers --> Logger["pkg/logger/*"]
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**图示来源**
|
|||
|
|
- [main.go:22-64](file://cmd/server/main.go#L22-L64)
|
|||
|
|
- [router.go:14-41](file://internal/router/router.go#L14-L41)
|
|||
|
|
|
|||
|
|
**章节来源**
|
|||
|
|
- [main.go:22-64](file://cmd/server/main.go#L22-L64)
|
|||
|
|
- [router.go:14-41](file://internal/router/router.go#L14-L41)
|
|||
|
|
|
|||
|
|
## 性能考虑
|
|||
|
|
- 数据库连接池:设置最大打开连接数、空闲连接数与连接生命周期,减少连接开销
|
|||
|
|
- 上下文超时:仓库查询使用上下文传递超时,避免阻塞
|
|||
|
|
- 编码优化:处理器支持 Base64 编码响应,降低传输体积但增加 CPU 开销,需按场景权衡
|
|||
|
|
- 中间件顺序:日志中间件应置于末尾以统计真实耗时
|
|||
|
|
|
|||
|
|
[本节为通用指导,无需特定文件来源]
|
|||
|
|
|
|||
|
|
## 故障排除指南
|
|||
|
|
- 数据库连接失败:检查配置中的主机、端口、用户名与密码;确认网络可达与安全组放行
|
|||
|
|
- Meilisearch 搜索异常:确认索引名称与 API Key 正确,检查网络连通性
|
|||
|
|
- Redis 连接问题:核对主机、端口与认证信息,验证目标数据库编号
|
|||
|
|
- 日志输出异常:确认日志初始化成功与环境变量设置正确
|
|||
|
|
- 响应编码错误:当启用 Base64 编码时,确保编码流程无异常并返回合适的状态码
|
|||
|
|
|
|||
|
|
**章节来源**
|
|||
|
|
- [main.go:38-58](file://cmd/server/main.go#L38-L58)
|
|||
|
|
- [brand.go:32-43](file://internal/handler/brand.go#L32-L43)
|
|||
|
|
- [meilisearch.go:27-29](file://internal/search/meilisearch.go#L27-L29)
|
|||
|
|
|
|||
|
|
## 结论
|
|||
|
|
该分层架构通过清晰的职责划分与依赖注入,实现了表现层、业务层、数据访问层与基础设施层的解耦。MVC 变体在本项目中体现为:路由与处理器承担表现层职责,仓库承载业务逻辑,数据库/缓存/搜索引擎作为数据访问层,中间件与日志作为基础设施。建议在扩展新功能时遵循“高层不依赖低层”的原则,保持构造函数注入与接口隔离,持续优化连接池与查询性能,并完善监控与告警体系。
|