プロジェクト設計
これでフェーズ1〜4の全内容が完了しました。Charlieはこの知識を統合して, 完全なMegaShop eコマースプラットフォームを設計します。要件分析からアーキテクチャ設計, データモデルからAPI仕様まで, これが実践プロジェクトの第一歩です。
1. 学ぶ内容
- 要件分析:ユーザーロール (Alice/Bob/Charlie)と機能モジュールの分割
- アーキテクチャ:SSR + ISRハイブリッドレンダリング + Nitro API + Prisma + Redis
- データモデル:ER図設計 + Prismaスキーマ
- API仕様:RESTful API設計 + バージョン管理 + エラーコード
- 技術選定の決定と理由
2. アーキテクトのリアルストーリー
(1) ペインポイント:設計なしの開発
Charlieは以前, 設計ドキュメントなしでチームにすぐコードを書かせました。その結果, Aliceのショッピングカートのデータ構造がBobの注文データと合わず, APIの命名は混沌とし, データベースにはインデックスがなく, クエリは遅く, 後からの変更コストは膨大でした。
(2) システム設計ソリューション
設計先行, その後に開発 - ER図でデータモデルを定義し, RESTful仕様でAPIを定義し, アーキテクチャ図でシステム境界を定義します。設計フェーズに1週間多く費やせば, 開発中に3週間節約できます。
(3) 利点:明確なロードマップ
すべての開発者が同じ設計ドキュメントを参照し - 一貫したデータ構造, 統一されたAPI仕様, 明確なアーキテクチャ境界 - があれば, 協調効率は3倍に向上します。
3. 要件分析
(1) ユーザーロールとコアニーズ
| ロール | アイデンティティ | コアニーズ | 主要指標 |
|---|---|---|---|
| Alice | 消費者 | 閲覧/検索/カート追加/購入/レビュー | 初回画面 < 2s, 検索 < 500ms |
| Bob | 管理者 | 商品管理/注文処理/ユーザー管理 | CRUD応答 < 200ms |
| Charlie | アーキテクト | システム安定性/高性能/スケーラビリティ | 99.9%可用性, 100万商品対応 |
(2) 機能モジュール分割
| モジュール | 機能 | 優先度 | 影響ページ |
|---|---|---|---|
| 認証 | サインアップ/サインイン/OAuth2/JWT | P0 | /login, /register |
| 商品 | CRUD/検索/カテゴリ/フィルタ | P0 | /products, /products/[id] |
| ショッピングカート | カート追加/数量変更/削除/クリア | P0 | /cart |
| 注文 | 作成/支払い/ステータス確認 | P0 | /checkout, /orders |
| ユーザー | 個人情報/住所/レビュー | P1 | /profile |
| 管理 | バックエンドCRUD/統計/モデレーション | P1 | /admin/** |
| 国際化 | 中国語/英語/日本語 + マルチ通貨 | P1 | グローバル |
| SEO | 動的meta/Sitemap/JSON-LD | P0 | 商品ページ |
4. アーキテクチャ設計
(1) MegaShopシステムアーキテクチャ概要
graph TB
subgraph Client["クライアント層"]
Browser[ブラウザ / モバイル]
Bot[検索エンジンボット]
end
subgraph CDN["CDN層"]
CF[Cloudflare / Vercel Edge]
end
subgraph Nuxt["Nuxt 3アプリケーション"]
SSR[SSRエンジン]
ISR[ISRキャッシュ]
API[Nitro APIルート]
MW[ミドルウェア / 認証]
end
subgraph Data["データ層"]
PG[(PostgreSQL)]
Redis[(Redisキャッシュ)]
S3[オブジェクトストレージ / 画像]
end
subgraph External["外部サービス"]
Stripe[Stripe決済]
Google[Google OAuth2]
Analytics[アナリティクスサービス]
end
Browser --> CDN
Bot --> CDN
CDN --> SSR
CDN --> ISR
SSR --> API
API --> MW
API --> PG
API --> Redis
API --> S3
API --> Stripe
API --> Google
API --> Analytics
ISR --> Redis
(2) レンダリング戦略設計
| ページタイプ | レンダリングモード | swr | 理由 |
|---|---|---|---|
| ホーム | SSG | - | 安定したコンテンツ |
| 商品一覧 | ISR | 3600s | 毎時更新 |
| 商品詳細 | ISR | 86400s | 毎日更新 |
| 検索結果 | SSR | - | リアルタイムクエリ |
| カート/チェックアウト | CSR | - | 会員専用 |
| 管理ダッシュボード | CSR | - | SEO不要 |
| APIルーティング | 動的 | - | オンデマンドキャッシュ |
(3) 技術スタックの選定
(1) ▶サンプル:nuxt.config.ts技術スタック統合
TYPESCRIPT
// nuxt.config.ts - 完全な技術スタック統合
export default defineNuxtConfig({
ssr: true,
modules: [
'@pinia/nuxt',
'@nuxtjs/tailwindcss',
'@nuxtjs/i18n',
'@nuxtjs/sitemap',
'@nuxt/image',
'~/modules/analytics'
],
i18n: {
locales: [
{ code: 'en', name: 'English', file: 'en.json', currency: 'USD' },
{ code: 'zh', name: 'Chinese', file: 'zh.json', currency: 'CNY' },
{ code: 'ja', name: 'Japanese', file: 'ja.json', currency: 'JPY' }
],
defaultLocale: 'en',
lazy: true,
langDir: 'locales/'
},
image: { quality: 80, format: ['webp', 'avif'] }
})
出力:
TEXT
// 実行成功
(2) ▶サンプル:Docker Composeサービスオーケストレーション
YAML
# docker-compose.yml - インフラ定義
services:
web:
build: .
ports: ["3000:3000"]
depends_on: [db, redis]
db:
image: postgres:16-alpine
volumes: [postgres_data:/var/lib/postgresql/data]
redis:
image: redis:7-alpine
volumes: [redis_data:/data]
nginx:
image: nginx:alpine
ports: ["80:80", "443:443"]
depends_on: [web]
出力:
TEXT
CONTAINER ID IMAGE STATUS PORTS
abc123 nginx:latest Up 2 hours 0.0.0.0:80->80/tcp
| 層 | 技術 | 選定理由 |
|---|---|---|
| フレームワーク | Nuxt 3 | SSR/ISR/CSRハイブリッドレンダリング |
| UI | Vue 3 + TailwindCSS | リアクティブ + 迅速な開発 |
| 状態管理 | Pinia | Vue 3公式ソリューション + SSR対応 |
| データベース | PostgreSQL + Prisma | 型安全ORM + 数百万クエリ対応 |
| キャッシュ | Redis + Nitro KV | APIキャッシュ + ISRストレージ |
| 認証 | JWT + OAuth2 | ステートレス + ソーシャルログイン |
| 国際化 | @nuxtjs/i18n | 多言語 + SEO hreflang |
| 画像 | @nuxt/image | WebP/AVIF + レスポンシブ |
| テスト | Vitest + Playwright | ユニット + E2E |
| デプロイ | Docker Compose | フルスタックサービスオーケストレーション |
| CI/CD | GitHub Actions | 自動化パイプライン |
5. データモデル設計
(1) ER図
erDiagram
User ||--o{ Order : "注文する"
User ||--o{ Review : "レビューする"
User ||--o{ CartItem : "持つ"
User ||--o{ Address : "所有する"
Product ||--o{ OrderItem : "含まれる"
Product ||--o{ CartItem : "追加される"
Product ||--o{ Review : "受ける"
Product ||--o{ ProductImage : "持つ"
Product }o--|| Category : "属する"
Category ||--o{ Category : "親子"
Order ||--o{ OrderItem : "含む"
Order }o--|| Address : "配送先"
(2) コアテーブル設計
(1) ▶サンプル:コアPrismaスキーマ
PRISMA
// prisma/schema.prismaの主要モデル
model Product {
id Int @id @default(autoincrement())
name String
slug String @unique
price Decimal @db.Decimal(10, 2)
inStock Boolean @default(true)
categoryId Int
category Category @relation(fields: [categoryId], references: [id])
orderItems OrderItem[]
cartItems CartItem[]
@@index([categoryId])
@@index([price])
}
model Order {
id Int @id @default(autoincrement())
userId Int
user User @relation(fields: [userId], references: [id])
total Decimal @db.Decimal(10, 2)
status OrderStatus @default(PENDING)
items OrderItem[]
@@index([userId])
@@index([status])
}
出力:
TEXT
// 実行成功
(2) ▶サンプル:APIエラーコード定義
TYPESCRIPT
// server/utils/errors.ts
export const ErrorCodes = {
VALIDATION_ERROR: { statusCode: 400, message: 'Validation error' },
AUTH_REQUIRED: { statusCode: 401, message: 'Authentication required' },
TOKEN_EXPIRED: { statusCode: 401, message: 'Token expired' },
FORBIDDEN: { statusCode: 403, message: 'Insufficient permissions' },
NOT_FOUND: { statusCode: 404, message: 'Resource not found' },
CONFLICT: { statusCode: 409, message: 'Resource conflict' },
RATE_LIMITED: { statusCode: 429, message: 'Too many requests' }
} as const
export function throwError(code: keyof typeof ErrorCodes, details?: any) {
const err = ErrorCodes[code]
throw createError({ statusCode: err.statusCode, message: err.message, data: { errorCode: code, details } })
}
出力:
TEXT
// 実行成功
(3) ▶サンプル:標準APIレスポンス形式
TYPESCRIPT
// server/utils/response.ts
export function successResponse(data: any, message = 'Success') {
return { data, message, timestamp: Date.now() }
}
export function listResponse(items: any[], total: number, page: number, limit: number) {
return { items, total, page, limit, timestamp: Date.now() }
}
export function errorResponse(statusCode: number, errorCode: string, message: string, details?: any) {
return { statusCode, errorCode, message, details, timestamp: Date.now() }
}
出力:
TEXT
// 実行成功
(4) ▶サンプル:routeRulesレンダリング戦略設定
TYPESCRIPT
// nuxt.config.ts - ルートレベルのレンダリング戦略
routeRules: {
'/': { prerender: true },
'/products': { swr: 3600 },
'/products/**': { swr: 86400 },
'/admin/**': { ssr: false },
'/cart': { ssr: false },
'/api/**': { cors: true },
'/_nuxt/**': { headers: { 'cache-control': 'public, max-age=31536000, immutable' } }
}
出力:
TEXT
// 実行成功
(5) ▶サンプル:CI/CDパイプライン構造
YAML
# .github/workflows/ci.yml - コアパイプライン
name: CI
on: [push, pull_request]
jobs:
lint:
runs-on: ubuntu-latest
steps: [{ uses: actions/checkout@v4 }, { run: npm ci }, { run: npm run lint }]
test:
needs: lint
steps: [{ run: npm run test:coverage }]
build:
needs: test
steps: [{ run: npm run build }]
出力:
TEXT
CI/CDパイプラインが読み込まれました
パイプラインステータス:合格
テスト:12件合格, 0件不合格
| テーブル | フィールド数 | プライマリインデックス | 推定データ量 |
|---|---|---|---|
| Product | 12 | slug, categoryId, price | 100万 |
| Category | 6 | slug, parentId | 500 |
| User | 10 | email, role | 50万 |
| Order | 8 | userId, status, createdAt | 100万 |
| OrderItem | 6 | orderId, productId | 500万 |
| CartItem | 5 | userId+productId (unique) | 10万 |
| Review | 7 | productId, userId | 200万 |
| Address | 8 | userId | 50万 |
6. API仕様
(1) RESTful API設計
| メソッド | パス | 説明 | 認証 |
|---|---|---|---|
| GET | /api/products | 商品一覧 (ページネーション/フィルタ) | なし |
| GET | /api/products/:id | 商品詳細 | なし |
| POST | /api/products | 商品作成 | 管理者 |
| PUT | /api/products/:id | 商品更新 | 管理者 |
| DELETE | /api/products/:id | 商品削除 | 管理者 |
| GET | /api/categories | カテゴリ一覧 | なし |
| POST | /api/auth/register | 登録 | なし |
| POST | /api/auth/login | ログイン | なし |
| POST | /api/auth/refresh | トークン更新 | Cookie |
| GET | /api/cart | ショッピングカート | ユーザー |
| POST | /api/cart/add | カート追加 | ユーザー |
| DELETE | /api/cart/remove | 削除 | ユーザー |
| POST | /api/orders | 注文作成 | ユーザー |
| GET | /api/orders | 注文一覧 | ユーザー |
| GET | /api/orders/:id | 注文詳細 | ユーザー |
(2) エラーコード仕様
| ステータスコード | エラーコード | 説明 |
|---|---|---|
| 400 | VALIDATION_ERROR | リクエストパラメータの検証失敗 |
| 401 | AUTH_REQUIRED | 未ログイン |
| 401 | TOKEN_EXPIRED | トークン期限切れ |
| 403 | FORBIDDEN | 権限不足 |
| 404 | NOT_FOUND | リソースが存在しない |
| 409 | CONFLICT | リソースの競合 (例:メールアドレスの重複登録) |
| 429 | RATE_LIMITED | リクエストレート超過 |
| 500 | INTERNAL_ERROR | サーバー内部エラー |
(3) レスポンス形式
TYPESCRIPT
// 成功レスポンス
{
"data": { ... },
"message": "操作成功"
}
// 一覧レスポンス
{
"items": [...],
"total": 1000000,
"page": 1,
"limit": 20
}
// エラーレスポンス
{
"statusCode": 400,
"message": "Validation error",
"errorCode": "VALIDATION_ERROR",
"details": { "field": "email", "reason": "Invalid format" }
}
7. 総合例:MegaShopプロジェクト構造
TEXT
megashop/
├── nuxt.config.ts
├── prisma/
│ ├── schema.prisma
│ ├── seed.ts
│ └── migrations/
├── pages/
│ ├── index.vue
│ ├── products/
│ │ ├── index.vue
│ │ └── [id].vue
│ ├── categories/
│ │ └── [slug].vue
│ ├── cart.vue
│ ├── checkout.vue
│ ├── login.vue
│ ├── register.vue
│ ├── profile/
│ │ ├── index.vue
│ │ └── orders.vue
│ └── admin/
│ ├── index.vue
│ ├── products/
│ └── orders/
├── components/
│ ├── AppHeader.vue
│ ├── AppFooter.vue
│ ├── product/
│ │ ├── ProductCard.vue
│ │ ├── ProductGrid.vue
│ │ └── ProductReview.vue
│ ├── cart/
│ │ └── CartItem.vue
│ └── common/
│ ├── LanguageSwitcher.vue
│ └── SearchBar.vue
├── composables/
│ ├── useCart.ts
│ ├── useAnalytics.ts
│ ├── useLocalizedPrice.ts
│ └── useProductSearch.ts
├── stores/
│ ├── cart.ts
│ └── user.ts
├── server/
│ ├── utils/
│ │ ├── prisma.ts
│ │ └── jwt.ts
│ ├── middleware/
│ │ └── auth.ts
│ ├── api/
│ │ ├── auth/
│ │ ├── products/
│ │ ├── categories/
│ │ ├── cart/
│ │ └── orders/
│ └── plugins/
│ └── stock.ts
├── middleware/
│ ├── 01-auth.global.ts
│ └── admin.ts
├── plugins/
│ ├── 01-config.ts
│ ├── 02-logger.ts
│ └── 03-stripe.client.ts
├── layouts/
│ ├── default.vue
│ └── sidebar.vue
├── locales/
│ ├── en.json
│ ├── zh.json
│ └── ja.json
├── modules/
│ └── analytics/
├── tests/
│ ├── composables/
│ ├── stores/
│ ├── components/
│ └── api/
├── e2e/
│ ├── cart-flow.spec.ts
│ ├── auth-flow.spec.ts
│ └── admin-flow.spec.ts
├── Dockerfile
├── docker-compose.yml
├── .github/workflows/
│ ├── ci.yml
│ ├── deploy-staging.yml
│ └── deploy-production.yml
└── public/
├── favicon.ico
└── robots.txt
❓よくある質問
Q 設計フェーズにはどのくらいの期間が必要ですか?
A 一般的に1〜2週間です。要件分析:2日, アーキテクチャ設計:2日, データモデリング:2日, API仕様:2日, 技術選定:1日。設計が詳細であるほど, 開発はスムーズに進みます。
Q PostgreSQLとMySQLのどちらを選ぶべきですか?
A PostgreSQLがMegaShopに適しています - JSONフィールド (商品属性), 全文検索 (商品検索), 多様なインデックスタイプをサポートしています。PrismaはPostgreSQLのサポートが最も充実しています。
Q Redisは必須ですか?
A 数百万商品のページでは推奨されます。Redisは商品キャッシュとISRストレージに使用され, 応答時間は5msに対してデータベースは200msです。低トラフィックのシナリオでは, 当面スキップしても構いません。
Q APIのバージョニングはどのように管理しますか?
A URLにバージョンプレフィックス/api/v1/productsを追加し, 重大な変更でバージョン番号を増やしてください。小さな変更は後方互換性のある形でフィールドを拡張してください。
Q モジュール分割の粒度はどのように決めるべきですか?
A ビジネスドメインで分割してください - 認証, 商品, ショッピングカート, 注文それぞれ1つのモジュール。各モジュールに独自のAPI, Store, Composable, ページを持たせてください。技術層で分割しないでください。
Q 設計ドキュメントとコードの一致性はどのように確保しますか?
A Prismaスキーマをデータモデルの唯一の信頼できる情報源とし, TypeScriptインターフェースでAPI仕様を定義してください。設計変更時は, まずスキーマとインターフェースを更新し, 次にコードを更新してください。
📖まとめ
- 要件分析:3つのロール (Alice, Bob, Charlie)のコアニーズと主要指標
- アーキテクチャ:CDN → Nuxt (SSR/ISR/CSR)→ Nitro API → PostgreSQL/Redis
- データモデル:8つのコアテーブル;Productテーブルは100万行;インデックスによるクエリ最適化
- API仕様:RESTfulスタイル + 統一エラーコード + 標準レスポンス形式
- 技術スタック:Nuxt 3 + Pinia + Prisma + Redis + Docker Compose
📝練習問題
- 基本問題 (難易度:⭐):プロジェクトのユーザーロールと機能モジュールを示す図を作成してください
- 応用問題 (難易度:⭐⭐):完全なPrismaスキーマを設計してください (5つ以上のモデルを含む), インデックスと外部キーを考慮してください
- チャレンジ (難易度:⭐⭐⭐):完全なAPI仕様ドキュメントを設計してください。すべてのエンドポイント, リクエスト/レスポンス形式, エラーコードを含めてください
---|



