Appearance
软件架构设计导论
软件架构不是把系统套进某个流行名词,而是围绕业务复杂度、团队协作、数据一致性、部署运维和长期演进做取舍。好的架构能让系统在需求变化时仍然容易理解、容易修改、容易验证、容易上线。
架构设计的核心判断标准不是形式是否先进,而是能否让变化成本保持在可控制的范围内。
架构选型判断
架构选型先看两个维度:业务复杂度和组织复杂度。业务复杂度高,说明规则、流程、模型和数据一致性难;组织复杂度高,说明团队多、交付链路长、部署协作复杂。
| 当前情况 | 推荐起点 | 不建议一开始做什么 |
|---|---|---|
| 小团队、业务简单、快速交付 | 分层单体 | 不要直接拆微服务 |
| 小团队、业务复杂、规则多 | DDD + 模块化单体 + 六边形 | 不要先拆部署单元 |
| 多团队、业务相对清晰、集成多 | SOA / API 集成 / 模块化治理 | 不要让服务边界按技术层拆 |
| 多团队、业务复杂、交付互相阻塞 | 微服务 + DDD 边界 + 自动化治理 | 不要只拆服务不拆数据边界 |
| 读写差异大、查询压力高 | CQRS / 读模型 / 缓存视图 | 不要为了概念强行事件溯源 |
| 状态变化要通知多个系统 | 事件驱动 | 不要把事件当远程过程调用 |
| 弹性伸缩、容器化、自动化要求高 | 云原生 | 不要忽略可观测性和发布治理 |
| 短任务、事件触发、流量波动大 | Serverless | 不要放入长事务和强状态流程 |
架构决策流程
实际做架构决策时,可以按这个顺序问:
- 业务边界是否清楚:如果边界不清,先做领域梳理,不要急着拆服务。
- 数据归属是否清楚:每份核心数据应该由哪个模块或服务负责。
- 是否需要独立交付:如果多个团队经常互相阻塞,才考虑独立部署边界。
- 是否能接受最终一致:不能接受时优先保持单体事务或强一致边界。
- 运维能力是否跟得上:微服务、云原生、事件驱动都需要监控、追踪、发布和故障处理能力。
- 复杂度是否值得:架构引入的新成本必须小于它解决的问题。
架构演进路线
架构最好渐进演进,不要一开始跳到最复杂形态。
第一阶段:分层单体
适合业务刚起步、团队小、需求变化快的阶段。
重点不是“先进”,而是把代码放整齐:
- 表示层只处理接口协议、参数解析、响应封装。
- 应用层编排用例,不写复杂领域规则。
- 领域层表达业务规则、状态变化和核心约束。
- 基础设施层处理数据库、缓存、消息、外部 API。
第二阶段:模块化单体
当单体变大后,先做模块化,而不是直接拆微服务。
落地动作:
- 按业务能力拆包或模块,例如订单、库存、支付、会员。
- 限制跨模块直接访问数据库表。
- 模块之间通过接口、应用服务或领域事件协作。
- 建立模块依赖规则,避免循环依赖。
第三阶段:DDD 与六边形
当业务规则复杂后,要把业务模型从技术细节中保护出来。
落地动作:
- 用限界上下文划分模型边界。
- 用聚合定义一致性边界。
- 用端口定义核心业务需要的外部能力。
- 用适配器对接数据库、消息、第三方 SDK。
第四阶段:微服务
微服务不是“把类拆成服务”,而是把独立业务能力、数据归属和交付责任拆开。
适合拆服务的信号:
- 团队之间交付频繁互相阻塞。
- 某个业务能力需要独立扩缩容。
- 某个模块故障需要和主系统隔离。
- 数据边界清楚,服务能拥有自己的数据。
第五阶段:云原生和平台化治理
当服务数量增加后,核心问题从“如何写服务”变成“如何稳定运行服务”。
需要补齐:
- 容器化和编排。
- 配置、密钥、服务发现。
- 灰度发布、回滚、弹性伸缩。
- 日志、指标、链路追踪。
- 限流、熔断、超时、重试。
主流架构对比
| 架构 | 核心定位 | 优先解决的问题 | 主要代价 |
|---|---|---|---|
| 分层架构 | 按职责层次组织代码 | 结构清晰、职责分离 | 容易贫血、跨层绕行 |
| 六边形架构 | 核心业务通过端口隔离外部技术 | 可测试、技术替换 | 抽象成本上升 |
| 微服务架构 | 按业务能力拆分独立服务 | 团队自治、独立部署 | 分布式复杂度 |
| 事件驱动架构 | 用事件传播状态变化 | 解耦、异步扩展 | 最终一致和排查成本 |
| CQRS | 读写模型分离 | 高性能查询、复杂写模型 | 同步和一致性成本 |
| 云原生架构 | 面向弹性、自动化和可观测性运行系统 | 规模化运维 | 工具链和治理成本 |
| SOA | 企业能力服务化和标准化集成 | 企业系统集成 | 治理和中间层复杂 |
| Serverless | 由事件触发函数运行,平台托管运维 | 短任务和弹性流量 | 冷启动、状态和可观测性 |
| DDD | 围绕业务领域建模 | 复杂业务表达 | 学习和建模成本 |
常见架构误区
把微服务当成默认答案
微服务能解决团队自治和独立部署问题,但会引入网络调用、分布式事务、链路追踪、服务治理、数据一致性、发布协调等成本。没有自动化运维和清晰业务边界时,微服务会放大混乱。
只拆代码,不拆数据所有权
服务边界清楚但共享同一批表,本质上仍然强耦合。一个服务直接改另一个服务的数据,会让所有边界失效。
用技术层拆服务
把系统拆成用户服务、订单服务是业务拆法;把系统拆成 controller 服务、service 服务、dao 服务是错误方向。服务边界应按业务能力和数据归属划分。
把事件当同步调用替代品
事件表示已经发生的事实,例如 OrderPaid。如果发送事件只是为了让对方立刻返回结果,那更像远程调用,不是事件驱动。
架构图画得很好,但没有约束
架构需要约束才能落地。比如模块依赖规则、数据库访问规则、接口版本规则、发布规则、错误处理规则、监控指标规则。
架构落地检查清单
业务边界
- 核心业务流程是否画清楚。
- 每个模块或服务负责的数据是否明确。
- 哪些规则必须强一致,哪些可以最终一致。
- 哪些概念在不同上下文中含义不同。
代码结构
- 包结构是否按业务能力组织。
- 应用层、领域层、基础设施层职责是否清楚。
- 是否存在跨层调用和循环依赖。
- 业务规则是否被技术细节污染。
数据与集成
- 是否有共享表、跨库直连、跨服务直接写库。
- 接口是否有版本管理和兼容策略。
- 事件是否有幂等键、重试和死信处理。
- 外部依赖是否有超时、熔断和降级。
部署与运维
- 是否有健康检查、日志、指标、链路追踪。
- 是否支持灰度、回滚和配置隔离。
- 服务容量和扩缩容策略是否明确。
- 故障时是否能定位到模块、接口、依赖和数据。
阅读顺序
建议按这个顺序阅读:
- 分层架构:先建立最基础的代码分层和职责边界。
- 六边形架构:理解如何保护业务核心。
- DDD:学习复杂业务如何建模和划边界。
- 事件驱动架构:理解异步解耦和最终一致。
- CQRS:理解读写模型分离的代价和收益。
- 微服务架构:在边界和治理能力具备后再学习拆分。
- 云原生架构:理解服务规模化运行需要哪些平台能力。
架构设计的判断标准很朴素:一个改动能否被限制在合理范围内,一个故障能否被快速定位和隔离,一个新团队能否在清晰边界内独立交付。
