@einja/dev-cli 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +179 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +49 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/init.d.ts +3 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +243 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/list.d.ts +2 -0
- package/dist/commands/list.d.ts.map +1 -0
- package/dist/commands/list.js +23 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/sync.d.ts +7 -0
- package/dist/commands/sync.d.ts.map +1 -0
- package/dist/commands/sync.js +294 -0
- package/dist/commands/sync.js.map +1 -0
- package/dist/commands/sync.test.d.ts +2 -0
- package/dist/commands/sync.test.d.ts.map +1 -0
- package/dist/commands/sync.test.js +593 -0
- package/dist/commands/sync.test.js.map +1 -0
- package/dist/commands/task-loop.d.ts +11 -0
- package/dist/commands/task-loop.d.ts.map +1 -0
- package/dist/commands/task-loop.js +81 -0
- package/dist/commands/task-loop.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/file-system.d.ts +39 -0
- package/dist/lib/file-system.d.ts.map +1 -0
- package/dist/lib/file-system.js +79 -0
- package/dist/lib/file-system.js.map +1 -0
- package/dist/lib/mcp-config.d.ts +43 -0
- package/dist/lib/mcp-config.d.ts.map +1 -0
- package/dist/lib/mcp-config.js +109 -0
- package/dist/lib/mcp-config.js.map +1 -0
- package/dist/lib/mcp-config.test.d.ts +2 -0
- package/dist/lib/mcp-config.test.d.ts.map +1 -0
- package/dist/lib/mcp-config.test.js +285 -0
- package/dist/lib/mcp-config.test.js.map +1 -0
- package/dist/lib/merger.d.ts +41 -0
- package/dist/lib/merger.d.ts.map +1 -0
- package/dist/lib/merger.js +164 -0
- package/dist/lib/merger.js.map +1 -0
- package/dist/lib/preset-update/cli-repo-detector.d.ts +35 -0
- package/dist/lib/preset-update/cli-repo-detector.d.ts.map +1 -0
- package/dist/lib/preset-update/cli-repo-detector.js +83 -0
- package/dist/lib/preset-update/cli-repo-detector.js.map +1 -0
- package/dist/lib/preset-update/cli-repo-detector.test.d.ts +2 -0
- package/dist/lib/preset-update/cli-repo-detector.test.d.ts.map +1 -0
- package/dist/lib/preset-update/cli-repo-detector.test.js +120 -0
- package/dist/lib/preset-update/cli-repo-detector.test.js.map +1 -0
- package/dist/lib/preset-update/file-copier.d.ts +59 -0
- package/dist/lib/preset-update/file-copier.d.ts.map +1 -0
- package/dist/lib/preset-update/file-copier.js +220 -0
- package/dist/lib/preset-update/file-copier.js.map +1 -0
- package/dist/lib/preset-update/file-copier.test.d.ts +2 -0
- package/dist/lib/preset-update/file-copier.test.d.ts.map +1 -0
- package/dist/lib/preset-update/file-copier.test.js +297 -0
- package/dist/lib/preset-update/file-copier.test.js.map +1 -0
- package/dist/lib/preset-update/preset-finder.d.ts +39 -0
- package/dist/lib/preset-update/preset-finder.d.ts.map +1 -0
- package/dist/lib/preset-update/preset-finder.js +92 -0
- package/dist/lib/preset-update/preset-finder.js.map +1 -0
- package/dist/lib/preset-update/preset-finder.test.d.ts +2 -0
- package/dist/lib/preset-update/preset-finder.test.d.ts.map +1 -0
- package/dist/lib/preset-update/preset-finder.test.js +128 -0
- package/dist/lib/preset-update/preset-finder.test.js.map +1 -0
- package/dist/lib/preset.d.ts +14 -0
- package/dist/lib/preset.d.ts.map +1 -0
- package/dist/lib/preset.js +52 -0
- package/dist/lib/preset.js.map +1 -0
- package/dist/lib/sync/backup-manager.d.ts +50 -0
- package/dist/lib/sync/backup-manager.d.ts.map +1 -0
- package/dist/lib/sync/backup-manager.js +117 -0
- package/dist/lib/sync/backup-manager.js.map +1 -0
- package/dist/lib/sync/backup-manager.test.d.ts +2 -0
- package/dist/lib/sync/backup-manager.test.d.ts.map +1 -0
- package/dist/lib/sync/backup-manager.test.js +155 -0
- package/dist/lib/sync/backup-manager.test.js.map +1 -0
- package/dist/lib/sync/batch-processor.d.ts +27 -0
- package/dist/lib/sync/batch-processor.d.ts.map +1 -0
- package/dist/lib/sync/batch-processor.js +46 -0
- package/dist/lib/sync/batch-processor.js.map +1 -0
- package/dist/lib/sync/batch-processor.test.d.ts +2 -0
- package/dist/lib/sync/batch-processor.test.d.ts.map +1 -0
- package/dist/lib/sync/batch-processor.test.js +110 -0
- package/dist/lib/sync/batch-processor.test.js.map +1 -0
- package/dist/lib/sync/category-validator.d.ts +36 -0
- package/dist/lib/sync/category-validator.d.ts.map +1 -0
- package/dist/lib/sync/category-validator.js +46 -0
- package/dist/lib/sync/category-validator.js.map +1 -0
- package/dist/lib/sync/category-validator.test.d.ts +2 -0
- package/dist/lib/sync/category-validator.test.d.ts.map +1 -0
- package/dist/lib/sync/category-validator.test.js +89 -0
- package/dist/lib/sync/category-validator.test.js.map +1 -0
- package/dist/lib/sync/conflict-reporter.d.ts +57 -0
- package/dist/lib/sync/conflict-reporter.d.ts.map +1 -0
- package/dist/lib/sync/conflict-reporter.js +81 -0
- package/dist/lib/sync/conflict-reporter.js.map +1 -0
- package/dist/lib/sync/conflict-reporter.test.d.ts +2 -0
- package/dist/lib/sync/conflict-reporter.test.d.ts.map +1 -0
- package/dist/lib/sync/conflict-reporter.test.js +132 -0
- package/dist/lib/sync/conflict-reporter.test.js.map +1 -0
- package/dist/lib/sync/diff-engine.d.ts +28 -0
- package/dist/lib/sync/diff-engine.d.ts.map +1 -0
- package/dist/lib/sync/diff-engine.js +118 -0
- package/dist/lib/sync/diff-engine.js.map +1 -0
- package/dist/lib/sync/diff-engine.test.d.ts +2 -0
- package/dist/lib/sync/diff-engine.test.d.ts.map +1 -0
- package/dist/lib/sync/diff-engine.test.js +133 -0
- package/dist/lib/sync/diff-engine.test.js.map +1 -0
- package/dist/lib/sync/file-filter.d.ts +40 -0
- package/dist/lib/sync/file-filter.d.ts.map +1 -0
- package/dist/lib/sync/file-filter.js +171 -0
- package/dist/lib/sync/file-filter.js.map +1 -0
- package/dist/lib/sync/file-filter.test.d.ts +2 -0
- package/dist/lib/sync/file-filter.test.d.ts.map +1 -0
- package/dist/lib/sync/file-filter.test.js +179 -0
- package/dist/lib/sync/file-filter.test.js.map +1 -0
- package/dist/lib/sync/hash-cache.d.ts +34 -0
- package/dist/lib/sync/hash-cache.d.ts.map +1 -0
- package/dist/lib/sync/hash-cache.js +51 -0
- package/dist/lib/sync/hash-cache.js.map +1 -0
- package/dist/lib/sync/hash-cache.test.d.ts +2 -0
- package/dist/lib/sync/hash-cache.test.d.ts.map +1 -0
- package/dist/lib/sync/hash-cache.test.js +110 -0
- package/dist/lib/sync/hash-cache.test.js.map +1 -0
- package/dist/lib/sync/integration.test.d.ts +2 -0
- package/dist/lib/sync/integration.test.d.ts.map +1 -0
- package/dist/lib/sync/integration.test.js +317 -0
- package/dist/lib/sync/integration.test.js.map +1 -0
- package/dist/lib/sync/marker-processor.d.ts +54 -0
- package/dist/lib/sync/marker-processor.d.ts.map +1 -0
- package/dist/lib/sync/marker-processor.js +208 -0
- package/dist/lib/sync/marker-processor.js.map +1 -0
- package/dist/lib/sync/marker-processor.test.d.ts +2 -0
- package/dist/lib/sync/marker-processor.test.d.ts.map +1 -0
- package/dist/lib/sync/marker-processor.test.js +245 -0
- package/dist/lib/sync/marker-processor.test.js.map +1 -0
- package/dist/lib/sync/metadata-manager.d.ts +46 -0
- package/dist/lib/sync/metadata-manager.d.ts.map +1 -0
- package/dist/lib/sync/metadata-manager.js +129 -0
- package/dist/lib/sync/metadata-manager.js.map +1 -0
- package/dist/lib/sync/metadata-manager.test.d.ts +2 -0
- package/dist/lib/sync/metadata-manager.test.d.ts.map +1 -0
- package/dist/lib/sync/metadata-manager.test.js +137 -0
- package/dist/lib/sync/metadata-manager.test.js.map +1 -0
- package/dist/lib/sync/performance.test.d.ts +2 -0
- package/dist/lib/sync/performance.test.d.ts.map +1 -0
- package/dist/lib/sync/performance.test.js +126 -0
- package/dist/lib/sync/performance.test.js.map +1 -0
- package/dist/types/index.d.ts +59 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/preset-update.d.ts +106 -0
- package/dist/types/preset-update.d.ts.map +1 -0
- package/dist/types/preset-update.js +5 -0
- package/dist/types/preset-update.js.map +1 -0
- package/dist/types/sync.d.ts +169 -0
- package/dist/types/sync.d.ts.map +1 -0
- package/dist/types/sync.js +19 -0
- package/dist/types/sync.js.map +1 -0
- package/package.json +72 -0
- package/presets/minimal/.claude/agents/einja/docs/docs-updater.md +161 -0
- package/presets/minimal/.claude/agents/einja/frontend/design-engineer.md +685 -0
- package/presets/minimal/.claude/agents/einja/frontend/frontend-architect.md +747 -0
- package/presets/minimal/.claude/agents/einja/frontend/frontend-coder.md +441 -0
- package/presets/minimal/.claude/agents/einja/git/conflict-resolver.md +148 -0
- package/presets/minimal/.claude/agents/einja/specs/spec-design-generator.md +462 -0
- package/presets/minimal/.claude/agents/einja/specs/spec-qa-generator.md +466 -0
- package/presets/minimal/.claude/agents/einja/specs/spec-requirements-generator.md +416 -0
- package/presets/minimal/.claude/agents/einja/specs/spec-tasks-generator.md +608 -0
- package/presets/minimal/.claude/agents/einja/task/task-committer.md +82 -0
- package/presets/minimal/.claude/agents/einja/task/task-executer.md +352 -0
- package/presets/minimal/.claude/agents/einja/task/task-modification-analyzer.md +369 -0
- package/presets/minimal/.claude/agents/einja/task/task-qa.md +74 -0
- package/presets/minimal/.claude/agents/einja/task/task-reviewer.md +169 -0
- package/presets/minimal/.claude/commands/einja/frontend-implement.md +322 -0
- package/presets/minimal/.claude/commands/einja/spec-create.md +254 -0
- package/presets/minimal/.claude/commands/einja/start-dev.md +98 -0
- package/presets/minimal/.claude/commands/einja/sync-cursor-commands.md +203 -0
- package/presets/minimal/.claude/commands/einja/task-exec.md +390 -0
- package/presets/minimal/.claude/commands/einja/update-docs-by-task-specs.md +448 -0
- package/presets/minimal/.claude/hooks/einja/biome-format.sh +49 -0
- package/presets/minimal/.claude/hooks/einja/design-doc-check.sh +61 -0
- package/presets/minimal/.claude/hooks/einja/detect-secrets.sh +62 -0
- package/presets/minimal/.claude/hooks/einja/large-file-warning.sh +42 -0
- package/presets/minimal/.claude/hooks/einja/playwright-resize.sh +36 -0
- package/presets/minimal/.claude/hooks/einja/typecheck.sh +37 -0
- package/presets/minimal/.claude/hooks/einja/unset-volta-recursion.sh +32 -0
- package/presets/minimal/.claude/hooks/einja/validate-git-commit.sh +239 -0
- package/presets/minimal/.claude/hooks/einja/warn-index-ts.sh +34 -0
- package/presets/minimal/.claude/hooks/einja/warn-relative-import.sh +48 -0
- package/presets/minimal/.claude/settings.json +174 -0
- package/presets/minimal/.claude/skills/einja/api-development/SKILL.md +14 -0
- package/presets/minimal/.claude/skills/einja/backend-architecture/SKILL.md +14 -0
- package/presets/minimal/.claude/skills/einja/coding-standards/SKILL.md +120 -0
- package/presets/minimal/.claude/skills/einja/coding-standards/reference/naming-conventions.md +107 -0
- package/presets/minimal/.claude/skills/einja/coding-standards/reference/prohibited-patterns.md +169 -0
- package/presets/minimal/.claude/skills/einja/coding-standards/reference/typescript-rules.md +247 -0
- package/presets/minimal/.claude/skills/einja/component-design/SKILL.md +109 -0
- package/presets/minimal/.claude/skills/einja/component-design/reference/directory-structure.md +117 -0
- package/presets/minimal/.claude/skills/einja/component-design/reference/props-patterns.md +159 -0
- package/presets/minimal/.claude/skills/einja/component-design/reference/styling-guide.md +200 -0
- package/presets/minimal/.claude/skills/einja/conflict-resolver/SKILL.md +190 -0
- package/presets/minimal/.claude/skills/einja/frontend-development/SKILL.md +14 -0
- package/presets/minimal/.claude/skills/einja/general-context-loader/SKILL.md +254 -0
- package/presets/minimal/.claude/skills/einja/output-format/SKILL.md +137 -0
- package/presets/minimal/.claude/skills/einja/spec-context-loader/SKILL.md +177 -0
- package/presets/minimal/.claude/skills/einja/task-commit/SKILL.md +269 -0
- package/presets/minimal/.claude/skills/einja/task-qa/SKILL.md +306 -0
- package/presets/minimal/.claude/skills/einja/task-qa/reference/failure-patterns.md +69 -0
- package/presets/minimal/.claude/skills/einja/task-qa/reference/troubleshooting.md +65 -0
- package/presets/minimal/.claude/skills/einja/task-qa/reference/usage-patterns.md +52 -0
- package/presets/minimal/.claude/skills/einja/task-qa/templates/qa-test-template.md +128 -0
- package/presets/minimal/preset.yaml +111 -0
- package/presets/minimal/symlinks.json +45 -0
- package/scaffolds/.mcp.json +45 -0
- package/scaffolds/CLAUDE.md.template +318 -0
- package/scaffolds/steering/README.md +170 -0
- package/scaffolds/steering/acceptance-criteria-and-qa-guide.md +415 -0
- package/scaffolds/steering/architecture.md +481 -0
- package/scaffolds/steering/branch-strategy.md +362 -0
- package/scaffolds/steering/commit-rules.md +217 -0
- package/scaffolds/steering/db-schema-design.md +609 -0
- package/scaffolds/steering/development/api-development.md +783 -0
- package/scaffolds/steering/development/backend-architecture.md +731 -0
- package/scaffolds/steering/development/frontend-development.md +1537 -0
- package/scaffolds/steering/development/review-guidelines.md +365 -0
- package/scaffolds/steering/development/testing-strategy.md +819 -0
- package/scaffolds/steering/development-workflow.md +429 -0
- package/scaffolds/steering/infrastructure/deployment.md +277 -0
- package/scaffolds/steering/infrastructure/environment-variables.md +298 -0
- package/scaffolds/steering/product.md +540 -0
- package/scaffolds/steering/task-management.md +367 -0
- package/templates/README.md +159 -0
- package/templates/design-simple.md.template +172 -0
- package/templates/design.md.template +327 -0
- package/templates/qa-test.md.template +125 -0
- package/templates/requirements.md.template +254 -0
|
@@ -0,0 +1,731 @@
|
|
|
1
|
+
# バックエンドアーキテクチャ
|
|
2
|
+
|
|
3
|
+
## タスク: Turborepo Next.js モノレポ構築 (20251104)
|
|
4
|
+
|
|
5
|
+
**反映日時**: 2025-11-17
|
|
6
|
+
**ソース**: docs/specs/tasks/monorepo/20251104-monorepo-turborepo-nextjs-setup/
|
|
7
|
+
**抽出元**: design/architecture.md, design/implementation.md
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## 概要
|
|
12
|
+
|
|
13
|
+
Vercel TurborepoとNext.jsをベースとしたエンタープライズグレードのモノレポアーキテクチャです。
|
|
14
|
+
|
|
15
|
+
複数のアプリケーション(Web、Admin、Cron Worker)と1つの共有パッケージ(@repo/server-core)を統合し、**4層レイヤードアーキテクチャ**と**Result型パターン**による型安全なバックエンド開発を実現します。
|
|
16
|
+
|
|
17
|
+
### 主要な技術的課題と解決方針
|
|
18
|
+
|
|
19
|
+
1. **コードの重複と保守性**
|
|
20
|
+
- 解決策: @repo/server-core による DRY 原則の徹底
|
|
21
|
+
- Repositoryパターンでドメイン層とインフラ層を分離
|
|
22
|
+
- Mapperパターンで Prisma ⇔ Domain の変換を Infrastructure層に集約
|
|
23
|
+
|
|
24
|
+
2. **型安全性とエラーハンドリング**
|
|
25
|
+
- 解決策: Result型パターンによる例外を使わないエラーハンドリング
|
|
26
|
+
- Hono + zValidatorによるリクエストバリデーションの型安全性
|
|
27
|
+
- ApplicationErrorクラス階層による構造化されたエラー表現
|
|
28
|
+
|
|
29
|
+
3. **モジュールエクスポート管理**
|
|
30
|
+
- 解決策: **index.ts完全不使用方針**
|
|
31
|
+
- package.jsonのワイルドカードexports (`"./*": "./src/*.ts"`)
|
|
32
|
+
- ファイル追加時のpackage.json更新不要(自動対応)
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## 1. ディレクトリ構造
|
|
37
|
+
|
|
38
|
+
### モノレポ全体構造
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
project-root/
|
|
42
|
+
├── apps/
|
|
43
|
+
│ ├── web/ # メインWebアプリ(Next.js 14 App Router)
|
|
44
|
+
│ ├── admin/ # 管理画面(Next.js 14 App Router)
|
|
45
|
+
│ └── cron-worker/ # バッチ処理(CLI型)
|
|
46
|
+
│
|
|
47
|
+
├── packages/
|
|
48
|
+
│ └── server-core/ # 共有バックエンドロジック⭐
|
|
49
|
+
│ ├── core/ # アーキテクチャのコア(Result型等)
|
|
50
|
+
│ ├── domain/ # Domain層
|
|
51
|
+
│ └── infrastructure/ # Infrastructure層
|
|
52
|
+
│
|
|
53
|
+
├── apps/
|
|
54
|
+
│ ├── web/src/application/ # Application層(webアプリ固有)⭐
|
|
55
|
+
│ ├── admin/src/application/ # Application層(adminアプリ固有)⭐
|
|
56
|
+
│ └── cron-worker/src/application/ # Application層(cron-worker固有)⭐
|
|
57
|
+
│
|
|
58
|
+
├── biome.json # ルートLinter設定
|
|
59
|
+
├── tsconfig.base.json # ベースTS設定
|
|
60
|
+
├── turbo.json # Turborepo設定
|
|
61
|
+
├── pnpm-workspace.yaml # ワークスペース定義
|
|
62
|
+
└── docker-compose.yml # PostgreSQL動的ポート設定
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### apps/ の役割
|
|
66
|
+
|
|
67
|
+
| アプリ | ポート | 用途 | 技術スタック |
|
|
68
|
+
|-------|-------|------|------------|
|
|
69
|
+
| **web** | 3000 | メインWebアプリケーション | Next.js 14 App Router + Hono API |
|
|
70
|
+
| **admin** | 4000 | 管理画面 | Next.js 14 App Router + Hono API |
|
|
71
|
+
| **cron-worker** | 5000 | バッチ処理(CLI型) | Next.js + tsx実行 |
|
|
72
|
+
|
|
73
|
+
**Worktree環境対応**: ブランチ名のMD5ハッシュから動的にポート番号を計算し、複数ブランチの並行開発をサポート。
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
### @repo/server-core の内部構造(4層アーキテクチャ)
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
packages/server-core/
|
|
81
|
+
├── src/
|
|
82
|
+
│ ├── domain/ # 📗 Domain層(ビジネスロジック)
|
|
83
|
+
│ │ ├── entities/ # エンティティ
|
|
84
|
+
│ │ │ ├── User.ts
|
|
85
|
+
│ │ │ ├── Post.ts
|
|
86
|
+
│ │ │ └── Session.ts
|
|
87
|
+
│ │ │
|
|
88
|
+
│ │ ├── value-objects/ # 値オブジェクト
|
|
89
|
+
│ │ │ ├── Email.ts
|
|
90
|
+
│ │ │ └── Password.ts
|
|
91
|
+
│ │ │
|
|
92
|
+
│ │ ├── repository-interfaces/ # リポジトリインターフェース⭐
|
|
93
|
+
│ │ │ ├── IUserRepository.ts
|
|
94
|
+
│ │ │ ├── IPostRepository.ts
|
|
95
|
+
│ │ │ └── ISessionRepository.ts
|
|
96
|
+
│ │ │
|
|
97
|
+
│ │ └── validators/ # Zodバリデーター
|
|
98
|
+
│ │ ├── user.ts
|
|
99
|
+
│ │ ├── post.ts
|
|
100
|
+
│ │ └── session.ts
|
|
101
|
+
│ │
|
|
102
|
+
│ ├── infrastructure/ # 📙 Infrastructure層(実装)
|
|
103
|
+
│ │ ├── database/
|
|
104
|
+
│ │ │ ├── prisma/
|
|
105
|
+
│ │ │ │ ├── schema.prisma
|
|
106
|
+
│ │ │ │ └── migrations/
|
|
107
|
+
│ │ │ │
|
|
108
|
+
│ │ │ ├── client.ts # Prismaクライアント⭐
|
|
109
|
+
│ │ │ │
|
|
110
|
+
│ │ │ ├── repositories/ # リポジトリ実装⭐
|
|
111
|
+
│ │ │ │ ├── UserRepository.ts
|
|
112
|
+
│ │ │ │ ├── PostRepository.ts
|
|
113
|
+
│ │ │ │ └── SessionRepository.ts
|
|
114
|
+
│ │ │ │
|
|
115
|
+
│ │ │ └── mappers/ # Prisma ⇔ Domain変換⭐
|
|
116
|
+
│ │ │ ├── UserMapper.ts
|
|
117
|
+
│ │ │ ├── PostMapper.ts
|
|
118
|
+
│ │ │ └── SessionMapper.ts
|
|
119
|
+
│ │ │
|
|
120
|
+
│ │ ├── email/ # メール送信
|
|
121
|
+
│ │ │ ├── EmailService.ts
|
|
122
|
+
│ │ │ └── ResendEmailService.ts
|
|
123
|
+
│ │ │
|
|
124
|
+
│ │ └── storage/ # ストレージ
|
|
125
|
+
│ │ ├── StorageService.ts
|
|
126
|
+
│ │ └── S3StorageService.ts
|
|
127
|
+
│ │
|
|
128
|
+
│ └── core/ # アーキテクチャのコア
|
|
129
|
+
│ └── result.ts # Result型定義⭐
|
|
130
|
+
│
|
|
131
|
+
└── package.json
|
|
132
|
+
└── "exports": { "./*": "./src/*.ts" } # index.ts不使用⭐
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## 2. 4層レイヤードアーキテクチャ
|
|
138
|
+
|
|
139
|
+
### アーキテクチャ図
|
|
140
|
+
|
|
141
|
+
```mermaid
|
|
142
|
+
graph TD
|
|
143
|
+
subgraph "Frontend (React)"
|
|
144
|
+
UI[UI Components]
|
|
145
|
+
TQ[Tanstack Query]
|
|
146
|
+
HC[Hono Client]
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
subgraph "📕 Presentation層 (API Routes)"
|
|
150
|
+
Router[Hono Router]
|
|
151
|
+
Validator[zValidator + Zod]
|
|
152
|
+
Handler[Route Handler]
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
subgraph "📘 Application層 (UseCases)"
|
|
156
|
+
UC[UseCase<br/>Object Literal]
|
|
157
|
+
ResultCompose[Result Composition<br/>flatMap / map]
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
subgraph "📗 Domain層"
|
|
161
|
+
Entity[Domain Entities]
|
|
162
|
+
VO[Value Objects]
|
|
163
|
+
RepoIF[Repository<br/>Interfaces ⭐]
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
subgraph "📙 Infrastructure層"
|
|
167
|
+
Mapper[Mapper Classes<br/>Prisma ⇔ Domain]
|
|
168
|
+
RepoImpl[Repository<br/>Implementation]
|
|
169
|
+
PrismaClient[Prisma Client]
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
subgraph "Database"
|
|
173
|
+
DB[(PostgreSQL)]
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
UI --> TQ
|
|
177
|
+
TQ --> HC
|
|
178
|
+
HC --> Router
|
|
179
|
+
Router --> Validator
|
|
180
|
+
Validator --> Handler
|
|
181
|
+
Handler --> UC
|
|
182
|
+
UC --> ResultCompose
|
|
183
|
+
ResultCompose --> RepoIF
|
|
184
|
+
RepoIF -.implements.-> RepoImpl
|
|
185
|
+
RepoImpl --> Mapper
|
|
186
|
+
RepoImpl --> PrismaClient
|
|
187
|
+
Mapper --> Entity
|
|
188
|
+
UC --> Entity
|
|
189
|
+
PrismaClient --> DB
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### 各層の責務と配置
|
|
193
|
+
|
|
194
|
+
#### 📕 Presentation層(API Routes)
|
|
195
|
+
|
|
196
|
+
**配置**: `apps/web/src/app/api/`, `apps/admin/src/app/api/`
|
|
197
|
+
|
|
198
|
+
**責務**:
|
|
199
|
+
- HTTPリクエスト/レスポンスの処理
|
|
200
|
+
- Zodバリデーション(zValidator)
|
|
201
|
+
- UseCaseの呼び出し
|
|
202
|
+
- エラーのHTTPステータスコードへのマッピング
|
|
203
|
+
|
|
204
|
+
**技術**: Hono、zValidator、Zod
|
|
205
|
+
|
|
206
|
+
**実装例**:
|
|
207
|
+
```typescript
|
|
208
|
+
// apps/web/src/app/api/posts/route.ts
|
|
209
|
+
import { Hono } from "hono"
|
|
210
|
+
import { zValidator } from "@hono/zod-validator"
|
|
211
|
+
import { postSchema } from "@repo/server-core/domain/validators/post"
|
|
212
|
+
import { postUseCases } from "@/application/use-cases/PostUseCases" // アプリ内のApplication層
|
|
213
|
+
|
|
214
|
+
const app = new Hono()
|
|
215
|
+
.post("/", zValidator("json", postSchema), async (c) => {
|
|
216
|
+
const data = c.req.valid("json")
|
|
217
|
+
const result = await postUseCases.create(data)
|
|
218
|
+
|
|
219
|
+
if (!result.isSuccess) {
|
|
220
|
+
return c.json({ error: result.error.message }, result.error.statusCode)
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return c.json(result.value, 201)
|
|
224
|
+
})
|
|
225
|
+
|
|
226
|
+
export const GET = app.fetch
|
|
227
|
+
export const POST = app.fetch
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
#### 📘 Application層(UseCases)
|
|
233
|
+
|
|
234
|
+
**配置**: `apps/*/src/application/use-cases/` (各アプリケーション固有)⭐
|
|
235
|
+
|
|
236
|
+
**重要**: Application層は各アプリケーション(web、admin、cron-worker)に配置します。@repo/server-coreには配置しません。
|
|
237
|
+
|
|
238
|
+
**責務**:
|
|
239
|
+
- ビジネスフロー(複数Repositoryの調整)
|
|
240
|
+
- トランザクション管理
|
|
241
|
+
- Result型による安全なエラー伝播
|
|
242
|
+
|
|
243
|
+
**技術**: Result型、UseCase統合パターン
|
|
244
|
+
|
|
245
|
+
**設計パターン: UseCase統合パターン**
|
|
246
|
+
|
|
247
|
+
従来のCRUD操作ごとにファイルを分けるのではなく、**リソース単位で1ファイルに統合**します。
|
|
248
|
+
|
|
249
|
+
```typescript
|
|
250
|
+
// ❌ 旧パターン: CRUD操作ごとにファイル分割(過度な細分化)
|
|
251
|
+
// - ListPostsUseCase.ts
|
|
252
|
+
// - CreatePostUseCase.ts
|
|
253
|
+
// - UpdatePostUseCase.ts
|
|
254
|
+
// - DeletePostUseCase.ts
|
|
255
|
+
|
|
256
|
+
// ✅ 新パターン: リソース単位で統合(シンプル)
|
|
257
|
+
// apps/web/src/application/use-cases/PostUseCases.ts
|
|
258
|
+
|
|
259
|
+
// 型定義例
|
|
260
|
+
export type PostSearchCriteria = {
|
|
261
|
+
userId?: string
|
|
262
|
+
published?: boolean
|
|
263
|
+
createdAfter?: Date
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
export type CreatePostInput = {
|
|
267
|
+
title: string
|
|
268
|
+
content: string
|
|
269
|
+
userId: string
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
export type UpdatePostInput = {
|
|
273
|
+
title?: string
|
|
274
|
+
content?: string
|
|
275
|
+
published?: boolean
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
export const postUseCases = {
|
|
279
|
+
list: async (criteria: PostSearchCriteria) => {
|
|
280
|
+
// ...
|
|
281
|
+
},
|
|
282
|
+
|
|
283
|
+
create: async (data: CreatePostInput) => {
|
|
284
|
+
// ...
|
|
285
|
+
},
|
|
286
|
+
|
|
287
|
+
update: async (id: string, data: UpdatePostInput) => {
|
|
288
|
+
// ...
|
|
289
|
+
},
|
|
290
|
+
|
|
291
|
+
delete: async (id: string) => {
|
|
292
|
+
// ...
|
|
293
|
+
},
|
|
294
|
+
}
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
**メリット**:
|
|
298
|
+
- 各ファイル150行程度で十分に可読性が高い
|
|
299
|
+
- 関連操作が1箇所にまとまり、変更が容易
|
|
300
|
+
- 呼び出しがシンプル: `postUseCases.create()` vs `createPostUseCase(repo).execute()`
|
|
301
|
+
|
|
302
|
+
---
|
|
303
|
+
|
|
304
|
+
#### 📗 Domain層(ビジネスロジック)
|
|
305
|
+
|
|
306
|
+
**配置**: `packages/server-core/src/domain/`
|
|
307
|
+
|
|
308
|
+
**責務**:
|
|
309
|
+
- ビジネスルールの定義
|
|
310
|
+
- エンティティと値オブジェクトの管理
|
|
311
|
+
- リポジトリインターフェースの定義⭐
|
|
312
|
+
- ドメインバリデーション(Zod)
|
|
313
|
+
|
|
314
|
+
**技術**: TypeScript、Zod
|
|
315
|
+
|
|
316
|
+
**重要な原則**:
|
|
317
|
+
- **インフラ層に依存しない**(Prismaを知らない)
|
|
318
|
+
- リポジトリは**インターフェース**のみ定義
|
|
319
|
+
- データベースの実装詳細から独立
|
|
320
|
+
|
|
321
|
+
**実装例: Entity**
|
|
322
|
+
|
|
323
|
+
```typescript
|
|
324
|
+
// packages/server-core/src/domain/entities/User.ts
|
|
325
|
+
export class User {
|
|
326
|
+
constructor(
|
|
327
|
+
public readonly id: string,
|
|
328
|
+
public readonly email: Email, // 値オブジェクト
|
|
329
|
+
public readonly name: string,
|
|
330
|
+
public readonly createdAt: Date,
|
|
331
|
+
) {}
|
|
332
|
+
|
|
333
|
+
// ビジネスロジック
|
|
334
|
+
canDelete(): boolean {
|
|
335
|
+
// 作成後30日以内は削除不可
|
|
336
|
+
const daysSinceCreation = (Date.now() - this.createdAt.getTime()) / (1000 * 60 * 60 * 24)
|
|
337
|
+
return daysSinceCreation > 30
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
**実装例: Repository Interface**
|
|
343
|
+
|
|
344
|
+
```typescript
|
|
345
|
+
// packages/server-core/src/domain/repository-interfaces/IUserRepository.ts
|
|
346
|
+
|
|
347
|
+
// SearchCriteria型: すべてのフィールドはオプショナル
|
|
348
|
+
export type UserSearchCriteria = {
|
|
349
|
+
id?: string
|
|
350
|
+
email?: string
|
|
351
|
+
name?: string
|
|
352
|
+
createdAfter?: Date
|
|
353
|
+
createdBefore?: Date
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
export interface IUserRepository {
|
|
357
|
+
find(criteria: UserSearchCriteria): Promise<Result<User | null, DatabaseError>>
|
|
358
|
+
search(criteria: UserSearchCriteria): Promise<Result<User[], DatabaseError>>
|
|
359
|
+
create(user: User): Promise<Result<User, DatabaseError>>
|
|
360
|
+
update(id: string, user: Partial<User>): Promise<Result<User, DatabaseError>>
|
|
361
|
+
delete(id: string): Promise<Result<void, DatabaseError>>
|
|
362
|
+
}
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
---
|
|
366
|
+
|
|
367
|
+
#### 📙 Infrastructure層(実装)
|
|
368
|
+
|
|
369
|
+
**配置**: `packages/server-core/src/infrastructure/`
|
|
370
|
+
|
|
371
|
+
**責務**:
|
|
372
|
+
- データベースアクセス(Prisma)
|
|
373
|
+
- 外部サービス連携(メール、ストレージ)
|
|
374
|
+
- リポジトリインターフェースの**実装**⭐
|
|
375
|
+
- Prismaモデル ⇔ Domainエンティティの変換(Mapper)⭐
|
|
376
|
+
|
|
377
|
+
**技術**: Prisma、Mapper、外部API
|
|
378
|
+
|
|
379
|
+
**実装例: Repository Implementation**
|
|
380
|
+
|
|
381
|
+
```typescript
|
|
382
|
+
// packages/server-core/src/infrastructure/database/repositories/UserRepository.ts
|
|
383
|
+
import type { IUserRepository, UserSearchCriteria } from "@repo/server-core/domain/repository-interfaces/IUserRepository"
|
|
384
|
+
import { UserMapper } from "../mappers/UserMapper"
|
|
385
|
+
import { prisma } from "../client"
|
|
386
|
+
|
|
387
|
+
export const userRepository: IUserRepository = {
|
|
388
|
+
find: async (criteria: UserSearchCriteria) => {
|
|
389
|
+
const prismaUser = await prisma.user.findFirst({
|
|
390
|
+
where: {
|
|
391
|
+
id: criteria.id,
|
|
392
|
+
email: criteria.email,
|
|
393
|
+
createdAt: {
|
|
394
|
+
gte: criteria.createdAfter,
|
|
395
|
+
lte: criteria.createdBefore,
|
|
396
|
+
},
|
|
397
|
+
},
|
|
398
|
+
})
|
|
399
|
+
|
|
400
|
+
if (!prismaUser) {
|
|
401
|
+
return { isSuccess: true, value: null }
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
const user = UserMapper.toDomain(prismaUser)
|
|
405
|
+
return { isSuccess: true, value: user }
|
|
406
|
+
},
|
|
407
|
+
|
|
408
|
+
create: async (user) => {
|
|
409
|
+
const createInput = UserMapper.toPrismaCreate(user)
|
|
410
|
+
const prismaUser = await prisma.user.create({ data: createInput })
|
|
411
|
+
const domainUser = UserMapper.toDomain(prismaUser)
|
|
412
|
+
return { isSuccess: true, value: domainUser }
|
|
413
|
+
},
|
|
414
|
+
|
|
415
|
+
// ...
|
|
416
|
+
}
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
**実装例: Mapper**
|
|
420
|
+
|
|
421
|
+
```typescript
|
|
422
|
+
// packages/server-core/src/infrastructure/database/mappers/UserMapper.ts
|
|
423
|
+
import type { User as PrismaUser } from "@prisma/client"
|
|
424
|
+
import { User } from "@repo/server-core/domain/entities/User"
|
|
425
|
+
import { Email } from "@repo/server-core/domain/value-objects/Email"
|
|
426
|
+
|
|
427
|
+
export class UserMapper {
|
|
428
|
+
static toDomain(prismaUser: PrismaUser): User {
|
|
429
|
+
return new User(
|
|
430
|
+
prismaUser.id,
|
|
431
|
+
new Email(prismaUser.email),
|
|
432
|
+
prismaUser.name,
|
|
433
|
+
prismaUser.createdAt,
|
|
434
|
+
)
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
static toPrismaCreate(user: User): Prisma.UserCreateInput {
|
|
438
|
+
return {
|
|
439
|
+
id: user.id,
|
|
440
|
+
email: user.email.value,
|
|
441
|
+
name: user.name,
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
static toPrismaUpdate(user: Partial<User>): Prisma.UserUpdateInput {
|
|
446
|
+
return {
|
|
447
|
+
email: user.email?.value,
|
|
448
|
+
name: user.name,
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
---
|
|
455
|
+
|
|
456
|
+
### 層間の依存関係ルール
|
|
457
|
+
|
|
458
|
+
```
|
|
459
|
+
上位層 → 下位層のみ依存可能
|
|
460
|
+
|
|
461
|
+
Presentation → Application → Domain ← Infrastructure
|
|
462
|
+
↑
|
|
463
|
+
インターフェースに依存
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
**重要な原則**:
|
|
467
|
+
1. **Presentation層**: Application層のUseCaseを呼び出す
|
|
468
|
+
2. **Application層**: Domain層のエンティティとリポジトリ**インターフェース**を使用
|
|
469
|
+
3. **Domain層**: どの層にも依存しない(最も純粋)
|
|
470
|
+
4. **Infrastructure層**: Domain層の**インターフェース**を実装
|
|
471
|
+
|
|
472
|
+
この設計により:
|
|
473
|
+
- ✅ テストが容易(モックRepositoryで差し替え可能)
|
|
474
|
+
- ✅ データベースの変更がドメイン層に影響しない
|
|
475
|
+
- ✅ ビジネスロジックが永続化の詳細から独立
|
|
476
|
+
|
|
477
|
+
---
|
|
478
|
+
|
|
479
|
+
## 3. デザインパターン
|
|
480
|
+
|
|
481
|
+
### 3.1 Repositoryパターン
|
|
482
|
+
|
|
483
|
+
**目的**: データアクセスロジックの抽象化
|
|
484
|
+
|
|
485
|
+
**設計の特徴**:
|
|
486
|
+
- **検索条件ベース設計**: `find(criteria)`, `search(criteria)` で統一
|
|
487
|
+
- **Result型**: すべてのメソッドがResult型を返す
|
|
488
|
+
- **SearchCriteria**: 柔軟な検索条件(すべてのフィールドはオプショナル)
|
|
489
|
+
|
|
490
|
+
**SearchCriteria型の設計原則**:
|
|
491
|
+
```typescript
|
|
492
|
+
// ✅ すべてのフィールドはオプショナル
|
|
493
|
+
export type UserSearchCriteria = {
|
|
494
|
+
id?: string
|
|
495
|
+
email?: string
|
|
496
|
+
name?: string
|
|
497
|
+
createdAfter?: Date
|
|
498
|
+
createdBefore?: Date
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
// ❌ 必須フィールドを設けない
|
|
502
|
+
export type UserSearchCriteria = {
|
|
503
|
+
email: string // NG: 必須にすると柔軟性が失われる
|
|
504
|
+
name?: string
|
|
505
|
+
}
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
**重要な原則**:
|
|
509
|
+
- すべての検索条件フィールドは**オプショナル**とする
|
|
510
|
+
- これにより、同一のRepositoryメソッドで多様な検索パターンに対応可能
|
|
511
|
+
- 必須パラメータはメソッドの引数として別途定義する(例: `update(id: string, data)`)
|
|
512
|
+
|
|
513
|
+
**主要メソッド**:
|
|
514
|
+
|
|
515
|
+
| メソッド | 説明 | 返り値 |
|
|
516
|
+
|---------|------|--------|
|
|
517
|
+
| `find(criteria)` | 単一レコード検索 | `Result<T \| null, E>` |
|
|
518
|
+
| `search(criteria, options)` | 複数レコード検索 | `Result<T[], E>` |
|
|
519
|
+
| `create(entity)` | 作成 | `Result<T, E>` |
|
|
520
|
+
| `update(id, data)` | 更新 | `Result<T, E>` |
|
|
521
|
+
| `delete(id)` | 削除 | `Result<void, E>` |
|
|
522
|
+
| `exists(criteria)` | 存在確認 | `Result<boolean, E>` |
|
|
523
|
+
| `count(criteria)` | カウント | `Result<number, E>` |
|
|
524
|
+
|
|
525
|
+
---
|
|
526
|
+
|
|
527
|
+
### 3.2 Mapperパターン
|
|
528
|
+
|
|
529
|
+
**目的**: Prismaモデル ⇔ Domainエンティティの変換
|
|
530
|
+
|
|
531
|
+
**設計のポイント**:
|
|
532
|
+
- Infrastructure層に配置
|
|
533
|
+
- 変換ロジックを一箇所に集約
|
|
534
|
+
- Domain層をPrismaの実装詳細から保護
|
|
535
|
+
|
|
536
|
+
**変換方向**:
|
|
537
|
+
1. **toDomain**: PrismaモデルからDomainエンティティへ
|
|
538
|
+
2. **toPrismaCreate**: DomainエンティティからPrisma CreateInputへ
|
|
539
|
+
3. **toPrismaUpdate**: DomainエンティティからPrisma UpdateInputへ
|
|
540
|
+
|
|
541
|
+
---
|
|
542
|
+
|
|
543
|
+
### 3.3 Result型パターン
|
|
544
|
+
|
|
545
|
+
**目的**: 例外を使わないエラー表現
|
|
546
|
+
|
|
547
|
+
**型定義**:
|
|
548
|
+
```typescript
|
|
549
|
+
// packages/server-core/src/core/result.ts
|
|
550
|
+
type Success<T> = { isSuccess: true; value: T }
|
|
551
|
+
type Failure<E> = { isSuccess: false; error: E }
|
|
552
|
+
type Result<T, E> = Success<T> | Failure<E>
|
|
553
|
+
|
|
554
|
+
// ヘルパー関数
|
|
555
|
+
export function success<T>(value: T): Success<T> {
|
|
556
|
+
return { isSuccess: true, value }
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
export function failure<E>(error: E): Failure<E> {
|
|
560
|
+
return { isSuccess: false, error }
|
|
561
|
+
}
|
|
562
|
+
```
|
|
563
|
+
|
|
564
|
+
**使用例**:
|
|
565
|
+
```typescript
|
|
566
|
+
// UseCase
|
|
567
|
+
const userResult = await userRepository.find({ email })
|
|
568
|
+
if (!userResult.isSuccess) {
|
|
569
|
+
return failure(userResult.error) // エラーを伝播
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
const user = userResult.value
|
|
573
|
+
// 型安全: userResult.isSuccessのチェック後は、user は User型として扱える
|
|
574
|
+
```
|
|
575
|
+
|
|
576
|
+
**メリット**:
|
|
577
|
+
- ✅ 型レベルでエラーハンドリングを強制
|
|
578
|
+
- ✅ try-catchが不要
|
|
579
|
+
- ✅ flatMap/mapでエラーをチェーン可能
|
|
580
|
+
|
|
581
|
+
---
|
|
582
|
+
|
|
583
|
+
## 4. パッケージエクスポート戦略
|
|
584
|
+
|
|
585
|
+
### ⛔ index.ts 完全禁止(絶対厳守)
|
|
586
|
+
|
|
587
|
+
> **警告**: このプロジェクトでは `index.ts` ファイルの作成を**一切禁止**しています。
|
|
588
|
+
> いかなる理由があっても `index.ts` を作成してはいけません。
|
|
589
|
+
> 詳細は [CLAUDE.md の index.ts 完全禁止ルール](../../../CLAUDE.md) を参照してください。
|
|
590
|
+
|
|
591
|
+
従来のindex.tsパターンは、ファイル追加のたびにindex.tsとpackage.jsonの両方を更新する必要があり、メンテナンス負担が大きいため、**完全に禁止**しています。
|
|
592
|
+
|
|
593
|
+
**package.json設定**:
|
|
594
|
+
```json
|
|
595
|
+
{
|
|
596
|
+
"exports": {
|
|
597
|
+
"./*": "./src/*.ts"
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
```
|
|
601
|
+
|
|
602
|
+
**インポート例**:
|
|
603
|
+
```typescript
|
|
604
|
+
// ✅ 推奨: 直接ファイルパス指定
|
|
605
|
+
import { User } from "@repo/server-core/domain/entities/User"
|
|
606
|
+
import { userRepository } from "@repo/server-core/infrastructure/database/repositories/UserRepository"
|
|
607
|
+
import { postUseCases } from "@/application/use-cases/PostUseCases" // Application層は各アプリ内
|
|
608
|
+
|
|
609
|
+
// ❌ 非推奨: index.ts経由(使用不可)
|
|
610
|
+
import { User } from "@repo/server-core"
|
|
611
|
+
import { User } from "@repo/server-core/domain/entities" // index.tsなし
|
|
612
|
+
```
|
|
613
|
+
|
|
614
|
+
**メリット**:
|
|
615
|
+
- ✅ ファイル追加時にpackage.json更新不要(ワイルドカードで自動対応)
|
|
616
|
+
- ✅ index.tsの管理コストゼロ
|
|
617
|
+
- ✅ インポート元が明確
|
|
618
|
+
- ✅ IDEのジャンプ機能が正確に動作
|
|
619
|
+
|
|
620
|
+
---
|
|
621
|
+
|
|
622
|
+
## 5. 技術スタック
|
|
623
|
+
|
|
624
|
+
| カテゴリ | 技術 | バージョン | 用途 |
|
|
625
|
+
|---------|------|-----------|------|
|
|
626
|
+
| **モノレポ管理** | Turborepo | 1.x | ビルドオーケストレーション |
|
|
627
|
+
| **パッケージマネージャー** | pnpm | 8.x | ワークスペース管理 |
|
|
628
|
+
| **フレームワーク** | Next.js | 14.x | Web/Admin/Cron Worker |
|
|
629
|
+
| **APIフレームワーク** | Hono | 4.x | 型安全なWebフレームワーク |
|
|
630
|
+
| **言語** | TypeScript | 5.x | 型安全性 |
|
|
631
|
+
| **環境変数管理** | dotenv-cli | 7.3.0 | 階層的env読み込み |
|
|
632
|
+
| **データベース** | Prisma | 5.x | ORM |
|
|
633
|
+
| **DB本体** | PostgreSQL | 15.x | データストア |
|
|
634
|
+
| **Linter & Formatter** | Biome | 1.9.4+ | コード品質・フォーマット |
|
|
635
|
+
| **バリデーション** | Zod | 3.x | スキーマ検証 |
|
|
636
|
+
| **日付処理** | date-fns | 3.x | 日付ユーティリティ |
|
|
637
|
+
|
|
638
|
+
---
|
|
639
|
+
|
|
640
|
+
## 6. Prisma Client設定
|
|
641
|
+
|
|
642
|
+
### グローバル化パターン(Hot Reload対応)
|
|
643
|
+
|
|
644
|
+
**配置**: `packages/server-core/src/infrastructure/database/client.ts`
|
|
645
|
+
|
|
646
|
+
**設計の要点**:
|
|
647
|
+
- 開発環境でのHot Reload時にPrismaクライアントの再作成を防ぐ
|
|
648
|
+
- `global`オブジェクトにPrismaインスタンスをキャッシュ
|
|
649
|
+
- ログレベルを環境別に設定
|
|
650
|
+
|
|
651
|
+
```typescript
|
|
652
|
+
// packages/server-core/src/infrastructure/database/client.ts
|
|
653
|
+
import { PrismaClient } from "@prisma/client"
|
|
654
|
+
|
|
655
|
+
const globalForPrisma = global as unknown as { prisma: PrismaClient }
|
|
656
|
+
|
|
657
|
+
export const prisma =
|
|
658
|
+
globalForPrisma.prisma ||
|
|
659
|
+
new PrismaClient({
|
|
660
|
+
log: process.env.NODE_ENV === "development" ? ["query", "error", "warn"] : ["error"],
|
|
661
|
+
})
|
|
662
|
+
|
|
663
|
+
if (process.env.NODE_ENV !== "production") globalForPrisma.prisma = prisma
|
|
664
|
+
```
|
|
665
|
+
|
|
666
|
+
---
|
|
667
|
+
|
|
668
|
+
## 7. 環境変数管理
|
|
669
|
+
|
|
670
|
+
### 階層的環境変数読み込み
|
|
671
|
+
|
|
672
|
+
```
|
|
673
|
+
ルート.env (共通設定、コミット可能)
|
|
674
|
+
↓
|
|
675
|
+
ルート.env.local (ローカル固有、gitignore)
|
|
676
|
+
↓
|
|
677
|
+
各アプリ.env.local (アプリ固有、gitignore、オプション)
|
|
678
|
+
```
|
|
679
|
+
|
|
680
|
+
### dotenv-cli による自動読み込み
|
|
681
|
+
|
|
682
|
+
**ルートpackage.json**:
|
|
683
|
+
```json
|
|
684
|
+
{
|
|
685
|
+
"scripts": {
|
|
686
|
+
"dev": "dotenv -e .env -e .env.local -- turbo run dev"
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
```
|
|
690
|
+
|
|
691
|
+
**各アプリのpackage.json**:
|
|
692
|
+
```json
|
|
693
|
+
{
|
|
694
|
+
"scripts": {
|
|
695
|
+
"dev": "dotenv -e ../../.env -e ../../.env.local -e .env.local -- next dev"
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
```
|
|
699
|
+
|
|
700
|
+
---
|
|
701
|
+
|
|
702
|
+
## 8. 参照ドキュメント
|
|
703
|
+
|
|
704
|
+
### 開発ガイド
|
|
705
|
+
- **[API開発ガイド](api-development.md)** - Hono API実装ルール、エンドポイント設計、**Server Actions vs Hono Clientの使い分け**
|
|
706
|
+
- **[フロントエンド開発ガイド](frontend-development.md)** - Server/Client Component、Tanstack Query、React Hook Form、Hono Client
|
|
707
|
+
|
|
708
|
+
### データベース
|
|
709
|
+
- **[スキーマ設計](../schema-design.md)** - Prismaスキーマ、テーブル定義、ERD
|
|
710
|
+
|
|
711
|
+
### インフラ
|
|
712
|
+
- **[CI/CDパイプライン](../infrastructure/ci-cd.md)** - GitHub Actions、Turborepoキャッシュ
|
|
713
|
+
- **[デプロイメント戦略](../infrastructure/deployment.md)** - Vercel、Docker、環境変数管理
|
|
714
|
+
|
|
715
|
+
### 品質管理
|
|
716
|
+
- **[コードレビューガイドライン](../review-guidelines.md)** - 品質基準とチェックリスト
|
|
717
|
+
- **[テスト戦略](../testing-strategy.md)** - テストの書き方
|
|
718
|
+
|
|
719
|
+
---
|
|
720
|
+
|
|
721
|
+
## まとめ
|
|
722
|
+
|
|
723
|
+
このバックエンドアーキテクチャは、以下を実現します:
|
|
724
|
+
|
|
725
|
+
✅ **4層アーキテクチャ**: 責務の明確化と保守性の向上
|
|
726
|
+
✅ **Result型パターン**: 型安全なエラーハンドリング
|
|
727
|
+
✅ **Repositoryパターン**: テスト容易性とドメイン独立性
|
|
728
|
+
✅ **Mapperパターン**: 永続化層からのドメイン保護
|
|
729
|
+
✅ **index.ts不使用**: シンプルで拡張しやすいモジュール管理
|
|
730
|
+
|
|
731
|
+
すべての開発者は、この設計原則に従ってバックエンド開発を行ってください。
|