コンテンツにスキップ

システムアーキテクチャ設計書

システム概要

リツビ BtoB ECサイトの技術スタック全体とアーキテクチャ設計をまとめた文書です。

ローカル開発環境アーキテクチャ

開発環境の構成

ローカル開発環境では、Vendureサーバーはホストマシンで直接実行され、依存サービス(PostgreSQL、Redis等)のみがDockerコンテナで実行されます。これはVendure開発における標準的な構成です。

アーキテクチャ図

┌─────────────────────────────────────────────────────────────┐
│  ホストマシン (開発環境)                                      │
│                                                             │
│  ┌──────────────────────────────────────────────────────┐  │
│  │ Vendure Server (Node.js) - ホストで直接実行          │  │
│  │ - TypeScript watch mode (自動コンパイル)             │  │
│  │ - pm2 watch (ホットリロード)                         │  │
│  │ - Port: 3021                                        │  │
│  │ - デバッガ直接アタッチ可能                           │  │
│  └──────────────────────────────────────────────────────┘  │
│                                                             │
│  ┌──────────────────────────────────────────────────────┐  │
│  │ Dashboard (Vite) - ホストで直接実行                  │  │
│  │ - Vite dev server                                    │  │
│  │ - Port: 6202 (独立起動の正規経路は `/`)                    │  │
│  └──────────────────────────────────────────────────────┘  │
│                                                             │
│  ┌──────────────────────────────────────────────────────┐  │
│  │ Docker Compose Services                              │  │
│  │                                                       │  │
│  │  - PostgreSQL (Port: 5433)                          │  │
│  │  - Redis (Port: 6379) ※profiles: redis (opt-in)   │  │
│  │  - pgAdmin (Port: 8080)                              │  │
│  │  - MailCatcher (Port: 1080/1025)                    │  │
│  │  - Redis Commander (Port: 8081) ※profiles: redis   │  │
│  └──────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────┘

この構成を採用する理由

  1. ホットリロードの高速化: TypeScriptのウォッチモードやpm2 watchによる再起動が、コンテナ内よりもホストマシンで直接実行する方が高速
  2. デバッグの容易さ: VS Codeなどのデバッガを直接アタッチでき、ブレークポイントやプロファイル調査が容易
  3. 開発効率: コンテナのビルドや再起動のオーバーヘッドがなく、開発サイクルが速い
  4. 依存サービスの分離: データベースやキャッシュなどの依存サービスだけをDocker化することで、本番環境に近い状態を維持しつつ、アプリケーション自体は軽量に開発できる

本番環境との違い

