# 1 \~ 5

## ch1 \~ ch5

ch.1 微服務 ch.2 演化式架構師 ch.3 如何建模服務 ch.4 集成 ch.5 分解單塊系統

能否修改&部署一個微服務，並且不影響其他服務? 不要嘗試讓所有服務都使用一種標準化的技術

架構安全性 我們的服務應該要很好的處理下游的錯誤請求

正常 & 被正確處理的 request 錯誤 & 服務有識別是錯誤，所以什麼也沒做 被訪問的服務當機，無法判斷請求是否正常

願景:充分溝通的技術願景，此願景可以滿足客戶及組織 同理心:對客戶和同事有同理心 合作:盡量多溝通，從而更好的對願景作定義、修訂、執行 適應性:在客戶、組織需要時，調整技術願景 自治性:在標準化、團隊自治之間找到平衡點 治理:確保系統按照技術願景實現

如何建構微服務

鬆耦合 盡可能地減少需要知道其他服務的事情

高內聚 把相關的行為聚集在一起

限界上下文 每個領域都包含多個限界上下文，每個限界上下文的模型可以分成兩個部分，一部分不需要與外部溝通，一部分則需要。 每個限界上下文都有明確的接口，該接口決定了它會曝露那些模型給其他上下文。

限界上下文 是找尋接縫的一個重要的工具 通過微服務將這些邊界匹配

`Eric Evans <領域驅動設計>` `Vaughn Vernon <實現領域驅動設計>`

針對請求/響應方式 PRC Remote Procedure Call REST Repersentational State Transfer

PRC 的核心想法 : 隱藏遠程調用 其中有一些依賴接口定義(SOAP、Thrift、protocol buffers) 有些RPC實現 與 特定的網路協議 綁定 (SOAP 名義上使用HTTP)

※不同的協議可以提供不同的特性，根據使用場景決定 TCP：能夠保證送達 UDP：雖然不能保證送達，但協議開銷較小

PRC的主要賣點之一：易於使用、快速啟動。 但其存在一些問題，一開始不明顯，但帶來的代價遠遠大於一開始的方便。

著名的錯誤觀點 "網路是可靠的" 要假設隨時有惡意攻擊者隨時有可能對網路進行破壞、因此網路出錯模式也不只一種。服務端可能會返回一個錯誤信息，或是請求本身就有問題。

如何區分各種不同的錯誤、故障方式? 區分之後如何處理?

REST 受Web啟發而產生的一種架構風格 REST 配合 HTTP 的 GET POST PUT DELETE

為了避免暴露太多東西，有一個有效的模式，是先設計外部接口，等接口穩定後，再實現內部的數據持久化(?)。副作用是推遲數據儲存的部分。在這之間，先將資料儲存到硬碟的文件上。這種做法，可以保證接口是根據使用者的需求產生的。

ATOM 一個符合 REST 規範的協議，提供資源聚合(feed)的發布服務。

響應式擴展 Reactive extensions , Rx 可以把多個調用的結果組裝起來，在此基礎上做操作。

以往： 我們會獲取數據，並對於數據做操作。 現在： 對操作的結果進行觀察(?)，結果會根據數據的改變進行更新。

很多Rx的實作很適合應用在分布式系統，因為調用的細節被隱藏，所以事情更容易處理。也可以直接觀察調用的結果，不用在乎它是同步 or 非同步。漂亮之處在於，可以把多個不同的調用結果組合起來，作相對應的處理。

服務及狀態機 不管用PRC 還是 REST，服務及狀態機的概念都很強大。 把關鍵領域的生命週期 顯示、視覺化 非常有用，我們不但可以在唯一的一個地方處理狀態衝突，還可以在狀態變化的基礎上封裝一些行為。

微服務世界中的DRY DRY, Don't Repeat Yourself 避免重複代碼，會比較好維護。

我們會希望微服務和 client 間避免耦合，否則微服務的任何小改動都可能導致 client 方也要改動，而共享代碼就有可能導致這種耦合。

建議： 微服務內部不要違反DRY 跨服務之間可以適當違反DRY

服務之間引入大量耦合，會比DRY造成更糟糕的問題。

客戶端庫，給Client 用的SDK (?) 建議開發 服務API 和 客戶端API 的不要是同一批人，因為同一批的話，會容易把服務端的邏輯洩漏到客戶端。

洩漏到客戶端的邏輯越多，內聚性就越差，當我們在修復服務端的問題時，也需要對Client進行修改。這樣做也會限制技術的選擇，尤其是當我們強制Client 必須使用客戶端庫的時候。

