Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ describe('PokemonCard', () => {
機能単位でモジュールを分離し、`index.ts`(バレルファイル)で公開APIを定義する。
モジュール内ファイルへの直接importはESLintで禁止され、必ずバレル経由でアクセスする。

設計判断の詳細は [ADR(Architecture Decision Records)](docs/adr/) を参照。

### ディレクトリ構成

```
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ GitHub Issues #1〜#5 に定義された各ステップに沿って、AI と協
| 言語 | TypeScript (strict mode) |
| ルーティング | Expo Router (file-based routing) |
| 国際化 | i18next / react-i18next / expo-localization |
| 状態管理 | React Context + AsyncStorage |
| 状態管理 | zustand |
| テスト | Jest / @testing-library/react-native |
| パッケージ管理 | pnpm |
| 外部 API | [PokeAPI](https://pokeapi.co/)(REST + GraphQL) |
Expand Down Expand Up @@ -84,15 +84,15 @@ src/
├── splash/ # スプラッシュアニメーション
└── shared/ # モジュール横断の共有コード
├── components/ # PokemonCard, FavoriteButton
├── contexts/ # FavoritesContext
├── stores/ # useFavoritesStore (zustand)
├── domain/ # 型定義, タイプカラー
├── i18n/ # 国際化設定, 翻訳ファイル
└── repository/ # PokeAPI クライアント

__tests__/ # テスト(src の構造をミラー)
```

各モジュールは `index.ts`(バレルファイル)で公開 API を定義し、モジュール内部への直接アクセスは ESLint で禁止しています。詳細は [CLAUDE.md](./CLAUDE.md) を参照してください。
各モジュールは `index.ts`(バレルファイル)で公開 API を定義し、モジュール内部への直接アクセスは ESLint で禁止しています。設計判断の詳細は [ADR](./docs/adr/) を、開発ルールは [CLAUDE.md](./CLAUDE.md) を参照してください。

---

Expand Down
33 changes: 33 additions & 0 deletions docs/adr/0000-adr-template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# [短いタイトル]

- ステータス: [Proposed | Accepted | Deprecated | Superseded by ADR-NNNN]
- 決定日: YYYY-MM-DD
- 決定者: [チーム名 or 個人]

## コンテキスト

この決定が必要になった背景・課題を記述する。

## 検討した選択肢

1. **選択肢1** — 概要
2. **選択肢2** — 概要
3. **選択肢3** — 概要

## 決定

選択した内容とその理由を記述する。

## 結果

### ポジティブな影響

- 良い影響を記述

### ネガティブな影響

- トレードオフや制約を記述

## 参考

- 関連リンクや関連ADRを記載
58 changes: 58 additions & 0 deletions docs/adr/0001-modular-monolith.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# モジュラーモノリスアーキテクチャの採用

- ステータス: Accepted
- 決定日: 2026-04-15
- 決定者: プロジェクトチーム

## コンテキスト

ポケモン図鑑アプリを段階的に構築する中で、機能が増えるにつれてコードの整理方法を決める必要があった。画面数やデータソースが増えても保守性を維持できるアーキテクチャが求められた。

## 検討した選択肢

1. **フラットなディレクトリ構成** — `components/`, `screens/`, `hooks/` を機能横断で配置する従来型の構成
2. **モジュラーモノリス** — 機能単位で `src/<module>/` にコードを分離し、バレルファイルで公開APIを制御する
3. **monorepo** — 各機能を独立したパッケージとして分離する

## 決定

**モジュラーモノリス**を採用する。

- 機能単位でモジュールを分離: `home`, `detail`, `favorites`, `settings`, `splash`
- 各モジュールは `index.ts`(バレルファイル)で公開APIのみを定義する
- モジュール内は `components/`, `screens/`, `hooks/`, `domain/`, `repository/` で役割分担する
- 2つ以上のモジュールが使う共通コードは `src/shared/` に配置する
- `app/` ディレクトリはExpo Routerのルーティング定義のみに限定する
- ESLint `no-restricted-imports` でモジュール境界を強制する

```javascript
// eslint.config.js
patterns: [{
group: [
"@/src/home/*", "@/src/home/*/**",
"@/src/detail/*", "@/src/detail/*/**",
// ...各モジュール
],
message: "モジュール内ファイルへの直接アクセスは禁止です。index.ts経由でimportしてください。",
}]
```

## 結果

### ポジティブな影響

- モジュール間の依存関係が明確になり、意図しない結合を防げる
- バレルファイルにより公開APIが一目で分かる
- 新機能追加時にモジュールを追加するだけでスケールできる
- ESLintによる自動チェックで規約違反を防止できる

### ネガティブな影響

- モジュール追加時にESLint設定も更新する必要がある
- バレルファイルの管理コストが発生する
- 小規模な機能でもモジュールとして分離するオーバーヘッドがある

## 参考

- [ADR-0009: コーディング規約](0009-coding-conventions.md) — ESLintによるモジュール境界の強制
- `eslint.config.js` — 実際のESLint設定
54 changes: 54 additions & 0 deletions docs/adr/0002-tech-stack.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# 技術スタックの選定

- ステータス: Accepted
- 決定日: 2026-04-15
- 決定者: プロジェクトチーム

## コンテキスト

React Native学習プロジェクトとして、モダンかつ安定した技術基盤を選定する必要があった。Expoのマネージドワークフローを活用しつつ、最新のReact Native機能(New Architecture)を取り入れたい。

## 検討した選択肢

1. **Expo(マネージドワークフロー)** — Expoが提供するビルド・設定の自動管理を活用
2. **React Native CLI(ベアワークフロー)** — ネイティブコードを直接管理
3. **他のクロスプラットフォームフレームワーク** — Flutter、Kotlin Multiplatformなど

## 決定

**Expo マネージドワークフロー**を採用し、以下の技術スタックを選定した。

| カテゴリ | 選定技術 | バージョン |
|---------|---------|-----------|
| フレームワーク | Expo + React Native + React | 55 / 0.83 / 19.2 |
| 言語 | TypeScript (strict mode) | 5.9 |
| ルーティング | Expo Router | ファイルベース |
| パッケージ管理 | pnpm | 10.33.0 |
| ランタイム | Node.js | 24 |

主な設定:
- `tsconfig.json`: `"strict": true` で型安全性を強制
- `package.json`: `"packageManager": "pnpm@10.33.0"` でpnpmバージョンを固定
- パスエイリアス `@/*` で絶対インポートを使用
- `app.json`: `"newArchEnabled": true` でNew Architecture有効化、`"typedRoutes": true` で型安全ルーティング

## 結果

### ポジティブな影響

- Expoによりネイティブビルド設定の複雑さを回避できる
- TypeScript strict modeにより型安全性が保証される
- pnpmによる高速なパッケージインストールとディスク効率
- Expo Routerのファイルベースルーティングにより直感的な画面管理

### ネガティブな影響

- Expoがサポートしないネイティブモジュールは使用できない
- pnpmのhoisted設定(`.npmrc`)が必要でExpo互換性のための妥協がある
- New Architecture対応のライブラリに限定される

## 参考

- `tsconfig.json` — TypeScript設定
- `package.json` — 依存関係とスクリプト定義
- `app.json` — Expo設定
65 changes: 65 additions & 0 deletions docs/adr/0003-tdd-bdd.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# TDD/BDD開発手法の採用

- ステータス: Accepted
- 決定日: 2026-04-15
- 決定者: プロジェクトチーム

## コンテキスト

AI-DLCプロジェクトとして、品質保証と学習効果を両立する開発手法が必要だった。AIとの協働開発においても一貫した品質を維持できる仕組みが求められた。

## 検討した選択肢

1. **TDD/BDD** — テストを先に書き、振る舞いを日本語で定義してから実装する
2. **実装後テスト** — 機能実装後にテストを追加する従来型の手法
3. **E2Eテスト中心** — Detoxなどを用いたEnd-to-Endテスト主体の品質保証

## 決定

**TDD/BDD**を採用する。

- **Red-Green-Refactor**サイクルを徹底する
1. Red — テストを先に書く(期待する振る舞いを定義)
2. Green — テストが通る最小限の実装を書く
3. Refactor — コードを整理する(テストが通ることを確認しながら)
- **BDDスタイル**: `describe` で機能名、`it` で日本語の振る舞いを記述
- テストフレームワーク: Jest + jest-expo + @testing-library/react-native
- テスト配置: `__tests__/` ディレクトリに `src/` のミラー構造(`app/` にはテスト禁止)
- カバレッジ閾値: branches / functions / lines / statements 全て **80%以上**

```javascript
// jest.config.js
coverageThreshold: {
global: {
branches: 80,
functions: 80,
lines: 80,
statements: 80,
},
}
```

カバレッジ計測の除外対象:
- `src/**/*.d.ts` — 型定義ファイル
- `src/**/*Samples*` — サンプルコード
- `src/**/screens/**` — 画面コンポーネント(UI統合テストの境界)
- `src/**/index.ts` — バレルファイル(再エクスポートのみ)

## 結果

### ポジティブな影響

- テストが仕様書として機能し、振る舞いが日本語で明確に記述される
- 80%のカバレッジ閾値によりリグレッションリスクが低減される
- AIとの協働でもテストファーストにより意図の齟齬を早期に検出できる

### ネガティブな影響

- 実装前にテストを書くため、初期の開発速度はやや低下する
- 画面コンポーネント(screens)のテストが除外されており、UI統合テストの補完が必要

## 参考

- [ADR-0008: CI/CDパイプライン](0008-ci-cd-pipeline.md) — テスト自動実行との連携
- `jest.config.js` — Jest設定
- `jest.setup.js` — モック定義
47 changes: 47 additions & 0 deletions docs/adr/0004-rest-graphql-api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# REST + GraphQL API併用戦略

- ステータス: Accepted
- 決定日: 2026-04-15
- 決定者: プロジェクトチーム

## コンテキスト

PokeAPIはREST API(`pokeapi.co/api/v2/`)とGraphQL API(`beta.pokeapi.co/graphql/v1beta`)の両方を提供している。ポケモン名の多言語対応が必要な一覧画面と、詳細情報を取得する画面で、最適なAPIの選択が必要だった。

## 検討した選択肢

1. **REST APIのみ** — 全てのデータ取得をREST APIで行う
2. **GraphQL APIのみ** — 全てのデータ取得をGraphQL APIで行う
3. **REST + GraphQL併用** — 用途に応じて使い分ける

## 決定

**REST + GraphQL併用**を採用する。

- **GraphQL**: `home` モジュールのポケモン一覧取得に使用。1回のクエリで多言語名(`pokemon_v2_pokemonspeciesnames`)を含む一覧データを取得できる
- **REST**: `detail`, `shared` モジュールの単体ポケモン詳細取得に使用。シンプルなエンドポイントで十分な場面ではRESTを選択
- **GraphQLクライアントライブラリは不使用**: 素の `fetch` でPOSTリクエストを送信(Apollo等の依存を回避)
- **Repositoryパターン**: 各APIアクセスをrepositoryとしてカプセル化し、ドメイン層から直接APIを呼ばない

GraphQLの使用は `home` モジュールに限定し、他モジュールではREST APIを使用する。

## 結果

### ポジティブな影響

- 多言語対応の一覧取得がGraphQLの1クエリで完結する(REST APIだとN+1問題が発生する)
- シンプルな詳細取得ではREST APIの手軽さを活かせる
- GraphQLクライアントライブラリ不使用により依存が最小限
- Repositoryパターンにより、API実装の詳細がドメイン層から隠蔽される

### ネガティブな影響

- 2種類のAPIアクセスパターンが混在するため、コードの一貫性がやや低下する
- GraphQL APIはbeta版であり、安定性のリスクがある
- GraphQLクライアントライブラリ不使用のため、キャッシュ管理は自前で行う必要がある

## 参考

- [ADR-0005: 国際化(i18n)戦略](0005-i18n-strategy.md) — GraphQLによる多言語データ取得との連携
- `src/home/repository/pokemonGraphqlApi.ts` — GraphQL実装
- `src/shared/repository/pokemonApi.ts` — REST API実装
46 changes: 46 additions & 0 deletions docs/adr/0005-i18n-strategy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# 国際化(i18n)戦略

- ステータス: Accepted
- 決定日: 2026-04-15
- 決定者: プロジェクトチーム

## コンテキスト

ポケモン図鑑アプリとして、UIラベルとポケモン名の両方を日本語・英語で表示する必要があった。UIラベルの翻訳とAPIから取得するポケモン名の多言語対応を統一的に扱う仕組みが必要だった。

## 検討した選択肢

1. **i18next + react-i18next** — React向けの実績豊富な国際化ライブラリ
2. **expo-localization + 自前実装** — Expoの組み込み機能と自前のリソース管理
3. **react-intl** — FormatJS系の国際化ライブラリ

## 決定

**i18next + react-i18next**を採用する。

- 対応言語: `ja`(日本語)、`en`(英語)
- 翻訳リソース: `src/shared/i18n/locales/ja.json`, `en.json` に配置
- 言語設定の永続化: `@react-native-async-storage/async-storage` でデバイスに保存
- デフォルト言語: `ja`
- UIラベル翻訳: i18nextのリソースファイルで管理
- ポケモン名の多言語対応: GraphQL APIの `pokemon_v2_pokemonspeciesnames` で取得(ADR-0004参照)
- 言語切り替え: `useLanguage` カスタムフックで管理(i18n変更 + AsyncStorage保存を同時に行う)

## 結果

### ポジティブな影響

- i18nextの豊富なエコシステムと実績による安定性
- `useTranslation` フックによるReactコンポーネントとの自然な統合
- AsyncStorageによりアプリ再起動後も言語設定が保持される
- UIラベルとポケモン名の多言語対応が別々の仕組みで適切に分離されている

### ネガティブな影響

- i18next + react-i18nextの2パッケージが追加依存となる
- ポケモン名の翻訳がi18nextのリソースではなくAPIから取得されるため、2系統の翻訳管理が必要

## 参考

- [ADR-0004: REST + GraphQL API併用戦略](0004-rest-graphql-api.md) — GraphQLによるポケモン名の多言語取得
- `src/shared/i18n/` — i18n設定と翻訳リソース
45 changes: 45 additions & 0 deletions docs/adr/0006-state-management.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# 状態管理戦略

- ステータス: Accepted
- 決定日: 2026-04-15
- 決定者: プロジェクトチーム

## コンテキスト

お気に入り機能など、複数画面で共有する状態の管理方法を決める必要があった。ポケモンの手持ちを模した「最大6件」という制約付きのお気に入りリストを、一覧画面・詳細画面・お気に入り画面の3画面で同期する必要がある。

当初はReact Context APIで実装していたが、Provider不要化とボイラープレート削減のためzustandへ移行した。

## 検討した選択肢

1. **React Context API** — React組み込みのContext + Providerパターン
2. **zustand** — 軽量なグローバル状態管理ライブラリ
3. **Redux Toolkit** — Flux系の状態管理ライブラリ

## 決定

**zustand**を採用する。

- `useFavoritesStore`(`create()` で生成)でお気に入り状態をグローバル管理
- お気に入り上限: 6件(`MAX_FAVORITES = 6`、ポケモンの手持ち数に由来)
- `useFavorites` カスタムフックで `isFavorite` / `isFull` の派生状態を提供
- Providerが不要なため、`app/_layout.tsx` のネストが削減された
- 現在の実装ではメモリ内のみ(AsyncStorageによる永続化は未実装)

## 結果

### ポジティブな影響

- Providerが不要でコンポーネントツリーがシンプル
- zustandのセレクタにより、必要な状態のみを購読し再レンダリングを最小化できる
- ボイラープレートが少なく、`create()` 1つでストアを定義できる
- `useFavorites` フックにより消費側のAPIがシンプル

### ネガティブな影響

- zustandへの外部依存が追加される
- 現在はメモリ内のみのため、アプリ再起動でお気に入りが消失する

## 参考

- `src/shared/stores/useFavoritesStore.ts` — zustandによる状態管理実装
Loading
Loading