很多团队对应用架构师的期待,是画出一张分层清晰、组件齐全的架构图。图当然有用,但它只是结论的呈现,不是工作的主体。架构师真正要回答的是几个更基础的问题:这个系统负责什么、不负责什么;哪些能力放在本系统内,哪些交给外部服务;模块之间是同步调用还是异步消息;失败时谁兜底;半年后想换掉某个组件,需要改多少地方。
这些问题没有标准答案,只有“在当前约束下更合适”的答案。约束包括团队规模、交付节奏、现有技术栈、运维能力、合规要求。一个五人的团队和一个五十人的团队,适合的架构往往不同。架构师的专业性,体现在能否把约束讲清楚,而不是能否背出流行架构的名字。
因此,架构师的第一份产出通常不是图,而是一份边界说明和一组决策记录。图可以后画,但边界和决策如果含糊,后面的代码和部署都会为此付出代价。
应用架构中最难的部分,是克制。新需求进来时,默认反应是“加进现有系统”,但每加一块能力,系统的职责就模糊一分。边界清晰的系统,通常有明确的“不做什么”清单:不承担用户认证、不直接对接第三方支付、不保存原始日志、不做跨域数据同步。
边界划分有一个实用方法:按变化频率和变更原因分组。经常一起变化的功能放在同一模块,变化原因不同的功能分到不同模块。这样,模块内部的耦合是合理的,模块之间的依赖是稀少的。比起按技术分层(controller、service、dao)切分,按业务能力切分更容易在需求变化时保持稳定。
边界确定后,要用契约固定下来。模块之间不直接依赖对方的内部实现,而是依赖接口或消息格式。接口一旦发布,修改就要考虑兼容性。这不是为了形式主义,而是为了让“换实现”成为可能。
# 领域层只依赖端口,不依赖具体基础设施
class ArticleRepository(Protocol):
def save(self, article: Article) -> None: ...
def find_by_id(self, article_id: str) -> Article | None: ...
class PublishArticle:
def __init__(self, repo: ArticleRepository, notifier: Notifier):
self.repo = repo
self.notifier = notifier
def execute(self, article_id: str) -> None:
article = self.repo.find_by_id(article_id)
if article is None:
raise ArticleNotFound(article_id)
article.publish()
self.repo.save(article)
self.notifier.published(article_id)这段代码的重点不是设计模式,而是依赖方向:业务逻辑依赖抽象端口,数据库和通知实现放在外层。这样,替换存储或通知方式时,核心逻辑不需要改动。边界清晰的直接收益,是变更成本可预测。
系统之间怎么连,是应用架构师最常被追问的问题。同步调用简单直接,但会把可用性绑在一起:下游慢,上游跟着慢;下游挂,上游跟着挂。异步消息解耦了时间,但引入了最终一致性、重复消费和顺序问题。
选择哪种方式,取决于业务对一致性和时效的要求,而不是技术偏好。强一致、低延迟、调用链短的场景,同步更合适;跨系统、可容忍延迟、需要削峰的场景,异步更合适。更常见的现实是混合:核心交易同步,通知和统计异步。
无论哪种方式,契约都要写清楚:输入输出字段、错误码、超时、重试策略、幂等要求。异步消息还要额外说明:消息可能重复、可能乱序、消费失败后进死信队列还是重试。这些内容如果只存在于某个人的记忆里,集成迟早出问题。
架构师在这里的职责,不是替每个服务写代码,而是确保契约被记录、被评审、被测试覆盖。契约是集成的法律,口头约定不算数。
架构决策有一个容易被忽略的维度:可逆性。有些决策改起来很便宜,比如换一个日志库;有些决策改起来很贵,比如选错数据库模型或拆分粒度。架构师应当优先选择可逆的方案,或者在做出不可逆决策前,投入更多论证。
一个实用原则是“推迟不可逆决策”。如果现在两种方案都可行,而其中一种保留更多调整空间,就选它。比如,先做模块化单体,等边界稳定、团队和流量都提出明确需求时再拆服务;先定义接口,再决定具体实现。这不是保守,而是承认信息不完备。
可逆性也影响技术选型。引入一个新框架前,要问:如果半年后要换掉它,成本有多大?它是否把业务逻辑绑死在框架里?它是否有清晰的退出路径?好的架构不是没有技术债,而是技术债有账可查、有路可退。
架构决策最容易失传。半年后有人问“为什么当时用消息队列而不是直接调用”,如果没人记得,团队就只能靠猜,或者重复踩坑。解决方式是架构决策记录:一条决策一页纸,写清楚背景、选项、决定和后果。
下面是一个极简模板,可以放进代码仓库,和代码一起评审:
# ADR-007: 文章发布采用异步通知
## 状态
已接受
## 背景
发布文章后需要通知订阅用户,通知服务偶发超时,
若同步调用会导致发布接口不可用。
## 选项
1. 同步调用通知服务
2. 发布事件,由通知服务异步消费
3. 定时任务扫描待通知文章
## 决定
采用选项 2,事件通过现有消息队列投递。
## 后果
- 发布接口不再依赖通知服务可用性
- 通知存在秒级延迟,需接受最终一致
- 消费方必须实现幂等决策记录的价值不在格式,而在于它迫使架构师把“为什么”写下来。它也是团队对齐的工具:新成员可以读记录理解现状,评审时可以针对假设讨论,而不是针对个人偏好争论。
在实际项目中,可以用一份清单检验应用架构是否站得住:系统边界是否有明确的“不做什么”;模块依赖方向是否清晰;跨系统契约是否写明了错误、超时和幂等;关键决策是否有记录;不可逆决策是否经过论证;是否有机制检查依赖规则。把这些做成评审项,架构才不会停留在文档里。
应用架构师的核心工作,是在业务目标和技术约束之间做选择,并让这些选择可解释、可回退、可检查。边界决定系统能长多大,契约决定系统能否协作,可逆性决定系统能否演进,决策记录决定团队能否记住为什么。架构图只是这些工作的一个快照。真正支撑系统走远的,是那些被写下来、被评审过、被代码和流程约束住的决定。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。