Netflix 非常強調客戶端的使用，但目的不僅僅只是為了避免代碼重複。事實上，Netflix 有另外一個同等重要的原因，保證系統的可靠性&可伸縮性。Netflix 的客戶端會處理類似服務發現、故障模式、日志等方面的工作。 不過後來還是有員工表示這樣做，產生了一定程度的耦合問題。

※如果要使用客戶端，一定要保證只處理底層傳輸，不要把與目標服務相關的邏輯放到客戶端中。並且想清楚是否允許別人使用不同的技術去呼叫API。並且確保由Client 來負責何時進行 Client的升級，這樣才能保證每個服務可以獨立於其他服務發布。

引用訪問 如何傳遞領域實體的相關資訊 是一個值得討論的話題。 微服務應該包含核心領域全生命週期的相關操作。

如果想要做跟客戶有關的改動，就必須向客戶服務發起請求。

有時發起請求，得到了一個客戶資源的本地副本。但可能在發送請求之後，有人對該資源進行了修改，持有越久，其內容失效的可能性就越高。

有時候使用本地副本是OK的，但有些場景你需要知道該副本是否已經失效，所以你要確保在持有本地副本的同時，也同時持有一個指向原始資源的引用，這樣在有需要的時候，可以進行更新。

有一種作法是 傳遞取得該資源的URI，當輪到該資源的時候，再取得該資源，並做相對應的動作。

還可以延伸 有效性時限，就可以做相對應的緩存。

版本控制

盡可能地推遲、減少破壞性修改。

Postel法則：寬進嚴出，對於自己出去的東西要嚴格，對於接收的東西要寬容。

語義化的版本管理 現在的版本，可以與那些版本相容，客戶端的版本。 如果一個Client 可以透過查看版本號，就可以知道能否和其他相容，那就太棒惹。

不同的接口共存 當我們做了所有的事情去避免接口的修改(但還是無法避免)，那下一步就是避免其影響。 我們不想強迫Client 一起升級，因為希望微服務可以獨立發布。

解法1： 有一種方式，就是新、舊接口同時並存，所以在發布一個破壞性修改時，可以部屬一個同時包含新舊接口的版本。

當Client 都轉移到新接口，就可以把舊接口的相關代碼移除。

解法2： 擴展/收縮 的實例，允許我們對破壞性修改進行平滑的過度。

在內部進行轉換，讓舊接口的請求，去訪問新接口，使用這種方式，以後要刪除那些代碼也會比較清楚。

用戶介面

誰來創建用戶介面? 維護服務的往往不是服務的使用者，如果 UI 是另外一個團隊創建的，這樣要做修改時，需要多個團隊參與。

如果畫面的每個區塊，都要去 call 各自的API，可能會很吃流量，對於mobile使用者不利。

使用API入口 (gateway) 可以很好的緩解這個狀況。

建議： 為前端服務的後端, BFF, Backends For Frontends

這個後端，只為一個應用 or 一個用戶介面(desktop or mobile)做事 雖然嵌在服務端，但它是UI的組成部分。 而API認證、授權 的檢查，應該會在 UI 和 BFF 之間。 使用這種方式的風險與其他聚合層相同，就是不要包含業務邏輯。業務邏輯應該在服務中，這一層只負責實現與某種特定的UI相關的邏輯。

與第三方軟體集成

在第三方軟體的外面，再加設一層框架、介面，而這層的接口是我們設計的標準呼叫方式(比如 HTTP REST)，而這一層則去因應各種第三方軟體，去做適應對方的呼叫，傳遞資料。

第四章結論

無論如何，避免直接去呼叫資料庫

理解 REST 和 RPC 的取捨，但總是使用 REST 作為 request/response

相比起編排，優先選擇協同

避免破壞性修改，理解 Postel 法則，使用容錯性讀取器

將用戶介面視為一個組合層

共享靜態數據的幾種方式 (國家代碼、主流瀏覽器、手機品牌..)

第一種： 為每個package 複製一份該表的內容，也就是說未來每個服務都會有一份該表的內容，當然 這會導致一個潛在的一致性問題。

第二種： 把這些共享的靜態數據放進代碼，比如放在屬性文件 or Enum 裡面。 數據一致性的問題依然存在，但是從經驗上看，修改配置文件，比修改線上資料庫來得簡單。

第三種： 極端一點，將這些靜態數據抽出來，放在一個單獨的服務中。 在某些場景下，數據量 & 重要性 值得我們這樣做，但如果只是一些少量、簡單的，就不必了。

共享動態數據 有時領域的概念不是在服務，而是在資料庫 這時新增加一個服務，去專門存取這個領域

共享table、重構資料庫 A服務和 B服務都會對 C表做操作 為了劃分清楚，拆成兩個表

