Swift 微服務架構師
在以其他方式建構的後端中,保留其結構和慣例,除非使用者要求更改,並僅針對後端缺少的部分套用此技能所定義的形狀。
X 代表目標名稱中的服務(LoansCore)以及類型命名的對象(BorrowBookUseCase、LoanService);<Org> 和 <org> 代表組織名稱。範例為 Bookshelf,一個小型借閱圖書館:
| 套件 | 內容 |
|---|---|
bookshelf-identity | 帳戶、密碼、工作階段;發行每個存取令牌;Valkey 中的重設碼;內部為其他服務提供成員資訊 |
bookshelf-catalog | 書籍:任何人可讀取,管理員可編輯;Loans 服務在內部查詢書籍資訊 |
bookshelf-loans | 成員的借閱記錄,一個租戶表;一個工作流程將逾期借閱標記並發送通知 |
bookshelf-gateway | 公開的 HTTP API,將請求轉發至各服務的 gRPC 契約 |
bookshelf-core | BookshelfAuthentication、BookshelfPersistence、BookshelfTransport、BookshelfTesting |
bookshelf-protos | IdentityProtos、CatalogProtos、LoansProtos |
架構形狀
用戶端 ── HTTPS ──► 邊緣 (TLS 終止) ── HTTP ──► 閘道 (Hummingbird, OpenAPI)
閘道 ── gRPC, mTLS, 呼叫者的 JWT ──► 每個服務的 XService
服務, 工作者 ── gRPC, mTLS ──► 其他服務的 XInternalService
服務, 工作者 ── mTLS ──► Temporal 服務 ──► 其自身的 Postgres 資料庫, Valkey
每個服務和閘道都依賴於 <org>-core、<org>-protos 以及 swift-microservices 函式庫 (堆疊)。
服務的套件結構
<org>-x
├── XCore 領域模型、錯誤、儲存庫協定與命令、連接埠、use case
├── XPostgres 每個資料庫角色一個範圍、儲存庫、陳述式、遷移
├── XGRPC XService 和 XInternalService、Protobuf 轉換、用於 Core 連接埠的 gRPC 用戶端
├── XTemporal 工作流程、活動容器、用於 Core 工作流程連接埠的 Temporal 用戶端
├── XValkey 用於短期存取值之儲存庫
├── X<Provider> 位於 Core 連接埠後方的第三方 API (LoansResend)
└── X 可執行檔:命令、預設配置、遷移、composition root
一個服務僅包含其所需的目標,每個目標僅導入其對應行所列出的內容。硬性規則:
- XCore 不導入任何驅動程式、傳輸層、契約、工作流程引擎、配置或生命週期模組 (Core 可以連結的內容)。
- 適配器目標之間永不互相導入;可執行檔透過 Core 的協定將一個適配器傳遞給另一個。
- 生成的契約類型永遠不離開 XGRPC、閘道的 HTTP 目標以及可執行檔。
- 只有可執行檔讀取配置、建立用戶端並執行
ServiceGroup。 - 沒有服務會讀取另一個服務的資料庫;資料僅能透過契約在服務間傳遞。
職責歸屬
| 關注點 | 擁有者 |
|---|---|
| 業務規則、授權決策、錯誤的含義 | XCore 中的 use case;適配器僅進行轉換和委派 |
| 服務對其他服務資料的了解 | XCore Ports/ 中的模型,位於返回該資料的連接埠旁 |
| Protobuf ↔ 領域,HTTP ↔ protobuf | XGRPC 的 Protobuf/ 轉換;閘道的 Schemas/ |
| 進程間訊息;公開 API | <org>-protos;閘道的 openapi.yaml |
| 呼叫者是誰 | 閘道驗證並轉發令牌;每個服務再次驗證它 |
| 哪些進程可以呼叫內部 RPC | 監聽器層級的 mTLS:環境授權機構簽署的每個進程 |
| 租戶隔離 | 列級安全;<Org>Persistence 將呼叫者綁定到每個交易,且每個受眾的 use case 以其資料庫角色執行 |
| 持久化、延遲或多服務工作 | XTemporal 中的 Temporal 工作流程 |
| 每個服務皆相同所需之物 | <org>-core:身分、租戶綁定、傳輸層讀取器 |
程式碼風格
程式碼應如同它所實作的需求般易讀,以 Apple 的 WWDC 範例程式碼為標竿 (風格)。Use case 和適配器應以下列方式映射錯誤:
do {
… // 工作內容,依照需求陳述的順序
} catch let error as BorrowBookUseCaseError {
throw error
} catch CatalogClientError.unavailable {
logger.warning("借閱失敗:目錄無法使用")
throw .catalogUnavailable
} catch {
logger.error("借閱失敗:未知錯誤", metadata: ["error": "\(String(reflecting: error))"])
throw .unknown
}
- 單一
do區塊包含所有工作直到其return,每個catch結束自己的路徑:不要在do之前宣告任何變數供catch使用。 - 建構,再呼叫:呼叫所取用的值應為具名
let,絕不允許在引數中進行轉換、try或await(建構,再呼叫)。 - Use case、工作流程或活動中不可有輔助方法,也不可有
Result操弄、導向旗標、try?、catch中的巢狀do,或強制解包。 - Swift 6 模式,啟用
ExistentialAny、MemberImportVisibility、InternalImportsByDefault和NonisolatedNonsendingByDefault(Swift 設定);跨服務目標使用package存取。 - 所有跨越邊界的東西都必須是
Sendable;禁止使用Task {}或Task.detached,僅在並行允許之處使用未檢查的並行。
陷阱
- 被排除在
ServiceGroup之外的元件永遠不會執行:未執行的GRPCClient或PostgresClient永遠不會連接,因此其呼叫會等待直到逾時。 - 以資料庫擁有者身分連接的進程會繞過列級安全,因此每個進程應以其角色連接,僅
serve --migrate-database以擁有者身分執行。 - Temporal 僅在執行時檢查
Codable:每個工作流程和活動的負載,以及其擁有的每個型別,都必須是Codable, Sendable(工作流程)。 - 工作流程程式碼會被重播:在工作流程中使用
Date.now、UUID()、亂數、Task.sleep或任何 I/O 都會導致其失效 (確定性)。 - 在自有程式碼中使用普通或限定範圍的
import Foundation會在 Linux 上連結完整的 Foundation;請改用條件式FoundationEssentials導入。
檢查清單
將任務的檢查清單複製到回應中,並在完成時勾選項目;每個提到的 .md 檔案都在 references/ 目錄中。
規劃後端
- [ ] 每個能力及其所擁有的資料;每個能力對應一個服務 (system.md#splitting-a-system-into-services)
- [ ] 為每個服務命名;根據圖形中的每個模組檢查其目標名稱
- [ ] 每個服務:依受眾劃分的 XService RPC、XInternalService RPC 及其呼叫者(無呼叫循環)、表格(租戶或非租戶)、工作流程、短期狀態
- [ ] 每個服務:其目標、命令和資料庫角色
- [ ] <org>-core 的產品、<org>-protos 的契約、閘道的分層路由
- [ ] 建構順序:<org>-core 和 <org>-protos、令牌發行者、每個服務在其呼叫者之前、閘道
- [ ] 根據 system.md#the-plan 撰寫計畫;在任何程式碼之前根據硬性規則進行檢查
添加服務
- [ ] <org>-protos:包含 XService 的 XProtos,以及如果進程需要呼叫則包含 XInternalService
- [ ] 根據 packages.md#packageswift-template 建立 Package <org>-x,僅包含計畫指定的目標
- [ ] XCore:模型、錯誤、儲存庫、連接埠、每個 RPC 對應一個 use case (use-cases.md)
- [ ] XPostgres:角色、表格、列級安全、陳述式、每個角色一個範圍 (persistence.md)
- [ ] XGRPC:基於 use case 的 proto 服務、轉換、用於連接埠的用戶端 (grpc.md)
- [ ] 根據計畫需要添加 XTemporal、XValkey、X<Provider>
- [ ] X:命令、InMemoryProvider 預設值、遷移、Serve 以及包含工作者的 Run (composition.md)
- [ ] 閘道:公開 RPC 的路由 (gateway.md)
- [ ] 每個模組一個測試目標,基於模擬物件 (testing.md)
- [ ] 其 CI 工作流程、Containerfile 和 Dependabot,作為一個服務 (delivery.md)
- [ ] swift build 和 swift test;修正並重複直到兩者皆無警告地通過
添加操作
- [ ] 契約:RPC 隸屬於 XService 或 XInternalService 中的相應受眾
- [ ] XCore:use case 的五個檔案;其範圍僅列出它所觸及的事物 (use-cases.md)
- [ ] XPostgres:其可能執行的角色的範圍採納其範圍協定
- [ ] XGRPC:處理器和輸入的轉換,每個錯誤情況已映射
- [ ] 可執行檔:use case 在其角色的資料庫上運作,並傳遞給服務
- [ ] 閘道:如果公開,則在其層級的路由
- [ ] 測試:use case 的結果和處理器的狀態,基於模擬物件
- [ ] swift build 和 swift test;修正並重複直到兩者皆無警告地通過
添加持久化程序
- [ ] XCore:XWorkflowClient 連接埠;其活動由 use case 執行,並由工作者範圍採納
- [ ] XTemporal:工作流程、其活動、TemporalXWorkflowClient、具名的 ActivityOptions (workflows.md)
- [ ] 啟動它的 use case,在其交易提交後,透過一個確定性 ID
- [ ] 可執行檔:Serve 中的 Temporal 用戶端;工作者群組及其 Run root
- [ ] 測試:工作在時間跳躍伺服器上的工作流程,其活動基於模擬物件
- [ ] swift build 和 swift test;修正並重複直到兩者皆無警告地通過
設置 CI/CD
- [ ] 套件的種類:公開函式庫、系統函式庫或服務 (delivery.md#package-kinds)
- [ ] 其 CI 工作流程,引用 swift-microservices/ci 在某個版本的提交;dependabot.yml;.license_header_template 和 .licenseignore;秘密
- [ ] 函式庫:semver 標籤和 .github/release.yml;服務:其 Containerfile
- [ ] 使用 CI 的旗標執行 swift build 和 swift test;修正並重複直到兩者皆通過
審查後端
- [ ] 執行邊界檢查 (packages.md#checking-the-boundaries)
- [ ] 每個 use case:其五個檔案、typed throws、每個情況一個 catch、無輔助方法、正確的資料庫角色
- [ ] 每個適配器僅進行轉換和委派;每個錯誤情況都已映射
- [ ] 每個 root:僅在其中讀取配置,ServiceGroup 中的每個元件按依賴順序排列
- [ ] Swift 設定、FoundationEssentials 導入、package 存取、命名 (conventions.md)
- [ ] 發現項依嚴重性排序:封鎖性(租戶隔離、授權、憑證、建構)、重大(形狀)、次要(命名、佈局、日誌記錄),每個發現項引用檔案和行號或空的 grep 結果,附帶規則和修正方法;然後記錄使用者的決定。僅在要求時更改程式碼
- [ ] swift build 和 swift test;報告每個結果,或無法運行的原因
參考資料
根據當前處理的部分閱讀對應參考資料:
| 參考資料 | 內容 |
|---|---|
| system.md | 拆分為服務、契約、服務間通訊方式、共享套件、計畫 |
| packages.md | 目標及其導入、Package.swift、Swift 設定、特性和 Foundation、佈局、邊界檢查 |
| stack.md | 函式庫、每個目標連結的目標、Foundation 連結、選擇版本 |
| use-cases.md | 模型、錯誤、儲存庫、連接埠、use case 的五個檔案及其主體、交易、授權 |
| persistence.md | 每個資料庫角色的範圍、列級安全、陳述式、遷移、Valkey |
| grpc.md | 契約、proto 服務、處理器、轉換、分頁、狀態碼、用戶端、傳輸層 |
| gateway.md | OpenAPI 型別、請求上下文和層級、控制器、webhook、錯誤、上游用戶端 |
| workflows.md | 啟動工作流程、負載、確定性、活動和選項、版本、工作者 |
| composition.md | 命令、composition root、配置、日誌記錄、ServiceGroup |
| security.md | 信任模型、mTLS、使用者令牌、授權層、秘密、webhook |
| style.md | 程式碼如何表達:錯誤、並行、導入、Foundation、型別、格式化 |
| testing.md | 測試目標、每個目標證明的內容、模擬物件 |
| delivery.md | 每個套件種類的 CI 工作流程、版本、部署、Containerfile、Dependabot |
| conventions.md | 命名套件、目標、型別、檔案、鍵或資料夾 |
相關技能
對於架構形狀內部的程式碼,請在已安裝時載入這些技能;若此技能陳述了形狀,請遵循它:
- 參與者、任務群組、取消、
Sendable診斷、Swift 6 遷移:swift-concurrency - 引數標籤、方法名稱、文件註解:
swift-api-design-guidelines - Use case、處理器和工作流程的測試:
swift-testing-expert