架構(gòu)治理基石:基于規(guī)范 + 模式的工具化
圍繞于 ArchGuard,我們一直在探索適合于大多數(shù)企業(yè)的治理模式。通常來說,對(duì)于應(yīng)用架構(gòu)的治理來說,我們的預(yù)期目標(biāo)是,對(duì)應(yīng)的 架構(gòu)設(shè)計(jì) (廣義上的)能被采納和遵守。如果過程中出現(xiàn)有流程上的問題,導(dǎo)致了架構(gòu)在實(shí)施過程中,架構(gòu)會(huì)不斷偏離預(yù)期的設(shè)計(jì)。那么,我們就會(huì)致力于匹配設(shè)計(jì)相應(yīng)的規(guī)范、規(guī)則和函數(shù),來確保后續(xù)在實(shí)施過程中是能正確的落地。
也因此,在架構(gòu)治理上,我們可以用一些簡(jiǎn)單的元素來進(jìn)行概括。
- 模式。尋找壞的味道,并使用好的設(shè)計(jì)來改進(jìn)它。
- 規(guī)范。一個(gè)關(guān)于架構(gòu)決策的文檔化。
- 規(guī)則 。規(guī)范的工具化與形式化表示
于是乎,在我們的場(chǎng)景下,架構(gòu)治理方案就可以圍繞于三個(gè)要素來構(gòu)建。
模式:壞的味道與好的方案
在我們的行業(yè)里,會(huì)將解決特定問題的解決方案稱之為模式,如設(shè)計(jì)模式、架構(gòu)模式。這些廣為流傳的編程模式,都是好的、最佳的實(shí)踐。但是,就個(gè)人而言,而另外一類,不好的模式其實(shí)也是模式,不過,我們往往把它們稱為有味道(Smell)的,代碼里的是 代碼壞味道 ,架構(gòu)里的便是 架構(gòu)的壞味道 。
在一個(gè)組織里,代碼隨著人員的內(nèi)部流動(dòng)、自定義框架的編碼風(fēng)格、公司級(jí)別的規(guī)范定義,使得整體的代碼模式會(huì)趨向于一致。這種一致性會(huì)受到人員變更帶來短期的影響,些許的高水平 “新人” 可能會(huì)帶給團(tuán)隊(duì)一股新鮮備注;大量的新人的涌入,也會(huì)可能使得原來的好的模式被沖淡。但是呢,不論如何,替換的只是模式本身,而不是模式的存在。而壞味道本身即是與好的模式進(jìn)行比較,即好的實(shí)踐應(yīng)該是怎樣的。
也因此,在治理的第一步就是讓壞味道能浮出來。它可以是通過人為地看項(xiàng)目代碼,進(jìn)而得到一些初步的結(jié)論,并基于結(jié)論構(gòu)建出洞見;也可以是像 ArchGuard 一樣的專家系統(tǒng),可以通過 AST 從語法中分析到壞的味道,并將它們可視化出來。
規(guī)范:架構(gòu)決策的文檔化
規(guī)范是我們?cè)谌粘5拈_發(fā)過程中約定俗成的標(biāo)準(zhǔn),其本質(zhì)是對(duì)于一系列架構(gòu)決策的文檔化。作為架構(gòu)師/開發(fā)者,我們定義所有的 API 應(yīng)該是怎樣的?如何去處理數(shù)據(jù)?如何構(gòu)建質(zhì)量防護(hù)?在另種一個(gè)話題: 輕量級(jí)架構(gòu)決策 里,我們定義的是架構(gòu)決策應(yīng)該編寫出來,以格式化的文檔。
好的規(guī)范的本質(zhì)是 推薦 一系列的 最佳實(shí)踐 ?!澳贻p” 的開發(fā)者往往不能理解諸多實(shí)踐的意義,為什么它應(yīng)該這么做?不這么做會(huì)影響到什么?有時(shí)候,需要經(jīng)驗(yàn)豐富的開發(fā)者告他們,WHY + WHAT + HOW。不過呢,在一些大型 IT 組織的里,人們往往依舊會(huì)采用 “考試” 的方式,用一種簡(jiǎn)單粗暴的方式來確保:對(duì)于什么是好的模式/實(shí)踐認(rèn)知是一切的。
而規(guī)范不論是明文規(guī)定,還是約定俗成,我們都可以發(fā)現(xiàn),在業(yè)務(wù)繁榮或者新的加入的時(shí)候,慢慢都會(huì)被破壞。所以,我們又開始尋找一些能讓規(guī)范有效力的方式。
規(guī)則:規(guī)范的工具化與形式化表示
規(guī)則從某種意義上來說,是一種規(guī)范的工具化手段。其最常見的方式是 Linter,一種基于語法樹/語法結(jié)構(gòu)的規(guī)則化工具。
這種規(guī)則可以是我們?cè)趯W(xué)習(xí)英語時(shí)的語法規(guī)則,它是語言中高度抽象的組合關(guān)系和聚合關(guān)系的約定俗成的語言的規(guī)則,包括組合規(guī)則和聚合規(guī)則。諸如于在英語中,常見的句型可以是:主語-謂語-賓語-賓語補(bǔ)足語(英語四級(jí)沒過,這簡(jiǎn)直是噩夢(mèng))。圍繞于這些規(guī)則,便可以構(gòu)建一系列的自動(dòng)化檢測(cè)工具。
這樣的工具,也可以是我們使用 Java 編寫企業(yè)應(yīng)用時(shí),用的 Checkstyle;又或者是使用 TypeScript 編寫前端應(yīng)用時(shí),用的 ESLint。從這一點(diǎn)上來說,它們就是對(duì)于常見規(guī)則的形式化。
治理:匹配模式,展示問題,規(guī)則化與演進(jìn)
模式、規(guī)范、規(guī)則都依賴于編寫工具的人,他應(yīng)該即是一個(gè)架構(gòu)上的專家,又需要精通 編碼 + 語言 本身。又或者是兩者一起進(jìn)行結(jié)對(duì),才能設(shè)計(jì)一個(gè)如此的系統(tǒng)。
回到編程來治理問題上,從過程上來說,我們治理架構(gòu)問題的方式是:
- 設(shè)計(jì)、尋找對(duì)應(yīng)的規(guī)范(即最佳實(shí)踐)
- 人為識(shí)別代碼中的模式,隨后通過編寫代碼匹配,即規(guī)則。
- 通過可視化 + 分析的方式,展示出代碼中的問題。
- 將規(guī)范規(guī)則化,并配合上度量指標(biāo)
- 構(gòu)建適應(yīng)度函數(shù),指導(dǎo)系統(tǒng)進(jìn)行演進(jìn)。
以 ArchGuard 中的 SQL 規(guī)則檢查為例,如下是代碼中的 SQL(經(jīng)過修改):
override fun getById(systemId: Long): SystemInfo? {
val sql = "select * from system_info where id=:systemId"
return jdbi.withHandle<Long, Nothing> {
it.createQuery(sql)
.bind("systemId", systemId)
.mapTo(SystemInfo::class.java)
.firstOrNull()
}
}
從 SQL 性能等角度來說,這里的 select *? 應(yīng)該是禁止的。但是呢,從識(shí)別的難度來說,它是存在的,我們需要結(jié)合著語法分析的結(jié)果,即 createQuery 的被調(diào)用 + 參數(shù)表中對(duì)應(yīng)值的存在,才能將 SQL 從代碼中解析出來。展開來說,在這個(gè)案例里,因?yàn)橄胫卫淼氖?SQL,所以我們所做的是:
- 尋找通用的 SQL 規(guī)范。
- 結(jié)合人為查閱的方式,從 SQL 規(guī)范中尋找第一個(gè)易于實(shí)現(xiàn)的案例
- 編寫代碼,從語法樹抽取 SQL,和對(duì)應(yīng)的 SQL 規(guī)則
- 將所有的問題展示到一起
從治理的層面來說,最大的難點(diǎn)在于 模式逃逸 —— 即開發(fā)者可能根據(jù)識(shí)別的模式,修改代碼的實(shí)現(xiàn)方式,導(dǎo)致度量無用。不過,這就是另外一個(gè)關(guān)于度量如何改進(jìn)的問題。