AWSってお値段たかいよね他のサービスに載せ替えできないの?といわれたときに
注意:この記事はAIが作成しています
参照元の存在、参考元のリンクの信頼度、事実の歪曲がないかをAIによりセルフチェックしています
課題:クラウドに縛られるコード
FaaSアプリケーション開発でよくある問題は、最初からAWS Lambda専用、Azure Functions専用でコードを書いてしまうことです。これだと後でクラウドを変えたくなった時に大変です。
解決策:ソースコードとハンドラーを分離
基本的な考え方は単純です:
- ビジネスロジック:クラウドに依存しない純粋なコード
- ハンドラー:各クラウド専用の薄いラッパー
- デプロイ時に組み合わせる
実装の考え方
1. ビジネスロジックは純粋関数で
// core/userService.ts(どのクラウドでも同じ)
export function createUser(userData: any) {
// バリデーション
if (!userData.email) {
return { error: 'Email required', status: 400 };
}
// ビジネスロジック
const user = processUser(userData);
return { user, status: 201 };
}
2. ハンドラーは薄いラッパーに
AWS Lambda用
export const handler = (event) => {
const result = createUser(JSON.parse(event.body));
return { statusCode: result.status, body: JSON.stringify(result) };
};
Azure Functions用
export default function(context, req) {
const result = createUser(req.body);
context.res = { status: result.status, body: result };
};
3. プロジェクト構造
src/
├── core/ # 共通ビジネスロジック
│ └── userService.ts
├── config/ # 関数マッピング設定
│ └── functions.ts
├── handlers/ # 各クラウド用ハンドラー
│ ├── aws.ts
│ ├── azure.ts
│ └── gcp.ts
└── tests/ # 共通テスト
または、各関数を個別のハンドラーファイルにする方法もあります:
src/
├── core/ # 共通ビジネスロジック
│ └── userService.ts
├── handlers/
│ ├── aws/
│ │ ├── createUser.ts # createUser専用ハンドラー
│ │ └── updateUser.ts # updateUser専用ハンドラー
│ └── azure/
│ ├── createUser.ts
│ └── updateUser.ts
└── tests/
デプロイ戦略
ビルド時にハンドラーを選択
package.jsonスクリプト例
{
"scripts": {
"build:aws": "webpack --config webpack.aws.js",
"build:azure": "webpack --config webpack.azure.js",
"build:gcp": "webpack --config webpack.gcp.js",
"deploy:aws": "npm run build:aws && serverless deploy",
"deploy:azure": "npm run build:azure && func azure functionapp publish"
}
}
ローカル開発の統一
開発時は普通のWeb APIとして実行
// local/server.ts
import express from 'express';
import { createUser } from '../core/userService';
const app = express();
app.post('/users', (req, res) => {
const result = createUser(req.body);
res.status(result.status).json(result);
});
これで開発時は npm start で普通にテストできます。
メリット
FaaS以外のサービスでも応用可能
この考え方はFaaSに限らず、他のクラウドサービスでも活用できます:
- データベース:SQL文を共通化し、接続部分だけをクラウド固有に(MySQL、PostgreSQL、Cloud SQLなど)
- ストレージ:ファイル操作ロジックを共通化し、APIアダプターを分離(S3、Blob Storage、Cloud Storageなど)
- メッセージキュー:メッセージ処理ロジックを共通化し、キュー接続部分を分離(SQS、Service Bus、Pub/Subなど)
- コンテナ:Dockerイメージは同じで、オーケストレーション設定だけを変更(ECS、AKS、GKEなど)
まとめ
要点は3つだけ:
- ビジネスロジックはクラウドに依存させない
- ハンドラーは各クラウド専用の薄いラッパーにする
- デプロイ時に適切なハンドラーと組み合わせる
この設計により、コードの大部分を変更することなく、いつでも好きなクラウドサービスに乗り換えることができます。最初からこの構造で作っておけば、後で「AWSが高くなったからGCPに移りたい」となった時も安心です。
参考リンク
- The Twelve-Factor App - クラウドネイティブアプリケーションの設計原則
- Clean Architecture - 依存関係の整理方法
- Serverless Framework - マルチクラウド対応デプロイツール
- AWS Lambda Best Practices
- Azure Functions Best Practices