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 ドメインモデル、エラー、リポジトリプロトコルとコマンド、ポート、ユースケース
├── XPostgres データベースロールごとのスコープ、リポジトリ、ステートメント、マイグレーション
├── XGRPC XService と XInternalService、Protobuf変換、Coreのポート向けgRPCクライアント
├── XTemporal ワークフロー、アクティビティコンテナ、Coreのワークフローポート向けTemporalクライアント
├── XValkey 短期間の値のためのリポジトリ
├── X<Provider> Coreポートの背後にあるサードパーティAPI(LoansResend)
└── X 実行ファイル: コマンド、設定デフォルト、マイグレーション、コンポジションルート
サービスは必要なターゲットのみを持ち、各ターゲットはその行にリストされたものだけをインポートします。厳格なルール:
- XCoreはドライバ、トランスポート、コントラクト、ワークフローエンジン、設定、ライフサイクルモジュールをインポートしません(Coreがリンクしてもよいものを参照)。
- アダプターターゲットは互いにインポートしません。実行ファイルがCoreのプロトコルを介して一方を他方に渡します。
- 生成されたコントラクト型は、XGRPC、ゲートウェイのHTTPターゲット、および実行ファイルの外に出ません。
- 実行ファイルのみが設定を読み取り、クライアントを構築し、
ServiceGroupを実行します。 - どのサービスも他のサービスのデータベースを読み取りません。データはコントラクトを介してのみサービス間を移動します。
誰が何を所有するか
| 関心事 | 所有者 |
|---|---|
| ビジネスルール、認可決定、エラーの意味 | XCoreのユースケース; アダプターは変換と委譲のみを行う |
| サービスが別のサービスのデータについて知っていること | XCore Ports/ のモデル、それを返すポートの隣 |
| Protobuf ↔ ドメイン、HTTP ↔ Protobuf | XGRPCの Protobuf/ 変換; ゲートウェイの Schemas/ |
| プロセス間のメッセージ; 公開API | <org>-protos; ゲートウェイの openapi.yaml |
| 呼び出し元の識別 | ゲートウェイがトークンを検証して転送; 各サービスが再度検証 |
| 内部RPCを呼び出せるプロセス | リスナーのmTLS: 環境の認証局が署名するすべてのプロセス |
| テナント分離 | 行レベルセキュリティ; <Org>Persistenceが呼び出し元を各トランザクションにバインドし、各オーディエンスのユースケースはそのデータベースロールとして実行 |
| 耐久性のある、遅延された、またはマルチサービスの作業 | XTemporalのTemporalワークフロー |
| すべてのサービスが同一に必要とするもの | <org>-core: アイデンティティ、テナントバインディング、トランスポートリーダー |
コードスタイル
コードは実装する要件のように読み取れ、AppleのWWDCサンプルコードを基準とします(スタイル)。ユースケースとアダプターは次のようにエラーをマッピングします:
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
}
- 1つの
doがreturnまで作業を保持し、各catchは自身のパスを終了します:catchが埋めるためにdoの前に何も宣言してはいけません。 - 構築してから呼び出す: 呼び出しが受け取る値は名前付きの
letであり、引数に変換、try、awaitを決して含めません(構築してから呼び出す)。 - ユースケース、ワークフロー、アクティビティにヘルパーメソッドはなく、
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: XProtos(XService付き)、およびプロセスが内部から呼び出す場合はXInternalService
- [ ] packages.md#packageswift-templateからパッケージ<org>-xを作成、計画が名前を挙げたターゲットのみで
- [ ] XCore: モデル、エラー、リポジトリ、ポート、RPCごとのユースケース(use-cases.md)
- [ ] XPostgres: ロール、テーブル、行レベルセキュリティ、ステートメント、ロールごとのスコープ(persistence.md)
- [ ] XGRPC: ユースケース上の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; 修正して繰り返し、両者が警告なしでパスするまで
操作を追加する
- [ ] コントラクト: XServiceのオーディエンス下のRPC、またはXInternalService内
- [ ] XCore: ユースケースの5つのファイル; そのスコープは触れるものだけをリストする(use-cases.md)
- [ ] XPostgres: 実行される可能性のあるロールのスコープがそのスコーププロトコルを採用
- [ ] XGRPC: ハンドラーと入力の変換、すべてのエラーケースがマッピングされている
- [ ] 実行ファイル: ユースケースをそのロールのデータベース上で、サービスに渡す
- [ ] ゲートウェイ: 階層内のルート(公開の場合)
- [ ] テスト: ユースケースの結果とハンドラーのステータス、モックを使って
- [ ] swift build と swift test; 修正して繰り返し、両者が警告なしでパスするまで
耐久性のあるプロセスを追加する
- [ ] XCore: XWorkflowClientポート; アクティビティが実行するユースケース(ワーカースコープによって採用)
- [ ] XTemporal: ワークフロー、そのアクティビティ、TemporalXWorkflowClient、名前付きActivityOptions(workflows.md)
- [ ] それを開始するユースケース(トランザクションコミット後、決定論的なIDで)
- [ ] 実行ファイル: Serve内のTemporalクライアント; ワーカーグループとそのRunルート
- [ ] テスト: タイムスキップサーバー上のワークフロー、モックを使ったアクティビティ
- [ ] 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)
- [ ] 各ユースケース: 5つのファイル、型付きthrows、ケースごとに1つのcatch、ヘルパーなし、正しいデータベースロール
- [ ] 各アダプターは変換と委譲のみ; すべてのエラーケースがマッピングされている
- [ ] 各ルート: 設定はそこでのみ読み取られる、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 | モデル、エラー、リポジトリ、ポート、ユースケースの5ファイルと本文、トランザクション、認可 |
| persistence.md | データベースロールごとのスコープ、行レベルセキュリティ、ステートメント、マイグレーション、Valkey |
| grpc.md | コントラクト、protoサービス、ハンドラー、変換、ページング、ステータスコード、クライアント、トランスポート |
| gateway.md | OpenAPI型、リクエストコンテキストと階層、コントローラー、ウェブフック、エラー、アップストリームクライアント |
| workflows.md | ワークフローの開始、ペイロード、決定論、アクティビティとオプション、バージョン、ワーカー |
| composition.md | コマンド、コンポジションルート、設定、ロギング、ServiceGroup |
| security.md | トラストモデル、mTLS、ユーザートークン、認可レイヤー、シークレット、ウェブフック |
| style.md | コードの読みやすさ: エラー、並行性、インポート、Foundation、型、フォーマット |
| testing.md | テストターゲット、各テストの証明内容、モック |
| delivery.md | パッケージ種類ごとのCIワークフロー、リリース、デプロイ、Containerfile、Dependabot |
| conventions.md | パッケージ、ターゲット、型、ファイル、キー、フォルダの命名 |
関連スキル
形状内のコードについては、これらがインストールされている場合にロードしてください。このスキルが形状を指定する場合、それに従ってください:
- アクター、タスクグループ、キャンセレーション、
Sendable診断、Swift 6移行:swift-concurrency - 引数ラベル、メソッド名、ドキュメンテーションコメント:
swift-api-design-guidelines - ユースケース、ハンドラー、ワークフローのテスト:
swift-testing-expert