HAOVP 从代码、配置和部署文件逐渐变成一套完整项目后,我开始重新梳理它的系统结构。仅有代码还不够,我需要说明这个系统为什么这样设计、各个模块如何协作、仓库材料能证明哪些能力,以及哪些结论还需要测试。
面对网关、认证、用户、内容、评论、推荐和管理等多个服务,如果直接按文件目录记录,很容易变成模块清单。我需要先建立一条主线,再把需求、架构、实现、部署和验证放到合适的位置。
先确定文档要回答什么
我先把问题分成四层:
为什么做这个系统
-> 系统需要解决哪些需求
-> 为什么选择当前架构和技术
-> 实现与验证能支持什么结论这四层对应不同证据:
- 背景说明需要可靠的公开资料支持;
- 需求分析需要来自实际功能范围;
- 架构与实现需要由仓库、配置和代码支持;
- 测试结论需要可复现的测试记录支持。
如果把这几类内容混在一起,设计目标很容易被写成已经实现,配置文件也容易被当成验证结果。
从文档目录建立主线
我先搭出六个主要部分:
项目背景与目标
相关技术与选择依据
系统需求与总体设计
核心模块实现
高可用方向与边界
测试方法与结果这个顺序不是简单按开发时间排列,而是一条论证链:先说明问题,再介绍解决问题需要的技术,然后给出系统设计、实现方式和验证方法。
每一部分都要能回答一个具体问题。例如:
- “微服务模块划分”解释服务边界;
- “模块间通信与协作”解释请求怎样跨服务流动;
- “数据库设计”解释数据归属和关系;
- “服务注册与发现”解释实例怎样被找到;
- “流量控制与熔断降级”解释异常怎样被限制;
- “缓存策略与数据一致性”解释性能和状态之间的取舍;
- “测试环境与方法”解释结论怎样得到。
如果某一节只能堆技术名词,却无法说明它在系统中的作用,就需要重新调整。
把仓库结构翻译成系统结构
仓库中的模块结构更接近开发视角:
统一网关
认证服务
用户服务
内容服务
评论服务
推荐服务
管理服务
公共模块
前端用户端与管理端
基础设施与部署文件系统文档需要把这些模块放进更大的关系中:
客户端
-> 网关与访问控制
-> 业务服务
-> 数据库、缓存、对象存储和消息组件
-> 监控与部署这样写可以避免逐个类、逐个接口介绍。文档关注的是服务职责、调用关系和关键实现,代码细节只在能说明设计时出现。
先画边界,再写模块
微服务系统最容易出现的问题,是每个模块都写了很多功能,却没有说明边界为什么这样划分。
我会先为每个服务写一张简表:
| 服务 | 负责的数据和行为 | 需要调用谁 | 不负责什么 |
|---|---|---|---|
| 认证服务 | 登录、令牌与认证状态 | 用户服务、Redis | 用户业务资料 |
| 内容服务 | 视频、分类、标签与上传链路 | 对象存储等组件 | 评论树和推荐排序 |
| 评论服务 | 评论、回复与互动状态 | 内容或用户相关接口 | 视频文件处理 |
| 推荐服务 | 行为、兴趣和候选结果 | 内容服务 | 视频元数据事实来源 |
表中的内容是对 HAOVP 服务职责的概括,不包含内部地址和真实配置。
当“负责什么”和“不负责什么”都能写清楚时,服务拆分才不只是项目中存在多个目录。
技术章节不能脱离使用场景
系统中会出现 Spring Cloud、Nacos、Gateway、Sentinel、MySQL、Redis、MinIO、RocketMQ 和 FFmpeg 等内容。如果按百科形式介绍,每一节都可能正确,但与项目关系不大。
我更希望按“问题—选择—边界”来写:
服务地址会变化
-> 使用 Nacos 做注册发现
-> 实例健康不等于业务链路健康
外部请求需要统一入口
-> 使用 Gateway 做路由和通用校验
-> 业务权限仍由业务服务判断
视频文件不适合放进关系数据库
-> 使用对象存储保存文件
-> 元数据和业务状态仍由数据库管理这样既能说明为什么使用某项技术,也能保留它解决不了的问题。
设计、代码和部署要相互核对
架构图、部署图和时序图不能只追求完整。每画出一个节点或一条调用线,我都需要回到项目中找依据。
我会按以下顺序核对:
文档中的技术或组件
-> README 是否列出
-> 依赖是否存在
-> 配置是否存在
-> 代码是否真正调用
-> 部署文件是否包含
-> 是否有运行或测试记录不同层级支持的写法不同:
- README 中的规划可以写“项目计划采用”;
- 依赖和配置存在可以写“项目已接入相关组件”;
- 代码路径存在可以说明具体实现;
- 部署成功和测试通过必须有单独记录。
例如,技术分析中可以讨论某种分布式事务或数据库集群方案,但如果仓库中没有对应运行时依赖和验证记录,就不能在实现说明中直接写成已经完成。
代码示例只保留关键部分
项目文档不需要粘贴完整控制器或服务实现。过长代码会遮住真正想说明的逻辑,也可能带出内部配置。
我会把代码示例缩小到一个判断点:
public Result<?> handle(Request request) {
validate(request);
Object result = service.execute(request);
return Result.success(result);
}这是结构示例。真正整理代码片段时,需要选择能够说明认证、缓存、推荐或容错逻辑的片段,并去掉无关代码、真实地址、密钥和测试数据。
每段代码前后还要回答:
- 这段代码解决什么问题;
- 为什么放在这一层;
- 异常时怎样处理;
- 它与其他服务怎样协作;
- 当前实现还有什么限制。
没有这些解释,代码片段本身不能证明设计合理。
图表要与文字互相补充
微服务系统中,很多关系只靠文字很难讲清。我准备使用不同图表表达不同问题:
- 架构图:系统由哪些层和组件组成;
- 模块图:服务职责与依赖关系;
- 时序图:登录、上传或评论等请求怎样流动;
- 数据库关系图:核心实体怎样关联;
- 部署图:服务实例和基础组件放在哪里;
- 测试表:输入、步骤、预期和实际结果。
一张图只解决一个主要问题。把所有服务、端口、数据库表和调用关系挤在一张图里,信息很多,却不一定更清楚。
图中的名称还要和正文、代码保持一致。如果正文写“内容服务”,图里却使用另一个历史名称,读者会误以为是两个模块。
高可用章节先写能力边界
“高可用”不能只靠多节点、Docker Compose 或几个中间件支撑。
我把高可用拆成几个可以单独检查的方面:
服务发现与多实例
统一入口与流量控制
超时、熔断和降级
数据复制与故障切换
缓存与数据一致性
对象存储副本
监控、告警与恢复每个方面都要区分:
- 理论上解决什么问题;
- HAOVP 当前采用什么方案;
- 配置和代码完成到什么程度;
- 还需要怎样测试;
- 哪些故障仍可能让系统不可用。
例如,单机 Compose 可以统一编排环境,但宿主机故障时所有容器仍可能一起不可用;MySQL 主从提供副本,但没有选主和流量迁移就不等于自动故障切换;对象副本同步也不等于原生分布式存储。
把这些边界写出来,比笼统地说“系统具备高可用”更准确。
测试部分先写方法,不提前写结果
当前已经规划了功能、性能、压力和容错测试,但 3 月 21 日这一天,我只能先确定测试方法和记录格式,不能用尚未完成的测试填结论。
一个测试条目至少包含:
测试目标
前置条件
请求或操作步骤
预期结果
实际结果
日志或数据证据
是否通过
异常与备注功能测试要覆盖登录、视频、评论、推荐和管理等主要链路;容错测试则要明确停止哪个实例、怎样观察故障、如何判断恢复。
性能测试同样不能只写一个平均响应时间。还需要记录并发模型、请求分布、测试时长、数据规模、环境资源和高分位耗时。环境和方法不完整,单个数字没有足够意义。
用事实强度约束文档用词
整理过程中,我给常见写法设了一条强度顺序:
计划采用
< 已完成设计
< 已有配置或代码
< 已部署运行
< 已经过指定场景验证只有证据到达相应层级,才能使用更强的表达。
这条规则也适用于个人职责。HAOVP 是我的个人项目,我负责需求理解、架构和技术决策、任务拆解、代码审查、测试判断、联调与验收;Codex 辅助代码实现、局部重构、测试补充、问题排查和文档整理。工具辅助不能被写成工具独立完成,也不能因为项目由我独立完成,就扩大成所有技术方案都已经达到生产级水平。
每部分完成后做一次反向检查
一部分写完后,我不会只检查错别字,而是从结论往回找证据:
这一节得出了什么结论?
-> 结论依赖哪些事实?
-> 事实来自代码、配置、文档还是测试?
-> 来源时间是否早于当前记录时间?
-> 是否把设计和实现混在了一起?
-> 是否泄露了账号、地址或个人信息?如果某句话找不到来源,就把它改成设计目标、待验证问题,或者直接删除。
这次梳理给我的结果
这次系统梳理让我从“功能能不能运行”转向“系统能不能被解释”。服务为什么拆分、数据为什么放在这里、故障为什么不会或仍然会扩散,都需要明确答案。
目前项目文档的主线已经建立,接下来要继续核对图表、实现片段和测试方法,把设计、代码、部署与验证分别放在正确位置。文档可以总结项目,但不能替项目补出不存在的实现;只有能从材料和测试中追溯的内容,才适合作为结论写下来。