項目 ローカル開発環境 本番環境(Fly.io)
Vendureサーバー ホストマシンで直接実行 Dockerコンテナ内で実行
PostgreSQL Dockerコンテナ 独立したFly.ioアプリ(ritsubi-postgres-db
Redis Dockerコンテナ 本番・ステージングとも Fly.io 専用 Redis app
ホットリロード TypeScript watch + pm2 watch なし(ビルド済み)
デバッグ VS Code直接アタッチ可能 ログベース
メール送信 MailCatcher(開発用) 実際のSMTPサーバー
開発ツール MailCatcher、pgAdmin、Redis Commander 使用しない

詳細は Vendure開発ガイド を参照してください。

本番環境(Fly.io)コンテナ化アーキテクチャ

コンテナ化の単位

Fly.ioへのデプロイでは、Vendureサーバー(ritsubi-vendure-server)とその依存パッケージのみが1つのコンテナに含まれます

ビルドコンテキストと含まれるパッケージ

  • ビルドコンテキスト: プロジェクトルート(monorepo全体)
  • 含まれるパッケージ:
  • ritsubi-vendure-server(メインアプリケーション)
  • @ritsubi/plugins(すべてのカスタムプラグインを集約)
  • @ritsubi/domain(ドメインモデル)
  • @ritsubi/utils(ユーティリティ)
  • @ritsubi/contract(GraphQLスキーマ)
  • 除外されるパッケージ: ritsubi-storefront、その他の非依存パッケージ

マルチステージビルド

apps/vendure-server/Dockerfile.fly は3段階のマルチステージビルドを使用:

  1. baseステージ: 全ワークスペースパッケージをビルド
  2. deployステージ: 本番依存関係のみをインストールし、pnpm deployで展開(依存関係をフラット化しシンボリックリンク問題を回避)
  3. runnerステージ: 最小限のランタイムイメージにコピー

コンテナ内の構造

/app/apps/vendure-server/     # Vendureサーバー本体
├── dist/                      # ビルド済みTypeScript
├── node_modules/              # 依存関係(pnpm deployで展開)
└── src/                       # ソースコード(デバッグ用)

/app/packages/                 # 依存するワークスペースパッケージ(ビルド成果物のみ)
├── plugins/
├── domain/
├── utils/
└── contract/

デプロイフロー

  1. ビルド: GitHub Actions がルートコンテキストから Docker image を build
  2. プッシュ: registry.fly.io へ image を push
  3. デプロイ: staging / production は build 済み image を指定して展開
  4. スキーマ反映: Fly release_command が deploy 中に migration + structural repair を自動実行し、deploy-fly-app / --skip-release-command を使う復旧時だけ migrate-fly で同じ release contract を明示再実行する
  5. 起動: アプリケーションを起動し、必要な seed は post-deploy で個別実行

詳細は デプロイメントガイド を参照してください。

アーキテクチャ概要

システム構成

graph TB
    subgraph "Internet"
        U[ユーザー]
        CF[Cloudflare CDN]
    end

    subgraph "Cloudflare Workers (Global Edge)"
        subgraph "Storefront (Vite + TanStack Router)"
            NS["Worker (Static Assets / SPA Fallback / Worker API)"]
        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[(Redis: Fly-hosted)]
            VOL[Fly Volumes]
        end
    end

    subgraph "External Services"
        SMTP[メール配信]
        PAY[決済ゲートウェイ]
        ERP[SMILE ERP]
        S3[Cloudflare R2]
    end

    U --> CF
    CF --> NS
    NS --> V1
    NS --> 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

認証フロー(顧客の仮ID運用)

  • 顧客は Storefront の初回ログイン専用画面で仮IDと仮パスワードを入力する
  • 仮IDに @ritsubi-platform.com を付与して通常ログインを流用する
  • Vendure 側ではメールアドレスとして扱い、管理者向けの特別フローは設けない

Storefront の公開/認証境界

  • Storefront の正本は 会員制 catalog であり、//products/search/products/[slug]/campaigns/*/cart/checkout/quick-order/account/* など commerce 導線はログイン必須とする。
  • 未認証でも閲覧可能な公開例外は editorial 導線/articles/*/announcements/*、および signed preview token で保護する /products/preview に限定する。
  • この境界は TanStack Router の route guard (apps/storefront/src/runtime/auth-guard.ts) を正本として強制し、beforeLoad から適用する。未認証時は /auth/login?redirect=<元URL> へリダイレクトする。
  • Worker navigation guard (apps/storefront/worker.js) と /api/auth-session は、 cookie の存在だけではなく Vendure activeCustomer.id を確認する。stale cookie、 顧客削除、session drift は SPA shell を描画せず login redirect に倒す。
  • 目的は、得意先別の表示制御・価格制御・購買条件に依存する route を匿名閲覧や public cache から隔離し、CMS 記事/お知らせだけを外部公開可能な面として切り出すことにある。

技術スタック一覧

全体構成

コンポーネント 技術/サービス 目的
Frontend Vite 8.x + TanStack Router 1.x React SPA Storefront + Worker API
Backend Vendure 3.5 (Node.js/TypeScript) Headless E-commerce Engine
Database PostgreSQL 17 + pg_trgm (Fly.io Docker) メインデータストレージ
Cache/Queue 本番・ステージングとも Fly.io 専用 Redis staging も本番と同じ session/shared cache 経路
Storage Cloudflare R2 + Fly Volumes アセット・帳票・バックアップ保管とローカルキャッシュ
CDN Cloudflare 静的アセット配信
Hosting (FE) Cloudflare Workers + Static Assets グローバルエッジでの SPA / 静的配信
Hosting (BE) Fly.io (Tokyo Region) コンテナオーケストレーション
Hosting (CMS) Webarena Indigo (VPS) WordPress ホスティング (Ansible + Docker)

フロントエンド技術スタック

技術 バージョン 用途
Vite 8.x フロントエンド build / dev server
TanStack Router 1.x ルーティング / route guard
React 19.x UI ライブラリ
TypeScript 5.2.2 型安全性
Tailwind CSS 4.1.13 スタイリング
shadcn/ui Latest UI コンポーネント
TanStack Query 5.x 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.5.2 E-commerce エンジン
Node.js 24.x ランタイム
TypeScript 5.7.2 型安全性
GraphQL 16.11.0 API
PostgreSQL 17.x データベース
Redis 7.x キャッシュ・キュー
NestJS 11.1.6 フレームワーク
TypeORM 0.3.28 ORM

※ 正確なバージョン情報は package.json および pnpm-workspace.yaml を参照してください。

インフラ・運用技術スタック

技術 バージョン 用途
Cloudflare Workers Latest フロントエンドホスティング
Cloudflare CDN/DNS Latest CDN・DNS
Fly.io Latest Vendure コンテナホスティング
Fly Redis Latest Redis(本番)
Fly Volumes Latest Redis 永続ストレージ
PostgreSQL 17.x データベース
Redis 7.x キャッシュ・キュー

開発・テスト技術スタック

技術 バージョン 用途
oxlint 1.31.0 コード品質チェック
oxfmt 0.47.0 コード・JSON・Markdown・YAML フォーマット
Nx 20.2.0 ビルドシステム
Vitest 3.2.4 ユニットテスト・E2E (Vendure)
Playwright 1.49.0 E2Eテスト (Storefront)
Testing Library 16.3.0 React テストユーティリティ
React Cosmos Latest コンポーネント開発環境 (Storefront / Dashboard)
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 → Redis(Fly-hosted): 内部プライベート接続
Worker → Redis/PostgreSQL: 内部プライベート接続

外部通信(Public Internet)

Cloudflare Workers 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設定: Storefront のみからの API アクセス許可

アプリケーションセキュリティ

// Vendure セキュリティ設定例
export const securityConfig = {
  auth: {
    sessionDuration: "7d",
    sessionCacheStrategy: new RedisSessionCacheStrategy({
      redisOptions: {
        host: process.env.REDIS_URL,
        password: process.env.REDIS_PASSWORD,
        tls: { rejectUnauthorized: false },
      },
    }),
  },
  cors: {
    // Storefront Worker 経由の Shop API は alias domain を直接許可対象に増やさず、
    // Worker 側で canonical origin に正規化してから Vendure へ送る。
    origin: ["https://order.ritsubi-platform.com", "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 構成(本番・ステージングとも Fly.io 専用 Redis)

# Redis 設定(Fly.io 専用 app)
Runtime: redis:7-alpine
Region: Tokyo (nrt)
Persistence: Fly Volume + AOF
Eviction Policy: noeviction
TLS: 無効(`*.internal` で内部接続)

用途別設定

// Redis 用途別接続設定
export const redisConfig = {
  // セッションキャッシュ用
  session: {
    host: process.env.REDIS_HOST,
    port: 6379,
    password: process.env.REDIS_PASSWORD,
    tls: { rejectUnauthorized: false },
    keyPrefix: "session:",
    ttl: 7 * 24 * 60 * 60, // 7日間
  },

  // BullMQ ジョブキュー用
  queue: {
    host: process.env.REDIS_HOST,
    port: 6379,
    password: process.env.REDIS_PASSWORD,
    tls: { rejectUnauthorized: false },
    maxRetriesPerRequest: null, // BullMQ必須設定
    keyPrefix: "bull:",
    lazyConnect: true,
  },

  // 一般キャッシュ用
  cache: {
    host: process.env.REDIS_HOST,
    port: 6379,
    password: process.env.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)

パフォーマンス最適化

Vite / Cloudflare 最適化

  • Vite build はハッシュ付き /assets/* を生成し、Cloudflare Workers の ASSETS binding 経由で公開する。
  • worker.js/assets/*/favicon.ico/manifest.json などの静的要求を fallback せず返し、それ以外の navigation request だけを SPA へフォールバックする。
  • vite.config.ts では不要な依存の pre-bundle を除外し、mock/test 用パッケージを production bundle へ混入させない。

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,
    }),
  },

  // アセット最適化(Cloudflare R2)
  assetOptions: {
    storageStrategyFactory: configureS3AssetStorage({
      bucket: process.env.VENDURE_R2_BUCKET_NAME,
      credentials: {
        accessKeyId: process.env.VENDURE_R2_ACCESS_KEY_ID,
        secretAccessKey: process.env.VENDURE_R2_SECRET_ACCESS_KEY,
      },
      nativeS3Configuration: {
        endpoint: process.env.VENDURE_R2_ENDPOINT,
        region: process.env.VENDURE_R2_REGION ?? "auto",
        forcePathStyle: process.env.VENDURE_R2_FORCE_PATH_STYLE === "true",
      },
    }),
  },
};