建議： 當我們有一個單塊系統，我們拆成了兩個服務，資料庫的結構也有所異動，不建議在一次發布中，同時做這兩件事情。 會建議先把資料庫的異動先做發布，暫時先不對服務進行分離。 因為表的結構分離之後，可能會造成連線次數變多、破壞事務的完整性，對於應用程式造成影響。只先做一半，可以讓我先觀察狀況，看要繼續做下去，還是退回去原本的。

事務邊界

類似Transact 的概念 在單塊的時候，可以用Transact 去達到完整的一致性。 但是分離資料庫之後，這種好處就沒有了。

最終的一致性 (11章 scaling) 再試一次，將沒做成功的事情，放進佇列，之後再做一次。 對於某些操作來說，這樣是合理的(前提是重試可以成功解決問題)

終止整個操作 需要將系統重置到某種一致的狀態。假如一個失敗，要將其它的也還原、退回，則發起 補償事務 去抵銷之前的操作。 要用程式自動控制呢? 還是後台增加介面去做維護呢?

分布式事務、事務管理器 分布式事務 常用的算法是 兩階段提交，投票階段、提交操作。 但這個會使參與者暫停、導致系統中斷，如果事務管理器當機，就爆炸了。 如果真的要使用，可以用一些現成的API，例如JAVA 的 事務API，可以減少錯誤。

如果遇到的場景確實需要保持一致性，那麼盡量避免放在不同地方，如果實在不行，那麼要避免從純技術的角度去考慮。可以用不同的角度，讓他們概念上是同類型的事務，而去整合在一起。

報告

為了避免對主系統的效能造成影響，會把報告系統去指向副本資料庫，從中讀取數據。 缺點是，主系統跟報告系統會因為 table 而產生耦合。

一種方式：報告系統去讀取各個服務的API，去組合出自己需要的資料，但有很大的可能，是服務並沒有提供完全切合的API，那就要呼叫各種不同的API，再從中挑選、組合。這要效率會比較差。

一種是：主動把數據丟到報告系統。寫一支AP，專門去各服務將需要的資料丟到報告資料庫。(少數可以直接去耦合資料庫的例外。因為它讓報告變得很簡單，可以抵銷耦合帶來的缺點) 雖然 table 的耦合依然存在，但我們可以把它當作是一種公共API 這種比較穩定的東西。另外，我們可以用 view 去創建一個給指定報告所使用的結構。

事件數據導出 因為每個微服務 可以在自己管理的實體 狀態改變時，發送一些事件。 對於這些暴露事件聚合(feed) 的微服務來說，可以編寫自己的事件訂閱器，把數據丟到報告的資料庫去。

使用這種方式，可以將微服務和底層資料庫的耦合消除。我們只要綁定服務所發送的事件即可，而這些事件本來就是暴露給Client的。

因為是在事件發生時就給報告系統發送數據，所以資料可以更快的更新到資料庫。 如果我們還有紀錄那些事件被處理過，可以讓我們只對新事件進行處理，這意味著 Insert 會更有效率。

因為 事件數據導出 的方式與服務的內部耦合很小，所以可以將這個部分交給另外一個獨立的團隊來維護。

缺點： 所有的信息都用事件來廣播，所以數據量大時，不容易在資料庫的層級做擴展。 但不論如何，如果已經有適合的事件，還是建議用這種方式，它帶來的低耦合 & 數據時效性，是更好的。

CRC , Class-Responsibility Collaboration 是一種收集並整理卡片的開發方式。

一個CRC卡片，由類別名稱、職責、協作者。

類別名稱：一組相似的對象，人 事 地 物 都可以\~ 名稱盡量簡單，通常是一個名詞或一個名詞詞句。

職責：明確此類別應該知道或該做的事情 類可以對自己所知道的東西改值，而不能對其他類改值。

協作者：需要的信息、需要的動作。(需要相互作用的其他類。)

## 如何構建一個CRC模型呢? 只要循環以下步驟

### 找出類

進行分析任務的基礎工作。可分為實體類(Entity Class) 和 UI類(User Interface 介面)。 有時候某個角色，很難和實體類別對應時，也會用卡片去代表角色。

### 找出職責

除了應該問自己一個類應該做什麼，還應該問自己需要保留那些信息。 也經常會標記出與其他類協作的職責。

### 界定協作者

類經常會在執行其職責時，發現沒有足夠的信息支持。 因此它通常會配合(主導) 其他類 以便能完成工作。 協作應該是以下兩者之一：**一個信息請求** or **一個任務執行請求**。

### 移動卡片

為了讓所有人理解這個系統，所有的卡片應該使用容易理解的方式去做擺放，例如盡量把有協作關係的卡片放在一起，彼此沒有協作關係的盡量遠離。

參考資料:([https://blog.csdn.net/jgw2008/article/details/52512276](https://blog.csdn.net/jgw2008/article/details/52512276))
