設(shè)計(jì)不是天賦:一套可復(fù)制的技能樹(shù)與實(shí)踐方法)
假設(shè)你是一個(gè)寫(xiě)了三五年業(yè)務(wù)代碼的后端工程師日常 CRUD 得心應(yīng)手搜索引擎也玩得明白但某天評(píng)審會(huì)上技術(shù)負(fù)責(zé)人突然問(wèn)你“你這個(gè)系統(tǒng)的架構(gòu)是什么為什么訂單模塊和支付模塊要這樣分”你發(fā)現(xiàn)自己只能說(shuō)“我們用了微服務(wù)”“RPC 就完事了”這類(lèi)話。這不是少數(shù)人的困境而是很多后端開(kāi)發(fā)者的共同痛點(diǎn)代碼能力不差架構(gòu)設(shè)計(jì)能力卻一直停留在“看別人的架構(gòu)圖點(diǎn)頭”的水平。這里我想先給出一個(gè)明確判斷架構(gòu)設(shè)計(jì)不是少數(shù)天才的靈感也不完全依賴(lài)“十年經(jīng)驗(yàn)自然就會(huì)”。它是一套可以拆解、訓(xùn)練和顯式化的技能體系。所謂技能意味著你不需要等某種頓悟而是可以通過(guò)流程、模板、檢查清單和真實(shí)項(xiàng)目訓(xùn)練穩(wěn)定地產(chǎn)出合格的架構(gòu)設(shè)計(jì)。這也是為什么現(xiàn)在很多團(tuán)隊(duì)開(kāi)始把“架構(gòu)設(shè)計(jì)”本身沉淀成可復(fù)用的文檔模板、決策記錄甚至 Agent Skill。這篇文章會(huì)從概念、技能樹(shù)、完整工作流程、訂單系統(tǒng)示例、架構(gòu)評(píng)審、常見(jiàn)誤區(qū)到團(tuán)隊(duì)落地把“架構(gòu)設(shè)計(jì)技能”這件事講透。文章很長(zhǎng)建議先收藏再慢慢看。讀完你可以得到三樣?xùn)|西一張告訴你要練什么的技能樹(shù)、一套可以直接復(fù)制的文檔和決策模板、一份能拿去評(píng)審自己設(shè)計(jì)的檢查清單。1. 為什么“架構(gòu)設(shè)計(jì)”是一項(xiàng)技能而不是天賦很多開(kāi)發(fā)者對(duì)架構(gòu)設(shè)計(jì)的認(rèn)知是先寫(xiě)好幾年代碼然后某一天“開(kāi)竅”了突然就能畫(huà)架構(gòu)圖了。這種認(rèn)知很危險(xiǎn)因?yàn)樗岩粋€(gè)可以被系統(tǒng)訓(xùn)練的能力歸類(lèi)成了不可復(fù)制的個(gè)人天賦。技能的基本特征是“可分解、可學(xué)習(xí)、可訓(xùn)練、可評(píng)估”。架構(gòu)設(shè)計(jì)完全滿足這四個(gè)條件。拆開(kāi)來(lái)看一次架構(gòu)設(shè)計(jì)由幾個(gè)環(huán)節(jié)組成理解業(yè)務(wù)目標(biāo)、識(shí)別約束條件、抽象系統(tǒng)邊界、設(shè)計(jì)模塊劃分、選型技術(shù)方案、權(quán)衡質(zhì)量屬性、記錄決策過(guò)程。每一個(gè)環(huán)節(jié)都有對(duì)應(yīng)的方法論都可以單獨(dú)訓(xùn)練。例如“識(shí)別約束條件”可以練成本上限是多少、團(tuán)隊(duì)多少人、吞吐量預(yù)期是多少、合規(guī)要求有哪些、現(xiàn)有系統(tǒng)能復(fù)用多少。把這些問(wèn)句列出來(lái)就是一份檢查清單。清單不會(huì)讓你一夜成為架構(gòu)大師但會(huì)把你做架構(gòu)設(shè)計(jì)的下限抬得很高。為什么很多人仍然覺(jué)得架構(gòu)設(shè)計(jì)難因?yàn)榧軜?gòu)設(shè)計(jì)的“結(jié)果”往往是圖或者 PPT真正重要的“過(guò)程”——決策和取舍——是看不見(jiàn)的。老手看一眼需求就知道要拆幾個(gè)服務(wù)是因?yàn)樗麄冊(cè)谶^(guò)去幾千個(gè)項(xiàng)目里反復(fù)衡量過(guò)“拆或不拆”的代價(jià)這些判斷被壓縮成了直覺(jué)。但直覺(jué)背后依然是一層層顯式的問(wèn)題比如“數(shù)據(jù)一致性要求有多高”“團(tuán)隊(duì)協(xié)作邊界在哪里”“故障爆炸半徑能不能接受”“部署和發(fā)布頻率是否跟得上”。新手如果能一層層問(wèn)出這些問(wèn)題產(chǎn)出的設(shè)計(jì)質(zhì)量并不會(huì)差太多。這正是“技能化”的意義把高手腦內(nèi)壓縮的判斷還原成顯式的步驟和問(wèn)題讓更多人能夠執(zhí)行。所以本文的核心觀點(diǎn)是架構(gòu)設(shè)計(jì) 在不確定條件下做有記錄的權(quán)衡決策。注意兩個(gè)關(guān)鍵詞。第一是“不確定”架構(gòu)師永遠(yuǎn)不可能拿到完整信息才開(kāi)始設(shè)計(jì)必須學(xué)會(huì)帶著假設(shè)推進(jìn)第二是“有記錄”只做決策不記錄理由設(shè)計(jì)就沒(méi)有生命力后來(lái)者無(wú)法演進(jìn)只能推翻重來(lái)。2. 架構(gòu)設(shè)計(jì)的核心概念與技能樹(shù)在討論具體流程之前先統(tǒng)一幾個(gè)核心概念。這些概念會(huì)貫穿后續(xù)的示例和評(píng)審清單。2.1 軟件架構(gòu)是什么軟件架構(gòu)是系統(tǒng)的重要組件及其相互關(guān)系以及影響這些關(guān)系設(shè)計(jì)和演進(jìn)的原則。通俗講架構(gòu)不僅決定了系統(tǒng)里有哪些模塊更決定了這些模塊之間怎么通信、質(zhì)量屬性如何保證、未來(lái)如何演進(jìn)。一個(gè)系統(tǒng)沒(méi)有架構(gòu)是不可能的哪怕是“一坨代碼直接堆在 controller 里”也是一種架構(gòu)只是這種架構(gòu)是在無(wú)意識(shí)中長(zhǎng)出來(lái)的沒(méi)人對(duì)它負(fù)責(zé)罷了。2.2 質(zhì)量屬性架構(gòu)設(shè)計(jì)的標(biāo)尺架構(gòu)設(shè)計(jì)的好壞不是靠感覺(jué)而是靠質(zhì)量屬性。常見(jiàn)質(zhì)量屬性包括性能、可用性、可擴(kuò)展性、安全性、可維護(hù)性、可測(cè)試性、成本。任何架構(gòu)方案都在這些屬性之間做權(quán)衡。例如微服務(wù)提升了可擴(kuò)展性和團(tuán)隊(duì)自治但犧牲了運(yùn)維復(fù)雜度與分布式事務(wù)成本。沒(méi)有了質(zhì)量屬性作為標(biāo)尺“架構(gòu)好壞”就變成了純主觀爭(zhēng)論。2.3 架構(gòu)風(fēng)格不要為了拆分而拆分架構(gòu)風(fēng)格是常見(jiàn)問(wèn)題的一套預(yù)定義解決方案。理解幾種主流風(fēng)格能幫助你在設(shè)計(jì)時(shí)快速生成候選方案架構(gòu)風(fēng)格核心特征適合場(chǎng)景主要成本單體架構(gòu)應(yīng)用作為一個(gè)整體部署小團(tuán)隊(duì)、業(yè)務(wù)簡(jiǎn)單、初期快速驗(yàn)證規(guī)模變大后難以局部擴(kuò)展模塊化單體保持單部署單元但內(nèi)部嚴(yán)格分層分模塊中小團(tuán)隊(duì)希望控制復(fù)雜度且不愿意承擔(dān)分布式成本需要很強(qiáng)的模塊邊界紀(jì)律微服務(wù)架構(gòu)按業(yè)務(wù)能力拆分為獨(dú)立部署服務(wù)大團(tuán)隊(duì)、多業(yè)務(wù)線、需要獨(dú)立伸縮和發(fā)布運(yùn)維、觀測(cè)、分布式事務(wù)成本高事件驅(qū)動(dòng)架構(gòu)通過(guò)異步事件通信解耦生產(chǎn)者與消費(fèi)者高并發(fā)、異步流程、系統(tǒng)間集成事件一致性、消息回溯困難分層架構(gòu)按技術(shù)職責(zé)分層接口、應(yīng)用、領(lǐng)域、基礎(chǔ)設(shè)施絕大多數(shù)業(yè)務(wù)系統(tǒng)分層過(guò)細(xì)會(huì)導(dǎo)致樣板代碼膨脹這里特別想提醒一點(diǎn)微服務(wù)只是架構(gòu)風(fēng)格的一種不是架構(gòu)設(shè)計(jì)的目標(biāo)。如果團(tuán)隊(duì)只有一二十人、業(yè)務(wù)模型還在快速變化、發(fā)布頻率并不高模塊化單體通常是比微服務(wù)更穩(wěn)妥的選擇。這個(gè)判斷在后文的訂單系統(tǒng)示例中會(huì)再次出現(xiàn)。2.4 C4 模型統(tǒng)一大家的架構(gòu)視圖C4 模型把架構(gòu)視圖分成四個(gè)層次Context系統(tǒng)上下文、Container容器/進(jìn)程/應(yīng)用、Component組件、Code代碼。很多團(tuán)隊(duì)架構(gòu)討論低效是因?yàn)橛懻?Context 層的人在講“我們系統(tǒng)之間怎么調(diào)用”而聽(tīng)眾在思考 Component 層的類(lèi)怎么放。C4 的價(jià)值是讓團(tuán)隊(duì)先約定“現(xiàn)在討論的是哪一層”避免雞同鴨講。后續(xù)實(shí)戰(zhàn)示例會(huì)給出 Context 層的繪制示例。2.5 ADR記錄架構(gòu)決策為什么這么做ADRArchitecture Decision Record架構(gòu)決策記錄是一種輕量級(jí)的決策文檔。一個(gè) ADR 通常包含背景、決策、備選方案、后果。它解決的核心問(wèn)題是幾個(gè)月后或者換人后團(tuán)隊(duì)仍然知道“為什么當(dāng)初這么選”。沒(méi)有 ADR 的架構(gòu)文檔最后一定會(huì)退化成“現(xiàn)狀說(shuō)明書(shū)”只能告訴大家系統(tǒng)長(zhǎng)什么樣卻說(shuō)不清為什么長(zhǎng)成這樣。2.6 架構(gòu)設(shè)計(jì)技能樹(shù)綜合以上概念可以畫(huà)出一張架構(gòu)設(shè)計(jì)技能樹(shù)需求抽象從模糊的業(yè)務(wù)描述里提取功能范圍、質(zhì)量屬性和約束。邊界識(shí)別劃分系統(tǒng)內(nèi)部和外部確定協(xié)作關(guān)系。技術(shù)選型評(píng)估不同中間件、框架、語(yǔ)言是否匹配當(dāng)前約束。模塊設(shè)計(jì)定義模塊邊界、依賴(lài)方向和接口契約。質(zhì)量設(shè)計(jì)考慮性能、可用性、安全、可觀測(cè)性的落地手段。文檔與評(píng)審把決策顯式化并能讓他人驗(yàn)證。技能樹(shù)的每個(gè)葉子都可以用對(duì)應(yīng)的模板和清單來(lái)訓(xùn)練。下面我們就按這個(gè)技能樹(shù)走一遍完整的架構(gòu)設(shè)計(jì)流程。3. 架構(gòu)設(shè)計(jì)的完整工作流程架構(gòu)設(shè)計(jì)不應(yīng)該從畫(huà)圖開(kāi)始而應(yīng)該從澄清問(wèn)題開(kāi)始。很多失敗的架構(gòu)設(shè)計(jì)問(wèn)題都出在第一步需求還沒(méi)對(duì)齊就開(kāi)始畫(huà)高深的架構(gòu)圖。這里給出一套比較通用的六步流程。3.1 第一步澄清目標(biāo)與約束開(kāi)工前先問(wèn)一組問(wèn)題這次設(shè)計(jì)的業(yè)務(wù)目標(biāo)是什么要支撐什么增長(zhǎng)或者解決什么現(xiàn)狀問(wèn)題硬性約束有哪些預(yù)算、時(shí)間、團(tuán)隊(duì)規(guī)模、第三方依賴(lài)質(zhì)量屬性指標(biāo)是多少例如 QPS、可用性 SLA、響應(yīng)時(shí)間 P99。有沒(méi)有必須遵守的合規(guī)或安全要求這一步的產(chǎn)出是“一句話目標(biāo) 約束清單”。如果約束不明確后續(xù)所有決策都可能是空中樓閣。3.2 第二步識(shí)別干系人與系統(tǒng)上下文明確誰(shuí)在跟這個(gè)系統(tǒng)交互用戶、運(yùn)營(yíng)人員、外部系統(tǒng)、下游依賴(lài)。畫(huà)出 Context 圖。這個(gè)步驟的作用是確定系統(tǒng)的外部邊界避免把所有外部系統(tǒng)都當(dāng)成系統(tǒng)內(nèi)部的一部分。很多人畫(huà)架構(gòu)圖一上來(lái)就開(kāi)始畫(huà)內(nèi)部模塊其實(shí)應(yīng)該先確定外面那一圈邊界。3.3 第三步生成候選設(shè)計(jì)基于需求和邊界至少給出兩個(gè)候選方案而不是只拿著一個(gè)方案去評(píng)審。候選方案可以來(lái)自不同的架構(gòu)風(fēng)格組合。比如“單體重構(gòu)” vs “模塊化單體” vs “微服務(wù)拆分”。每個(gè)方案都要說(shuō)明它滿足了哪些質(zhì)量屬性在哪些方面表現(xiàn)較弱。3.4 第四步權(quán)衡與決策把候選方案放進(jìn)一個(gè)評(píng)估表里按質(zhì)量屬性逐項(xiàng)打分或者做優(yōu)劣分析最后選擇一個(gè)最適合當(dāng)前約束的方案。注意“最適合”不等于“技術(shù)上最先進(jìn)”。成本、團(tuán)隊(duì)能力、時(shí)間窗口都是決策變量。決策時(shí)用 ADR 把理由記錄下來(lái)。3.5 第五步形成設(shè)計(jì)文檔與契約只畫(huà)圖不夠還需要把設(shè)計(jì)落成可讀的文檔和契約。包括C4 圖、模塊清單、接口契約、數(shù)據(jù)流、異常鏈路、部署架構(gòu)。其中接口契約建議直接寫(xiě)成 OpenAPI 或 protobuf讓生成代碼和文檔共用同一份定義避免文檔和代碼分家。3.6 第六步評(píng)審與演進(jìn)架構(gòu)設(shè)計(jì)不是一次性活動(dòng)。評(píng)審?fù)ㄟ^(guò)、代碼落地后還需要持續(xù)檢查“實(shí)際代碼是否還符合設(shè)計(jì)”以及“當(dāng)初的假設(shè)是否失效”。這需要有常規(guī)的架構(gòu)評(píng)審機(jī)制和 ADR 沉淀。沒(méi)有演進(jìn)的架構(gòu)文檔就是很快腐爛的存檔。這套六步流程本身就是可訓(xùn)練、可復(fù)制的技能載體。一個(gè)人哪怕經(jīng)驗(yàn)不足只要嚴(yán)格走完流程、產(chǎn)出對(duì)應(yīng)模板也能做出值得評(píng)審的架構(gòu)方案。4. 從需求到架構(gòu)一個(gè)訂單系統(tǒng)的設(shè)計(jì)過(guò)程為了讓上面的流程更具體這里設(shè)計(jì)一個(gè)常見(jiàn)場(chǎng)景電商團(tuán)隊(duì)要建設(shè)“訂單中心”負(fù)責(zé)下單、支付回調(diào)、履約、售后等能力。現(xiàn)狀是一個(gè)老單體應(yīng)用里已經(jīng)揉進(jìn)了訂單、商品、庫(kù)存、支付等邏輯隨著業(yè)務(wù)增長(zhǎng)發(fā)布越來(lái)越難團(tuán)隊(duì)間開(kāi)始互相踩代碼老板希望解決協(xié)作和擴(kuò)展問(wèn)題。需求看起來(lái)很清楚但架構(gòu)設(shè)計(jì)必須把模糊需求轉(zhuǎn)成具體約束。我們先做 4.1 到 4.4 的推演再在下一章給出具體文檔產(chǎn)物。4.1 需求與約束識(shí)別從業(yè)務(wù)描述里能提取出的功能范圍用戶可創(chuàng)建訂單、取消訂單、查看訂單詳情。支付成功或失敗后訂單狀態(tài)需要更新。支付成功后需要向倉(cāng)儲(chǔ)側(cè)下發(fā)履約單。運(yùn)營(yíng)人員可查詢訂單并處理售后。關(guān)鍵質(zhì)量屬性和約束目標(biāo) QPS 并不高日均訂單量數(shù)十萬(wàn)量級(jí)。可用性要求較高訂單不能丟失但允許短暫延遲。團(tuán)隊(duì)規(guī)模僅兩個(gè)后端小組約十幾人當(dāng)前沒(méi)有專(zhuān)職運(yùn)維團(tuán)隊(duì)。老系統(tǒng)已經(jīng)在生產(chǎn)運(yùn)行必須平滑遷移。訂單金額相關(guān)操作需要考慮審計(jì)和數(shù)據(jù)一致性。從這些約束能明顯感覺(jué)到團(tuán)隊(duì)規(guī)模不大業(yè)務(wù)復(fù)雜度中等吞吐壓力有限。此時(shí)首選微服務(wù)架構(gòu)其實(shí)風(fēng)險(xiǎn)偏高。4.2 系統(tǒng)上下文建模我們把系統(tǒng)外部的角色列出來(lái)消費(fèi)者通過(guò)商城前端下單。運(yùn)營(yíng)人員查詢訂單、處理售后。支付網(wǎng)關(guān)外部支付能力。倉(cāng)儲(chǔ)中心接收履約單。消息中心發(fā)送短信和站內(nèi)信。系統(tǒng)上下文很清晰訂單系統(tǒng)在中間外部跟這些角色打交道。這一步不需要考慮內(nèi)部怎么拆分先確定邊界。4.3 生成候選方案根據(jù)約束可以提出三個(gè)候選方案方案 A繼續(xù)單體不做拆分只優(yōu)化分層。優(yōu)點(diǎn)改動(dòng)最小、風(fēng)險(xiǎn)最低。缺點(diǎn)團(tuán)隊(duì)協(xié)作問(wèn)題沒(méi)解決發(fā)布沖突依舊無(wú)法滿足后續(xù)多團(tuán)隊(duì)分工。方案 B模塊化單體。將訂單、支付、庫(kù)存等邏輯按業(yè)務(wù)模塊隔離模塊間通過(guò)內(nèi)部接口調(diào)用仍然是一個(gè)進(jìn)程一個(gè)部署單元。優(yōu)點(diǎn)復(fù)雜度可控協(xié)作邊界清楚所需基礎(chǔ)設(shè)施變化小。缺點(diǎn)模塊邊界維護(hù)需要紀(jì)律無(wú)法獨(dú)立擴(kuò)縮容物理隔離不夠。方案 C直接微服務(wù)拆分。按訂單、支付、履約等服務(wù)拆成多個(gè)獨(dú)立部署單元。優(yōu)點(diǎn)獨(dú)立發(fā)布、獨(dú)立伸縮、服務(wù)邊界強(qiáng)。缺點(diǎn)分布式事務(wù)、服務(wù)發(fā)現(xiàn)、日志鏈路、監(jiān)控體系、容器編排、運(yùn)維成本都需要補(bǔ)齊對(duì)兩個(gè)小團(tuán)隊(duì)壓力大。4.4 權(quán)衡與決策維度方案A 單體方案B 模塊化單體方案C 微服務(wù)團(tuán)隊(duì)協(xié)作改善弱中強(qiáng)部署發(fā)布效率弱中強(qiáng)運(yùn)維成本低低高分布式事務(wù)風(fēng)險(xiǎn)無(wú)內(nèi)部事務(wù)高遷移風(fēng)險(xiǎn)低中高技術(shù)演進(jìn)空間弱中強(qiáng)強(qiáng)在這個(gè)假設(shè)場(chǎng)景里更穩(wěn)妥的判斷是選擇方案 B模塊化單體作為第一階段架構(gòu)。核心原因是約束中的“團(tuán)隊(duì)規(guī)模小、無(wú)專(zhuān)職運(yùn)維、需要平滑遷移”。先通過(guò)模塊邊界解決協(xié)作問(wèn)題沉淀好領(lǐng)域模型和接口契約等業(yè)務(wù)和團(tuán)隊(duì)規(guī)模達(dá)到一定閾值后再按邊界逐步把模塊拆成獨(dú)立服務(wù)。這種“先收緊邊界再物理拆分”的路徑比直接上微服務(wù)穩(wěn)健得多。5. 完整示例架構(gòu)設(shè)計(jì)文檔與決策記錄上一章是推演過(guò)程這一章給出可以直接復(fù)制用于自己項(xiàng)目的產(chǎn)物模板。這些產(chǎn)物都圍繞“模塊化單體”方案展開(kāi)。5.1 上下文圖C4 Model / PlantUML 示例C4 的 Context 層用于描述系統(tǒng)與外界的邊界。以下是一個(gè)可復(fù)制的 PlantUML 骨架渲染后的圖可以作為架構(gòu)設(shè)計(jì)文檔的第一張圖。startuml order-context title 訂單中心系統(tǒng)上下文 actor 消費(fèi)者 actor 運(yùn)營(yíng)人員 rectangle 訂單中心 { usecase 創(chuàng)建訂單 as UC_CREATE usecase 訂單狀態(tài)變更 as UC_STATUS usecase 訂單查詢 as UC_QUERY usecase 售后處理 as UC_AFTER_SALE } rectangle 支付網(wǎng)關(guān) rectangle 倉(cāng)儲(chǔ)中心 rectangle 消息中心 消費(fèi)者 -- UC_CREATE 消費(fèi)者 -- UC_QUERY 運(yùn)營(yíng)人員 -- UC_QUERY 運(yùn)營(yíng)人員 -- UC_AFTER_SALE UC_CREATE .. 支付網(wǎng)關(guān) : 發(fā)起支付 UC_STATUS .. 支付網(wǎng)關(guān) : 支付結(jié)果回調(diào) UC_STATUS .. 倉(cāng)儲(chǔ)中心 : 下發(fā)履約單 UC_STATUS .. 消息中心 : 發(fā)送通知 enduml圖中最重要的是表達(dá)系統(tǒng)與外部依賴(lài)之間的關(guān)系。評(píng)審時(shí)可以先看這張圖系統(tǒng)邊界是否清楚、外部依賴(lài)是否齊全。很多人把上下文圖畫(huà)成了內(nèi)部模塊圖這是最常見(jiàn)的錯(cuò)誤。5.2 架構(gòu)決策記錄 ADR 示例ADR 的價(jià)值在于記錄決策理由。下面是一份完整示例可以直接放入團(tuán)隊(duì)倉(cāng)庫(kù)的docs/adr/目錄。# ADR-0001訂單中心第一階段采用模塊化單體架構(gòu) - 狀態(tài)已接受 - 日期2025-01-15 - 決策者張三、李四、王五 ## 背景 訂單中心需要從老單體中拆分演進(jìn)目標(biāo)是改善團(tuán)隊(duì)協(xié)作、 降低發(fā)布沖突并為后續(xù)業(yè)務(wù)擴(kuò)展保留空間。 當(dāng)前團(tuán)隊(duì)規(guī)模為兩個(gè)后端小組約 15 人沒(méi)有專(zhuān)職運(yùn)維團(tuán)隊(duì) 生產(chǎn)系統(tǒng)要求平滑遷移。 ## 決策 第一階段采用“模塊化單體”架構(gòu) - 保持單個(gè)部署單元避免過(guò)早引入分布式基礎(chǔ)設(shè)施。 - 在代碼層面嚴(yán)格劃分訂單、支付、履約、售后等業(yè)務(wù)模塊。 - 模塊之間只允許通過(guò)應(yīng)用層接口調(diào)用禁止直接訪問(wèn)內(nèi)部倉(cāng)儲(chǔ)。 - 采用領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)DDD的戰(zhàn)術(shù)模式定義模塊邊界。 ## 備選方案 1. 直接微服務(wù)拆分可提供更強(qiáng)的獨(dú)立伸縮與發(fā)布能力 但需要投入服務(wù)發(fā)現(xiàn)、鏈路追蹤、日志聚合、容器編排等基礎(chǔ)設(shè)施 團(tuán)隊(duì)規(guī)模與運(yùn)維成本不允許。 2. 完全單體不分層遷移成本最低但無(wú)法解決團(tuán)隊(duì)協(xié)作沖突 也不利于后續(xù)演進(jìn)。 ## 后果 - 好處協(xié)作邊界更清晰發(fā)布風(fēng)險(xiǎn)可控不需要重寫(xiě)系統(tǒng)。 - 代價(jià)模塊化邊界需要長(zhǎng)期維護(hù)編譯期約束不足 必須通過(guò)架構(gòu)測(cè)試保證模塊依賴(lài)方向不反向。 - 演進(jìn)路徑當(dāng)訂單模塊流量或團(tuán)隊(duì)規(guī)模達(dá)到預(yù)設(shè)閾值時(shí) 可優(yōu)先將訂單模塊拆分為獨(dú)立服務(wù)再逐步遷移其他模塊。注意 ADR 中必須有“備選方案”和“后果”。沒(méi)有備選方案ADR 就不是決策記錄而是公告“后果”則讓后來(lái)者知道這個(gè)決策付出的成本是什么。5.3 接口契約示例OpenAPI接口契約是模塊間協(xié)作的“法律”。建議在模塊化單體階段就把接口用 OpenAPI 定義好。以下是最小示例定義訂單創(chuàng)建的請(qǐng)求響應(yīng)結(jié)構(gòu)。openapi: 3.0.3 info: title: Order Service API version: 1.0.0 paths: /orders: post: summary: 創(chuàng)建訂單 requestBody: required: true content: application/json: schema: $ref: #/components/schemas/CreateOrderRequest responses: 200: description: 下單成功 content: application/json: schema: $ref: #/components/schemas/CreateOrderResponse 400: description: 參數(shù)錯(cuò)誤或庫(kù)存不足 components: schemas: CreateOrderRequest: type: object required: - userId - items properties: userId: type: string items: type: array items: $ref: #/components/schemas/OrderItem OrderItem: type: object required: - skuId - quantity properties: skuId: type: string quantity: type: integer minimum: 1 CreateOrderResponse: type: object properties: orderId: type: string status: type: string enum: - CREATED - REJECTED - PENDING_PAYMENT接口契約的好處在于模塊之間只依賴(lài)契約不依賴(lài)實(shí)現(xiàn)細(xì)節(jié)。后續(xù)即使要把訂單模塊拆成獨(dú)立服務(wù)HTTP 協(xié)議和數(shù)據(jù)結(jié)構(gòu)都不需要大改。5.4 模塊代碼結(jié)構(gòu)示例DDD 分層模塊化單體的關(guān)鍵是代碼結(jié)構(gòu)必須“物理可見(jiàn)”。如果只是口頭上說(shuō)模塊化代碼還是隨便放那等于沒(méi)有邊界。以下是一個(gè)可參考的 Java 工程結(jié)構(gòu)order-center/ ├── order-application/ # 應(yīng)用層用例編排、事務(wù)邊界 ├── order-domain/ # 領(lǐng)域?qū)佑唵魏诵哪P?、業(yè)務(wù)規(guī)則、領(lǐng)域服務(wù) ├── order-infrastructure/ # 基礎(chǔ)設(shè)施層數(shù)據(jù)庫(kù)、消息、外部 RPC ├── order-interfaces/ # 接口層HTTP Controller、事件消費(fèi)者 ├── payment-application/ ├── payment-domain/ ├── payment-infrastructure/ └── payment-interfaces/這里的關(guān)鍵規(guī)則是依賴(lài)方向interfaces 依賴(lài) applicationapplication 依賴(lài) domaindomain 不依賴(lài)任何外部框架和基礎(chǔ)設(shè)施。如果發(fā)現(xiàn) domain 里出現(xiàn)了 JPA 注解或者 RPC 調(diào)用說(shuō)明模塊邊界已經(jīng)被破壞了。可以在 CI 中加入依賴(lài)分析工具自動(dòng)攔截依賴(lài)反向。5.5 運(yùn)行與驗(yàn)證方式這些架構(gòu)設(shè)計(jì)產(chǎn)物如何驗(yàn)證分三層看。第一層圖能不能渲染、文檔是否放進(jìn)了倉(cāng)庫(kù)。用 PlantUML 渲染plantuml -tsvg order-context.puml如果本機(jī)沒(méi)有命令可以安裝對(duì)應(yīng)插件或者使用在線渲染工具。重點(diǎn)不是渲染工具本身而是讓圖成為倉(cāng)庫(kù)中可維護(hù)的代碼資產(chǎn)。第二層ADR 是否完整、決策是否能被團(tuán)隊(duì)理解。建議組織一次 30 分鐘的架構(gòu)評(píng)審讓不參與編碼的同事也能根據(jù) ADR 復(fù)述出“為什么選模塊化單體”。第三層接口契約是否可用??梢杂?OpenAPI 生成 mock server或者用契約測(cè)試工具校驗(yàn)實(shí)現(xiàn)是否滿足契約。契約測(cè)試的目的是就算訂單模塊還沒(méi)有拆出去接口變化也會(huì)被盡早發(fā)現(xiàn)。6. 如何驗(yàn)證一個(gè)架構(gòu)設(shè)計(jì)是“好”的很多團(tuán)隊(duì)的架構(gòu)評(píng)審最后都變成“各說(shuō)各話”因?yàn)闆](méi)有統(tǒng)一標(biāo)準(zhǔn)。實(shí)際上好架構(gòu)設(shè)計(jì)至少滿足四個(gè)條件可驗(yàn)證的質(zhì)量屬性、清晰的演進(jìn)路徑、團(tuán)隊(duì)能理解并執(zhí)行、決策原因有記錄。下面展開(kāi)講。6.1 看質(zhì)量屬性是否可驗(yàn)證如果設(shè)計(jì)文檔里寫(xiě)“系統(tǒng)要高性能”那等于沒(méi)說(shuō)。要寫(xiě)“下單接口 P99 小于 200ms”“訂單狀態(tài)最終一致消息延遲不超過(guò) 1 分鐘”。只有指標(biāo)明確才能在設(shè)計(jì)階段判斷是否可行也才能在事后驗(yàn)證。6.2 看是否有演進(jìn)路徑架構(gòu)設(shè)計(jì)不是終點(diǎn)而是起點(diǎn)。好的設(shè)計(jì)一定回答了這個(gè)問(wèn)題如果未來(lái)某個(gè)假設(shè)失效怎么演進(jìn)例如 ADR-0001 里寫(xiě)清楚了“當(dāng)訂單模塊流量或團(tuán)隊(duì)規(guī)模達(dá)到預(yù)設(shè)閾值時(shí)可優(yōu)先拆分訂單模塊”這就是演進(jìn)路徑。一個(gè)不能演進(jìn)的設(shè)計(jì)本質(zhì)上是在透支未來(lái)。6.3 看團(tuán)隊(duì)能否理解并執(zhí)行再漂亮的架構(gòu)圖如果團(tuán)隊(duì)沒(méi)人能講清楚模塊邊界和依賴(lài)規(guī)則代碼落地時(shí)一定會(huì)跑偏??梢宰鲆粋€(gè)簡(jiǎn)單的驗(yàn)證隨機(jī)找兩個(gè)開(kāi)發(fā)請(qǐng)他們畫(huà)一遍系統(tǒng)模塊圖和依賴(lài)方向。如果兩個(gè)人畫(huà)得基本一致說(shuō)明設(shè)計(jì)溝通有效如果差異很大問(wèn)題不在開(kāi)發(fā)者在文檔。6.4 看決策是否有記錄評(píng)審時(shí)檢查每個(gè)關(guān)鍵方案選擇是否都有 ADRADR 里有沒(méi)有備選方案和后果如果一個(gè)系統(tǒng)里到處是“技術(shù)人員拍腦袋定的”又沒(méi)有任何記錄架構(gòu)就會(huì)快速腐爛。6.5 架構(gòu)評(píng)審檢查清單示例為了讓評(píng)審落地可以準(zhǔn)備一份 YAML 格式的檢查清單隨架構(gòu)文檔一起提交# 文件路徑docs/architecture-review-checklist.yaml checks: - id: REQ-001 name: 功能范圍與約束 question: 文檔是否列出了功能范圍、硬性約束和非功能指標(biāo) required: true - id: CTX-001 name: 系統(tǒng)上下文 question: 上下文圖是否包含了所有外部角色與依賴(lài)系統(tǒng) required: true - id: ALT-001 name: 候選方案 question: 是否至少評(píng)估了兩個(gè)候選方案并說(shuō)明了各自取舍 required: true - id: ADR-001 name: 決策記錄 question: 每個(gè)關(guān)鍵決策是否有對(duì)應(yīng)ADR且包含備選方案與后果 required: true - id: MOD-001 name: 模塊邊界 question: 模塊清單是否清楚依賴(lài)方向是否繪制并約定 required: true - id: API-001 name: 接口契約 question: 跨模塊接口是否已有契約定義 required: true - id: OBS-001 name: 可觀測(cè)性 question: 日志、指標(biāo)、鏈路追蹤是否在設(shè)計(jì)中被考慮 required: true這份清單不是形式主義而是把架構(gòu)評(píng)審從“主觀感受”變成“核對(duì)項(xiàng)”。評(píng)審不通過(guò)的原因會(huì)非常具體例如“沒(méi)有列出備選方案”“上下文圖缺少支付網(wǎng)關(guān)”而不是“我覺(jué)得這個(gè)設(shè)計(jì)不行”。7. 常見(jiàn)架構(gòu)設(shè)計(jì)誤區(qū)與排查思路在項(xiàng)目里很多架構(gòu)問(wèn)題并不是孤立的而是反復(fù)出現(xiàn)的模式。下面整理成一張表格方便對(duì)照排查。問(wèn)題現(xiàn)象可能原因排查方式解決方案服務(wù)拆了很多線上故障率更高團(tuán)隊(duì)規(guī)模撐不起多服務(wù)運(yùn)維統(tǒng)計(jì)服務(wù)數(shù)量、發(fā)布頻率、人均維護(hù)服務(wù)數(shù)收縮邊界先減少服務(wù)數(shù)或改模塊化單體架構(gòu)文檔和代碼完全對(duì)不上文檔只在設(shè)計(jì)階段維護(hù)后續(xù)無(wú)人更新抽查文檔中的模塊圖與代碼結(jié)構(gòu)是否一致把文檔放進(jìn)代碼倉(cāng)庫(kù)評(píng)審 PR 時(shí)同步更新模塊化單體漸漸退化成大泥球沒(méi)有架構(gòu)測(cè)試約束依賴(lài)方向用依賴(lài)分析工具查看模塊間引用加入 CI 檢查禁止跨模塊直接訪問(wèn)倉(cāng)儲(chǔ)用了很新的技術(shù)棧但沒(méi)人能維護(hù)選型只看了技術(shù)先進(jìn)性沒(méi)評(píng)估團(tuán)隊(duì)能力線上問(wèn)題響應(yīng)時(shí)長(zhǎng)、熟悉該技術(shù)的人數(shù)選型前先做團(tuán)隊(duì)能力盤(pán)點(diǎn)和學(xué)習(xí)成本評(píng)估沒(méi)人知道當(dāng)初為什么這么設(shè)計(jì)缺少 ADR 決策記錄檢查關(guān)鍵節(jié)點(diǎn)是否有 ADR 和評(píng)審記錄補(bǔ)寫(xiě)關(guān)鍵 ADR以后新決策強(qiáng)制記錄每次評(píng)審都在爭(zhēng)論同一層問(wèn)題沒(méi)有約定 C4 視圖層級(jí)看評(píng)審材料是否標(biāo)注了當(dāng)前層評(píng)審前明確討論 Context / Container / Component 哪一層演進(jìn)到一半發(fā)現(xiàn)邊界切錯(cuò)了一開(kāi)始沒(méi)分析業(yè)務(wù)變更頻率和團(tuán)隊(duì)歸屬回顧拆服務(wù)后每次需求改動(dòng)涉及幾個(gè)服務(wù)用事件風(fēng)暴或業(yè)務(wù)能力地圖重新識(shí)別邊界這里最想強(qiáng)調(diào)的是第一行。很多團(tuán)隊(duì)在規(guī)模不夠時(shí)強(qiáng)行微服務(wù)化結(jié)果不是架構(gòu)先進(jìn)而是把問(wèn)題復(fù)雜度從業(yè)務(wù)層轉(zhuǎn)移到了運(yùn)維層。從材料看這種“為了微服務(wù)而微服務(wù)”的情況在中小團(tuán)隊(duì)里其實(shí)非常普遍。排查時(shí)先看數(shù)據(jù)服務(wù)數(shù)量、發(fā)布頻率、人均維護(hù)服務(wù)數(shù)、線上故障恢復(fù)時(shí)長(zhǎng)。如果服務(wù)很多但發(fā)布頻率和恢復(fù)能力都很差問(wèn)題往往不是拆分力度不夠而是拆過(guò)了頭。8. 架構(gòu)設(shè)計(jì)技能的團(tuán)隊(duì)落地與 AI 輔助個(gè)人掌握了架構(gòu)設(shè)計(jì)技能還不夠真正的工程價(jià)值在于把這項(xiàng)技能變成團(tuán)隊(duì)的常規(guī)能力。這里給出幾條落地建議。8.1 模板先行讓文檔產(chǎn)出標(biāo)準(zhǔn)化團(tuán)隊(duì)統(tǒng)一的 ADS架構(gòu)設(shè)計(jì)說(shuō)明書(shū)模板、ADR 模板、評(píng)審清單模板是所有后續(xù)實(shí)踐的基礎(chǔ)。新模塊設(shè)計(jì)、技術(shù)選型、系統(tǒng)拆分都要求按模板產(chǎn)出對(duì)應(yīng)文檔。模板會(huì)讓新人也敢于做架構(gòu)設(shè)計(jì)因?yàn)樗麄冇星逦牧鞒炭梢砸蕾?lài)。8.2 文檔即代碼進(jìn)入評(píng)審流程架構(gòu)文檔不要放在 Wiki 或者共享盤(pán)而是放進(jìn)代碼倉(cāng)庫(kù)的docs/目錄與代碼一起走 MR/PR 評(píng)審。這樣每次改動(dòng)架構(gòu)文檔都有記錄評(píng)審意見(jiàn)可追溯也容易與代碼變更對(duì)照。ADR 建議按編號(hào)遞增存放例如docs/adr/ADR-0001-modular-monolith.md。8.3 用架構(gòu)測(cè)試和契約測(cè)試守護(hù)邊界模塊化單體最大的風(fēng)險(xiǎn)是邊界腐化。可以通過(guò) CI 中的依賴(lài)檢查工具禁止跨模塊直連用契約測(cè)試確保接口實(shí)現(xiàn)不偏離定義。把檢查前置到 CI才能保證架構(gòu)設(shè)計(jì)在代碼層面真正被執(zhí)行。8.4 把架構(gòu)設(shè)計(jì)流程沉淀為 Skill架構(gòu)設(shè)計(jì)經(jīng)驗(yàn)一旦顯式化為流程、提示詞和檢查清單就可以打包成團(tuán)隊(duì)可復(fù)用的“技能資產(chǎn)”。如果你所在團(tuán)隊(duì)正在使用具備 Skill 能力的 AI Agent 輔助開(kāi)發(fā)可以把“先架構(gòu)設(shè)計(jì)再寫(xiě)代碼”的理念做成一個(gè) Skill 定義。下面是一個(gè)通用化的 YAML 示例表達(dá)的是流程控制思路具體平臺(tái)接入方式以你的工具文檔為準(zhǔn)。# 文件路徑skills/architecture-design-skill/skill.yaml name: architecture-design-skill description: 在產(chǎn)出代碼之前先完成架構(gòu)設(shè)計(jì)澄清與約束識(shí)別。 適用于新模塊設(shè)計(jì)、系統(tǒng)拆分、技術(shù)選型評(píng)審等場(chǎng)景。 version: 1.0.0 workflow: - step: clarify_goal_and_constraints prompt: | 請(qǐng)先識(shí)別本次設(shè)計(jì)的目標(biāo)、功能范圍、硬性約束和非功能指標(biāo) 輸出一句話目標(biāo)與約束清單。 - step: identify_stakeholders_and_context prompt: | 列出系統(tǒng)涉及的主要角色與外部系統(tǒng)描述系統(tǒng)上下文邊界。 - step: generate_candidate_designs prompt: | 基于需求生成至少2個(gè)候選方案說(shuō)明每個(gè)方案在質(zhì)量屬性上的取舍。 - step: document_adr prompt: | 使用ADR模板記錄最終決策、備選方案和決策后果。 checklist: - 是否識(shí)別了必須滿足的質(zhì)量屬性 - 是否至少評(píng)估了兩個(gè)候選方案 - 是否說(shuō)明了否決備選方案的原因 - 模塊依賴(lài)方向是否清晰 - 是否包含演進(jìn)路徑或回滾方案這個(gè) Skill 示例的核心價(jià)值是“先澄清再設(shè)計(jì)最后記錄”而不是讓 AI 直接代替人做決策。在架構(gòu)設(shè)計(jì)這件事上AI 更適合扮演“生成候選方案的助手”和“檢查清單的執(zhí)行者”而最終決策、責(zé)任和風(fēng)險(xiǎn)判斷仍然必須由人來(lái)做。尤其是涉及數(shù)據(jù)一致性、生產(chǎn)環(huán)境遷移等高風(fēng)險(xiǎn)決策時(shí)AI 的輸出只能作為參考不能替代評(píng)審。8.5 定期復(fù)盤(pán)架構(gòu)假設(shè)每個(gè) ADR 里都隱含了當(dāng)時(shí)做判斷的假設(shè)而這些假設(shè)可能在未來(lái)失效。建議團(tuán)隊(duì)每季度或每半年做一次“ADR 體檢”檢查哪些決策仍然成立哪些假設(shè)已經(jīng)變化。這一步能讓架構(gòu)設(shè)計(jì)保持活力而不是變成一堆歷史文檔。9. 總結(jié)與后續(xù)學(xué)習(xí)方向架構(gòu)設(shè)計(jì)這項(xiàng)能力真正值得投入的訓(xùn)練不是“看更多架構(gòu)圖”而是在真實(shí)項(xiàng)目里把一次設(shè)計(jì)從口頭討論變成有記錄的文檔、經(jīng)過(guò)評(píng)審并落地執(zhí)行。你可以從很小的事情開(kāi)始挑一個(gè)最近正在設(shè)計(jì)或重構(gòu)的小模塊花一兩個(gè)小時(shí)寫(xiě)一份 ADR記錄背景、備選方案、決策和后果。再對(duì)照評(píng)審清單檢查一遍你會(huì)發(fā)現(xiàn)很多此前沒(méi)有意識(shí)到的盲區(qū)。如果你想繼續(xù)深入學(xué)習(xí)可以從這幾個(gè)方向延伸領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)DDD用來(lái)練模塊邊界識(shí)別C4 Model 練架構(gòu)視圖表達(dá)ATAM 等架構(gòu)權(quán)衡分析方法練質(zhì)量屬性評(píng)估架構(gòu)適應(yīng)度函數(shù)Architecture Fitness Functions練如何用自動(dòng)化手段保護(hù)架構(gòu)規(guī)則。這些方法論都不是孤立的最終都會(huì)回到本文反復(fù)強(qiáng)調(diào)的那句話架構(gòu)設(shè)計(jì)的關(guān)鍵是在不確定條件下做出有記錄、可演進(jìn)、能被團(tuán)隊(duì)執(zhí)行的權(quán)衡決策。先在一個(gè)小項(xiàng)目里把流程跑通比等待“經(jīng)驗(yàn)足夠豐富”更有效。下一份架構(gòu)文檔從一份 ADR 開(kāi)始。