システムアーキテクチャ設計書¶
システム概要¶
リツビ BtoB ECサイトの技術スタック全体とアーキテクチャ設計をまとめた文書です。
ローカル開発環境アーキテクチャ¶
開発環境の構成¶
ローカル開発環境では、Vendureサーバーはホストマシンで直接実行され、依存サービス(PostgreSQL、Redis等)のみがDockerコンテナで実行されます。これはVendure開発における標準的な構成です。
アーキテクチャ図¶
┌─────────────────────────────────────────────────────────────┐
│ ホストマシン (開発環境) │
│ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ Vendure Server (Node.js) - ホストで直接実行 │ │
│ │ - TypeScript watch mode (自動コンパイル) │ │
│ │ - nodemon (ホットリロード) │ │
│ │ - Port: 3021 │ │
│ │ - デバッガ直接アタッチ可能 │ │
│ └──────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ Dashboard (Vite) - ホストで直接実行 │ │
│ │ - Vite dev server │ │
│ │ - Port: 5173 │ │
│ └──────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ Docker Compose Services │ │
│ │ │ │
│ │ - PostgreSQL (Port: 5433) │ │
│ │ - Redis (Port: 6379) │ │
│ │ - pgAdmin (Port: 8080) │ │
│ │ - MailCatcher (Port: 1080/1025) │ │
│ │ - Redis Commander (Port: 8081) │ │
│ └──────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
この構成を採用する理由¶
- ホットリロードの高速化:
TypeScriptのウォッチモードや
nodemonによる再起動が、コンテナ内よりもホストマシンで直接実行する方が高速 - デバッグの容易さ: VS Codeなどのデバッガを直接アタッチでき、ブレークポイントやプロファイル調査が容易
- 開発効率: コンテナのビルドや再起動のオーバーヘッドがなく、開発サイクルが速い
- 依存サービスの分離: データベースやキャッシュなどの依存サービスだけをDocker化することで、本番環境に近い状態を維持しつつ、アプリケーション自体は軽量に開発できる
本番環境との違い¶
| 項目 | ローカル開発環境 | 本番環境(Fly.io) |
|---|---|---|
| Vendureサーバー | ホストマシンで直接実行 | Dockerコンテナ内で実行 |
| PostgreSQL | Dockerコンテナ | 独立したFly.ioアプリ(ritsubi-postgres-db) |
| Redis | Dockerコンテナ | 未使用(in-memoryキャッシュ) |
| ホットリロード | TypeScript watch + nodemon | なし(ビルド済み) |
| デバッグ | VS Code直接アタッチ可能 | ログベース |
| メール送信 | MailCatcher(開発用) | 実際のSMTPサーバー |
| 開発ツール | MailCatcher、pgAdmin、Redis Commander | 使用しない |
詳細は
apps/vendure-server/LOCAL_DEVELOPMENT.md
を参照してください。
本番環境(Fly.io)コンテナ化アーキテクチャ¶
コンテナ化の単位¶
Fly.ioへのデプロイでは、Vendureサーバー(ritsubi-vendure-server)とその依存パッケージのみが1つのコンテナに含まれます。
ビルドコンテキストと含まれるパッケージ¶
- ビルドコンテキスト: プロジェクトルート(monorepo全体)
- 含まれるパッケージ:
ritsubi-vendure-server(メインアプリケーション)@ritsubi/plugins(カスタムプラグイン)@ritsubi/domain(ドメインモデル)@ritsubi/utils(ユーティリティ)@ritsubi/shared-graphql(GraphQLスキーマ)- 除外されるパッケージ:
ritsubi-storefront、その他の非依存パッケージ
マルチステージビルド¶
Dockerfile.flyは3段階のマルチステージビルドを使用:
- baseステージ: 全ワークスペースパッケージをビルド
- deployステージ: 本番依存関係のみをインストールし、
pnpm deployで展開 - runnerステージ: 最小限のランタイムイメージにコピー
コンテナ内の構造¶
/app/apps/vendure-server/ # Vendureサーバー本体
├── dist/ # ビルド済みTypeScript
├── node_modules/ # 依存関係(pnpm deployで展開)
└── src/ # ソースコード(デバッグ用)
/app/packages/ # 依存するワークスペースパッケージ
├── plugins/
├── domain/
├── utils/
└── shared-graphql/
デプロイフロー¶
- ビルド: ルートディレクトリをコンテキストとしてDockerイメージをビルド
- プッシュ: Fly.io Registryにイメージをプッシュ
- デプロイ: Blue-Greenデプロイメント戦略で展開
- マイグレーション:
release_commandでデータベースマイグレーション実行 - 起動: アプリケーションを起動
詳細は
apps/vendure-server/DEPLOYMENT.md
を参照してください。
アーキテクチャ概要¶
システム構成¶
graph TB
subgraph "Internet"
U[ユーザー]
CF[Cloudflare CDN]
end
subgraph "Cloudflare Pages (Global Edge)"
subgraph "Next.js Storefront"
NS1[Edge Function (App Router)]
NS2[Edge Function (API Routes)]
end
end
subgraph "Fly.io Tokyo Region (nrt)"
subgraph "Vendure Backend"
V1[Vendure Instance 1]
V2[Vendure Instance 2]
WK[Worker Instance]
end
subgraph "Data Layer"
PG[(Fly Postgres)]
UP[(Upstash Redis)]
VOL[Fly Volumes]
end
end
subgraph "External Services"
SMTP[メール配信]
PAY[決済ゲートウェイ]
ERP[SMILE ERP]
S3[Wasabi Object Storage]
end
U --> CF
CF --> NS1
CF --> NS2
NS1 --> V1
NS2 --> V2
V1 --> PG
V2 --> PG
V1 --> UP
V2 --> UP
WK --> UP
WK --> PG
V1 --> VOL
V2 --> VOL
V1 --> S3
V1 --> SMTP
V2 --> PAY
V1 --> ERP
技術スタック一覧¶
全体構成¶
| コンポーネント | 技術/サービス | 目的 |
|---|---|---|
| Frontend | Next.js 15.5 (App Router) | React SSR/SSG Storefront |
| Backend | Vendure 3.4 (Node.js/TypeScript) | Headless E-commerce Engine |
| Database | PostgreSQL 17 + PGroonga (Fly.io Docker) | メインデータストレージ |
| Cache/Queue | Upstash Redis | セッション管理、ジョブキュー |
| Storage | Wasabi (S3 互換) + Fly Volumes | アセット保管とローカルキャッシュ |
| CDN | Cloudflare | 静的アセット配信 |
| Hosting (FE) | Cloudflare Pages (Edge Functions) | グローバルエッジでのSSR/静的配信 |
| Hosting (BE) | Fly.io (Tokyo Region) | コンテナオーケストレーション |
フロントエンド技術スタック¶
| 技術 | バージョン | 用途 |
|---|---|---|
| Next.js | 15.x | React フレームワーク |
| React | 19.x | UI ライブラリ |
| TypeScript | 5.2.2 | 型安全性 |
| Tailwind CSS | 4.1.13 | スタイリング |
| shadcn/ui | Latest | UI コンポーネント |
| Apollo Client | 3.8.0 | GraphQL クライアント |
| Radix UI | 1.2.12 | UI プリミティブ |
| Lucide React | 0.544.0 | アイコンライブラリ |
| React Hook Form | 7.47.0 | フォーム管理 |
| Zod | 3.22.4 | バリデーション |
| Embla Carousel | 8.x | スライダー・カルーセル |
バックエンド技術スタック¶
| 技術 | バージョン | 用途 |
|---|---|---|
| Vendure | 3.4.3 | E-commerce エンジン |
| Node.js | 22.x | ランタイム |
| TypeScript | 5.2.2 | 型安全性 |
| GraphQL | 16.11.0 | API |
| PostgreSQL | 17.x + PGroonga 4.0.4 | データベース |
| Redis | 7.x | キャッシュ・キュー |
| NestJS | 10.3.10 | フレームワーク |
| TypeORM | 0.3.17 | ORM |
インフラ・運用技術スタック¶
| 技術 | バージョン | 用途 |
|---|---|---|
| Cloudflare Pages | Latest | フロントエンドホスティング |
| Cloudflare CDN/DNS | Latest | CDN・DNS |
| Fly.io | Latest | Vendure コンテナホスティング |
| Upstash | Latest | Redis マネージドサービス |
| PostgreSQL | 15.x | データベース |
| Redis | 7.x | キャッシュ・キュー |
開発・テスト技術スタック¶
| 技術 | バージョン | 用途 |
|---|---|---|
| oxlint | 1.31.0 | コード品質チェック |
| Prettier | 3.0.3 | コードフォーマット |
| Turbo | 1.13.4 | ビルドシステム |
| Vitest | 3.2.4 | ユニットテスト・E2E (Vendure) |
| Playwright | 1.49.0 | E2Eテスト (Storefront) |
| Testing Library | 16.3.0 | React テストユーティリティ |
| Storybook | 9.1.7 | コンポーネント開発環境 |
| GraphQL Codegen | 5.0.0 | 型安全なGraphQLクライアント |
| MSW | 2.4.9 | API モック |
デプロイメント構成¶
アプリケーション構成¶
# Fly.io アプリケーション構成
ritsubi-vendure:
- Primary Region: nrt (Tokyo)
- Instance Count: 2 (HA構成)
- Resource: 2x shared-cpu-1x (1GB RAM)
ritsubi-worker:
- Primary Region: nrt (Tokyo)
- Instance Count: 1
- Resource: 1x shared-cpu-1x (1GB RAM)
ネットワーク設計¶
内部通信(IPv6 Private Network)¶
Vendure API → PostgreSQL: 内部プライベート接続
Vendure API → Upstash Redis: 内部プライベート接続
Worker → Redis/PostgreSQL: 内部プライベート接続
外部通信(Public Internet)¶
Cloudflare Pages Edge → Vendure GraphQL: HTTPS (Port 443)
外部API → Vendure: HTTPS (Port 443, Admin GUI用)
Webhook → Vendure: HTTPS (Port 443, 決済通知用)
セキュリティ設計¶
ネットワークセキュリティ¶
- プライベートネットワーク: Fly.io IPv6 プライベートネットワーク使用
- 外部アクセス制限: Admin GUI のみ特定IPからアクセス許可
- HTTPS強制: すべての外部通信でTLS 1.3使用
- CORS設定: Next.js Storefront のみからのAPI アクセス許可
アプリケーションセキュリティ¶
// Vendure セキュリティ設定例
export const securityConfig = {
auth: {
sessionDuration: '7d',
sessionCacheStrategy: new RedisSessionCacheStrategy({
redisOptions: {
host: process.env.UPSTASH_REDIS_URL,
password: process.env.UPSTASH_REDIS_PASSWORD,
tls: { rejectUnauthorized: false },
},
}),
},
cors: {
origin: [
'https://ritsubi-storefront.pages.dev',
'https://order.ritsubi.co.jp',
],
credentials: true,
},
adminApiPath: 'admin-api',
shopApiPath: 'shop-api',
};
データベース設計¶
PostgreSQL 構成(Fly Postgres)¶
# Fly Postgres 設定
Database: ritsubi-vendure-db
Instance: shared-cpu-1x (1GB RAM, 10GB Storage)
Region: nrt (Tokyo)
Backup: 自動日次バックアップ (7日間保持)
High Availability: マスター/スタンバイ構成
接続管理¶
// Vendure データベース接続設定
export const dbConfig = {
type: 'postgres',
host: process.env.DATABASE_HOST,
port: 5432,
username: process.env.DATABASE_USER,
password: process.env.DATABASE_PASSWORD,
database: process.env.DATABASE_NAME,
ssl: { rejectUnauthorized: false },
synchronize: false, // 本番環境では false
migrationsRun: true,
logging: ['error', 'warn'],
poolSize: 10,
connectionTimeoutMillis: 5000,
idleTimeoutMillis: 10000,
};
Redis 構成(Upstash)¶
# Upstash Redis 設定
Plan: 固定料金プラン (3GB Max Data Size)
Region: Tokyo (Fly.io 内部配置)
Eviction Policy: noeviction (BullMQ対応)
Max Connections: 100
TLS: 有効
用途別設定¶
// Redis 用途別接続設定
export const redisConfig = {
// セッションキャッシュ用
session: {
host: process.env.UPSTASH_REDIS_HOST,
port: 6379,
password: process.env.UPSTASH_REDIS_PASSWORD,
tls: { rejectUnauthorized: false },
keyPrefix: 'session:',
ttl: 7 * 24 * 60 * 60, // 7日間
},
// BullMQ ジョブキュー用
queue: {
host: process.env.UPSTASH_REDIS_HOST,
port: 6379,
password: process.env.UPSTASH_REDIS_PASSWORD,
tls: { rejectUnauthorized: false },
maxRetriesPerRequest: null, // BullMQ必須設定
keyPrefix: 'bull:',
lazyConnect: true,
},
// 一般キャッシュ用
cache: {
host: process.env.UPSTASH_REDIS_HOST,
port: 6379,
password: process.env.UPSTASH_REDIS_PASSWORD,
tls: { rejectUnauthorized: false },
keyPrefix: 'cache:',
ttl: 60 * 60, // 1時間
},
};
スケーリング戦略¶
水平スケーリング¶
Auto Scaling 設定¶
# fly.toml - Auto scaling設定
[http_service]
internal_port = 3000
force_https = true
auto_stop_machines = true
auto_start_machines = true
min_machines_running = 1
processes = ["app"]
[[http_service.concurrency]]
type = "connections"
hard_limit = 1000
soft_limit = 800
[metrics]
port = 9091
path = "/metrics"
インスタンス拡張方針¶
# トラフィック増加時の拡張
# CPU使用率 > 70% で自動スケールアップ
# 接続数 > 800 で新インスタンス起動
# 負荷レベル別インスタンス数
Low Traffic (通常時): 2 instances
Medium Traffic (繁忙期): 4 instances
High Traffic (キャンペーン): 6 instances
垂直スケーリング¶
リソース拡張パス¶
# スケールアップパス
Tier 1 (開始): shared-cpu-1x (1GB RAM)
Tier 2 (成長): performance-1x (2GB RAM)
Tier 3 (拡大): performance-2x (4GB RAM)
Tier 4 (企業): performance-4x (8GB RAM)
パフォーマンス最適化¶
Next.js 最適化¶
// next.config.js - 最適化設定
const nextConfig = {
experimental: {
serverComponentsExternalPackages: ['@vendure/core'],
},
images: {
domains: ['ritsubi-assets.fly.dev'],
formats: ['image/webp', 'image/avif'],
},
compress: true,
poweredByHeader: false,
reactStrictMode: true,
swcMinify: true,
// ISR設定
generateBuildId: () => process.env.BUILD_ID || 'development',
// Cloudflare CDN設定
assetPrefix: process.env.CDN_URL || '',
// 本番最適化
productionBrowserSourceMaps: false,
optimizeFonts: true,
// Bundle分析
webpack: (config, { isServer }) => {
if (!isServer) {
config.resolve.fallback = {
...config.resolve.fallback,
fs: false,
net: false,
tls: false,
};
}
return config;
},
};
Vendure 最適化¶
// Vendure パフォーマンス設定
export const vendureConfig: VendureConfig = {
// データベース最適化
dbConnectionOptions: {
type: 'postgres',
poolSize: 10,
cache: {
type: 'redis',
options: redisConfig.cache,
},
},
// GraphQL 最適化
apiOptions: {
shopApiPlayground: false, // 本番では無効
adminApiPlayground: false,
introspection: false,
cors: securityConfig.cors,
},
// ジョブキュー最適化
jobQueueOptions: {
jobQueueStrategy: new BullMQJobQueueStrategy({
connection: redisConfig.queue,
}),
},
// セッション最適化
authOptions: {
sessionCacheStrategy: new RedisSessionCacheStrategy({
redisOptions: redisConfig.session,
}),
},
// アセット最適化
assetOptions: {
assetStorageStrategy: new CloudflareR2AssetStorageStrategy({
bucket: process.env.R2_BUCKET_NAME,
region: 'auto',
credentials: {
accessKeyId: process.env.R2_ACCESS_KEY_ID,
secretAccessKey: process.env.R2_SECRET_ACCESS_KEY,
},
}),
},
};
スケジューラ設定(DefaultSchedulerPlugin)¶
apps/vendure-server/src/vendure-config.tsでDefaultSchedulerPluginを有効化済み。- 開発環境では
manualTriggerCheckInterval: '5s'、本番系は'10s'を指定し、手動トリガー監視の反応速度を調整。 - 既定で
runTasksInWorkerOnlyがtrueのため、スケジュールタスクはワーカープロセス側で処理される想定。 - 新規タスクは同ファイル内の
schedulerOptions.tasksに追記し、想定SLAに合わせてtimeout等のオプションを設定する。 - マルチインスタンス運用時でも単一実行を保証するため、BullMQ 導入済みの場合も当プラグインを併用する。
災害復旧・可用性¶
バックアップ戦略¶
Database Backup:
- 自動日次バックアップ(Fly Postgres標準)
- Point-in-time recovery(PITR)対応
- Cross-region backup(将来的に大阪リージョン)
Redis Backup:
- Upstash 自動バックアップ(24時間ごと)
- データ永続化(AOF + RDB)
Application Backup:
- Git リポジトリ(GitHub)
- Docker イメージ(Fly Registry)
- 設定ファイル(環境変数はSecrets管理)
障害対応¶
graph TD
A[障害検知] --> B{障害レベル判定}
B -->|Level 1| C[自動復旧]
B -->|Level 2| D[手動介入]
B -->|Level 3| E[緊急対応]
C --> F[ヘルスチェック]
D --> G[ログ調査]
E --> H[バックアップから復旧]
F --> I[正常化確認]
G --> J[原因特定]
H --> K[サービス復旧]
I --> L[監視強化]
J --> M[修正デプロイ]
K --> N[事後検証]
RTO/RPO 目標¶
Recovery Time Objective (RTO):
- Level 1 障害: 5分以内
- Level 2 障害: 30分以内
- Level 3 障害: 2時間以内
Recovery Point Objective (RPO):
- データベース: 1時間以内
- Redis キャッシュ: 即座に再構築可能
- ファイルアセット: 24時間以内(Cloudflare R2)
運用考慮事項¶
デプロイメント戦略¶
Deployment Strategy: Blue-Green Deployment
- 新バージョンを並行環境にデプロイ
- ヘルスチェック通過後にトラフィック切り替え
- 問題発生時は即座にロールバック
Release Schedule:
- メジャーリリース: 月1回(計画停止あり)
- マイナーリリース: 週1回(無停止デプロイ)
- ホットフィックス: 随時(緊急対応)
監視・アラート¶
Key Metrics:
- Response Time: < 200ms (P95)
- Error Rate: < 0.1%
- Availability: > 99.5%
- Database Connections: < 80%
- Redis Memory Usage: < 70%
Alert Thresholds:
- Critical: サービス停止、データ損失リスク
- Warning: パフォーマンス劣化、リソース逼迫
- Info: 通常の運用イベント
メンテナンス¶
Scheduled Maintenance:
- Database Maintenance: 月1回(日曜深夜)
- Security Updates: 月2回
- Performance Tuning: 四半期ごと
Maintenance Window:
- 時間: 日曜 2:00-4:00 JST
- 通知: 1週間前にユーザーへ告知
- ロールバック: 30分以内で実行可能
文書バージョン: 1.0 作成日: 2025年9月17日 次回レビュー: 2025年12月17日(サービス開始3ヶ月後)