実際のアプリケーションでは、複数のサービスが連携して完全なシステムを形成することが 一般的です。Goaは単一のプロジェクト内で複数のサービスを設計および実装することを 容易にします。このガイドでは、複数のサービスを効果的に作成および管理するプロセスを 説明します。
Goaにおけるサービスは、特定の機能を提供する関連エンドポイントの論理的なグループを 表します。単純なアプリケーションでは1つのサービスで十分かもしれませんが、 大規模なアプリケーションでは機能を複数のサービスに分割することで恩恵を受けることが 多いです。このアプローチにより、APIエンドポイントのより良い組織化、関心事の明確な 分離、より容易な保守とテスト、独立したデプロイ機能、および細かいセキュリティ 制御が可能になります。
マルチサービスシステムを設計する際、サービスは通常、フロントサービスと バックサービスの2つのカテゴリーに分類されます。これらのパターンを理解することで、 スケーラブルで保守可能なアーキテクチャを設計するのに役立ちます。
Goaは、サービスの設計と生成されたコードをどのように組織化するかについて柔軟性を 提供します。主な2つのアプローチ、統合設計と独立設計を見てみましょう。
統合アプローチは、サービス固有の実装を維持しながら、すべてのサービスを単一の設計 階層の下にまとめます。以下がその仕組みです:
// design/design.go - トップレベルの設計ファイル
package design
import (
_ "myapi/services/users/design" // 各サービスは独自の設計を持つ
_ "myapi/services/products/design"
. "goa.design/goa/v3/dsl"
)
var _ = API("myapi", func() {
Title("My API")
Description("マルチサービスAPIの例")
})
各サービスは、全体のAPIに貢献する独自の設計ファイルを維持します:
// services/users/design/design.go - サービス固有の設計
package design
import (
. "goa.design/goa/v3/dsl"
"myapi/design/types"
)
var _ = Service("users", func() {
// サービス固有の設計
})
このアプローチはコード生成と型の共有を一元化します:
gen/
ディレクトリに配置されるgoa gen
コマンドですべてのサービスコードを生成独立アプローチは、各サービスを独立したユニットとして扱います:
// services/users/design/design.go - 独立したサービス設計
package design
import (
. "goa.design/goa/v3/dsl"
)
var _ = API("users", func() {
Title("ユーザーサービス")
Description("ユーザー管理API")
})
var _ = Service("users", func() {
// サービス固有の設計
})
このアプローチはサービスの独立性を最大化します:
gen/
ディレクトリを維持トランスポートプロトコルの選択は、サービス間の相互作用に大きな影響を与えます。 各アプローチの利点を見てみましょう:
gRPCは以下を通じて内部サービス通信に優れています:
HTTPは以下を提供することで外部向けサービスに適しています:
適切に組織化されたリポジトリは、チームがコードベースをナビゲートおよび 維持するのに役立ちます。以下は推奨される構造です:
myapi/
├── README.md # システム概要とセットアップガイド
├── design/ # 共有設計要素
│ ├── design.go # 統合アプローチのトップレベル設計
│ └── types/ # 共有型定義
├── gen/ # 生成されたコード(統合アプローチ)
│ ├── http/ # HTTPトランスポート層コード
│ ├── grpc/ # gRPCトランスポート層コード
│ └── types/ # 生成された共有型
├── scripts/ # 開発とデプロイメントスクリプト
└── services/ # サービス実装
├── users/ # 例:ユーザーサービス
│ ├── cmd/ # サービス実行可能ファイル
│ ├── design/ # サービス固有の設計
│ ├── gen/ # 生成されたコード(独立アプローチ)
│ ├── handlers/ # ビジネスロジック
│ └── README.md # サービスドキュメント
└── products/ # 例:製品サービス
└── ...
サービス間の相互作用を設計する際は、以下の一般的なパターンを考慮してください:
サービスは通常、以下の2つのカテゴリーに分類されます:
フロントサービス:外部向けサービスで:
バックサービス:内部サービスで:
一般的なアーキテクチャパターンは、プラットフォームの機能を外部クライアントに 公開する少数のフロントサービス(時には1つだけ)と、実際のビジネスロジックを 処理する複数のバックサービスを持つことです。
scripts/
ディレクトリは、一般的な開発とデプロイメントタスクの自動化を提供します。
これらのスクリプトは統合アプローチと独立アプローチの両方に適応し、選択した
アーキテクチャに関係なくサービスを簡単に管理できるようにします。
コア開発スクリプトは、コード生成、ビルド、およびテストを処理します:
# scripts/gen.sh - コード生成スクリプト
#!/bin/bash
if [ "$1" == "" ]; then
# 統合アプローチ:すべてのサービスを生成
goa gen myapi/design
else
# 独立アプローチ:特定のサービスを生成
cd services/$1 && goa gen myapi/services/$1/design
fi
# scripts/build.sh - ビルドスクリプト
#!/bin/bash
if [ "$1" == "" ]; then
# すべてのサービスをビルド
for service in services/*/; do
service=${service%*/}
echo "Building ${service##*/}..."
go build -o bin/${service##*/} ./$service/cmd/${service##*/}
done
else
# 特定のサービスをビルド
go build -o bin/$1 ./services/$1/cmd/$1
fi
# scripts/test.sh - テストランナー
#!/bin/bash
if [ "$1" == "" ]; then
# すべてのサービスと共有コードをテスト
go test ./... -v
else
# 特定のサービスをテスト
go test ./services/$1/... -v
fi
デプロイメントスクリプトは、サービスの実行とコンテナデプロイメントを処理します:
# scripts/run.sh - ローカルサービスランナー
#!/bin/bash
if [ "$1" != "" ]; then
# 特定のサービスを実行
./bin/$1
else
# 利用可能なサービスを一覧表示
echo "利用可能なサービス:"
ls bin/
fi
# scripts/deploy.sh - Kubernetesデプロイメント
#!/bin/bash
if [ "$1" != "" ]; then
deploy_service() {
echo "$1をデプロイ中..."