スケジューラ設定(DefaultSchedulerPlugin)

  • apps/vendure-server/src/vendure-config.shared.tsDefaultSchedulerPlugin を有効化済み。
  • 開発環境では manualTriggerCheckInterval: '5s'、本番系は '10s' を指定し、手動トリガー監視の反応速度を調整。
  • 既定で runTasksInWorkerOnlytrue のため、スケジュールタスクはワーカープロセス側で処理される想定。
  • 新規タスクは同ファイル内の schedulerOptions.tasks に追記し、想定SLAに合わせて timeout 等のオプションを設定する。
  • マルチインスタンス運用時でも単一実行を保証するため、BullMQ 導入済みの場合も当プラグインを併用する。

災害復旧・可用性

バックアップ戦略

Database Backup:
  - Vendure PostgreSQL: GitHub Actions (hourly) -> Cloudflare R2 (custom dump, 30日保持)
  - WordPress MariaDB: systemd timer (hourly) -> Cloudflare R2 (gzip SQL, 30日保持)
  - PITR / cross-region backup は現行実装では未提供

Operational State:
  - Redis は session/shared cache の再構築前提
  - Storefront maintenance KV は単一キーの手動再作成前提

Object Storage:
  - R2 asset bucket は primary storage であり、独立バックアップではない

Application Backup:
  - Git リポジトリ(GitHub)
  - Docker イメージ(Fly Registry)
  - 設定ファイル(環境変数はSecrets管理)

[!NOTE] current implementation / gap analysis の正本は docs/05-delivery/maintenance/backup-and-restore.md を参照してください。

障害対応

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)

[!NOTE] 上記は目標値です。現在の実装到達状況は docs/05-delivery/maintenance/backup-and-restore.md を正本とします。

運用考慮事項

デプロイメント戦略

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ヶ月後)