この記事はシリーズ「動いているリポジトリを読む — Next.js 16 で学ぶクリーンアーキテクチャと TDD」の第 1 回(全 19 回)です。
実際に動いている公開リポジトリ kai-kou/gem-hunter(MIT)を1本まるごと読み解く連載です。架空のサンプルではなく実ファイルを引用し、掲載しているコマンド結果はすべて実際に動かして採取しています。
全体を通しで読みたい方へ: 同じ内容を 1 冊にまとめた Zenn Book を無料で公開しています。
シリーズ全体の目次(クリックで開く)
- 第 1 回 動いているリポジトリを読むという学び方(この記事)
- 第 2 回 clone からテストが緑になるまで(公開予定)
- 第 3 回 要件IDとADR — 何を解くアプリなのかを地図にする(公開予定)
- 第 4 回 App Router の地図 — フォルダがそのまま URL になる(公開予定)
- 第 5 回 Server Component と Client Component の境界は6ファイルしかなかった(公開予定)
- 第 6 回 なぜ層を分けるのか、分けない選択肢との比較で(公開予定)
- 第 7 回 domain層 — ブランド型で「検証済みの値」を型にする(公開予定)
- 第 8 回 usecases層 — DIコンテナを使わない依存性逆転(公開予定)
- 第 9 回 infrastructure層 — 外部APIの語彙を持ち込ませない翻訳層(公開予定)
- 第 10 回 依存規則を440行のPythonで機械検査する(わざと壊してみる)(公開予定)
- 第 11 回 URLのクエリが画面に出るまでを1本追跡する(公開予定)
- 第 12 回 ライブラリなしの i18n と、変化を目で見ていない人へ伝える実装(公開予定)
- 第 13 回 テスト戦略とTDD — どの層を何でテストするか(公開予定)
- 第 14 回 失敗するテストを先に書く — Red→Green を1周する(公開予定)
- 第 15 回 MSWとTesting Library — 「実ネットワークに出ない」を設定1行で保証する(公開予定)
- 第 16 回 Playwright E2E — 外部APIに依存しないブラウザテストの構成(公開予定)
- 第 17 回 赤くなったテストをどう判定するか — axeの限界とflakyの正体(公開予定)
- 第 18 回 生IPを残さないレート制限・Cookie暗号化・第三者HTMLの表示(公開予定)
- 第 19 回 CIは道具ではなくゲートの集合 — そして自分のプロジェクトへの持ち帰り方(公開予定)
TL;DR
- 実際に動いている公開リポジトリ kai-kou/gem-hunter(MIT)を 1 本まるごと読み解く連載を始めます
- 対象は Next.js 16 / クリーンアーキテクチャ / Vitest / Playwright に触れたことがないエンジニア
- 架空のサンプルは使いません。全部そのリポジトリに実在するファイルを引用します
- 掲載するコマンド結果は すべて実際に動かして採取した生ログ です
対象読者は、Next.js 16 / クリーンアーキテクチャ / Vitest / Playwright に業務で触れたことがないエンジニアです。プログラミング自体は仕事でしていて、React は未経験か少し触った程度、という方を想定しています。
入門書を読んでも埋まらないもの
チュートリアルをなぞって動くものが作れるようになったあと、実務で最初に困るのは「どう書くか」ではありません。だいたいこういう問いです。
- この関数はどの層に置けばいいのか
- この値はいつ検証すればいいのか。入り口か、使う直前か
- このテストはユニットで書くべきか、E2E に回すべきか
- この設定は「そういうものだから」なのか、誰かが理由あって選んだのか
こうした判断は、完成したコードの 形 を見ても分かりません。分かるのは、その形が選ばれた理由 を読んだときです。
そして理由は、たいていどこにも残っていません。
理由が残っているリポジトリを 1 本読む
この連載で読む gem-hunter は、その理由がリポジトリの中に残っている珍しい例です。
- 設計判断が ADR(Architecture Decision Record)15 本 に記録されていて、却下した代替案 まで残っている
- コード中のコメントに「なぜこう書いたか」だけでなく「こう書かないと何が起きたか」が実測つきで書かれている
たとえばこんな記録があります。
- 正規表現に 1 文字加えたら検査が 57 秒 かかるようになった、という記録
- E2E が不安定だった原因を「タイミングの揺らぎ」と推測したが それは誤りで、真因はリンクの自動プリフェッチだった、という記録
- 依存規則の抑止コメントを、秘密情報に関わる検査にだけは効かせない ことにした、という判断
この連載がやるのは、そこに残っている判断を掘り起こして、初めてこのスタックに触れる人にも読める形に翻訳することです。
どんなアプリか
GitHub の OSS を「star の多さ」ではなく「実際にどれだけ使われているか」で探す Web アプリケーションです。
要件定義書の冒頭に、こう書かれています。
star は注目度の指標であって利用実績ではない。約 600 万個の偽 star が観測されており、star 順に並べるほど「実際に使われているが知られていないもの」は見えなくなる。
実例として、
debug_inspectorは 25 star でありながら 111,000 以上の OSS プロジェクトから依存されている。
25 star で 11 万プロジェクトから依存されているライブラリは、star 順のランキングには絶対に出てきません。これを見つけられるようにする、というのがこのアプリのミッションです。
この連載が読む教材の版
gem-hunter は今も開発が続いているリポジトリです。この連載が読み、数えたのは コミット 39ba4a1(2026-08-23 13:28 JST) の状態で、以降に出てくる引用・行番号・件数はすべてこの版のものです。
まったく同じ数字を手元で見たい場合は、clone したあとにこの版へ移動してください。
git checkout 39ba4a1 # 連載と同じ版に合わせる
git switch - # 元のブランチ(main)に戻る
git checkout <コミット> はブランチから離れた状態(detached HEAD)になります。読み終わったら 必ず git switch - で元のブランチに戻してください。この 2 つはセットで覚えておくと迷子になりません。
最新の main を読んでも構いません。 その場合、件数や行数はこの連載より増えているはずです。この連載が扱う「なぜそう書いたか」は、件数が変わっても変わりません。
規模を先に掴んでおく
読み始める前に、これから読むものの大きさを見ておきます。以下は基準コミット 39ba4a1 を 私の手元で実際に測った値 です(Linux コンテナ・Node.js v22.22.2)。採取コマンドも並べたので、手元で数え直せます。
| 項目 | 実測値 | 採取コマンド |
|---|---|---|
src/ 配下のファイル数 |
162 | find src -type f | wc -l |
| テストファイル数 | 81 | npx vitest run |
| テストケース数 | 954(アプリ 841 / ビルド時ツール 113) | npx vitest run |
| E2E の spec 数 | 23 ファイル・107 テスト | npx playwright test --list |
'use client' が付いているファイル |
6 | grep -rl "^'use client'" src app | wc -l |
src/ui/ のファイル数 |
68 | find src/ui -type f | wc -l |
| ADR(設計判断の記録) | 15 本 | ls docs/adr/*.md | wc -l |
| 依存規則の静的検査の対象 | 176 ファイル(違反 0) | python3 tools/check_architecture_boundaries.py |
この表が示すのは規模だけです。テストが何件通るか、E2E が全部緑になるかは別の話で、第 2 回で実際に走らせて確かめます。
個人が数週間で読み切れる規模でありながら、「サンプルアプリ」では出てこない問題(キャッシュ、レート制限、認証、国際化、アクセシビリティ、デプロイ)が一通り実装されています。
特に注目してほしいのは UI を置く src/ui/ が 68 ファイルあるのに、'use client' は 6 ファイルしかない ことと、テストが 954 件ある ことです。前者は「Next.js 16 でサーバー側にどこまで寄せられるか」の実例、後者は「テストを書きながら作るとどれくらいの量になるか」の実例です。どちらも連載の中で詳しく見ます。
技術スタック
| 領域 | 採用技術 |
|---|---|
| フレームワーク | Next.js 16(App Router・React Server Components) |
| 言語 | TypeScript 5 |
| スタイル | Tailwind CSS v4 + shadcn/ui |
| バリデーション | zod 4 |
| ユニット・結合テスト | Vitest 4 + Testing Library + MSW 2 |
| E2E | Playwright + axe |
| 実行環境 | Cloudflare Workers(OpenNext 経由)※参考程度 に扱います |
前提知識の境界線
この連載が「知っている前提で進む」ものと「その場で説明する」ものを分けておきます。
| 分野 | 扱い |
|---|---|
ターミナルで git clone / npm が使える |
前提 |
| HTTP・URL・クエリパラメータが分かる | 前提 |
| TypeScript の型注釈が読める | 前提 |
JSX・React のフック(useState / useEffect) |
必要になった回の冒頭で最小限だけ翻訳 します |
| Server Components とは何か | 本連載の主題(第 5 回) |
| クリーンアーキテクチャ | 本連載の主題(第 6〜10 回) |
| Vitest / Testing Library / MSW / Playwright | 本連載の主題(第 13〜17 回) |
React そのものの入門はしません。 この連載は、React を知らない人が コードを読めるようになる ところまでを引き受け、書けるようになる ところまでは引き受けません。
掲載するログについて
この連載には、コマンドの実行結果がそのまま貼ってある箇所があります。これらは 実際に実行して採取した生ログ です。第 10 回では、依存規則をわざと破ったときに検査がどう落ちるかを実際に再現して載せます。
一方、実行できなかったもの(Cloudflare へのデプロイなど、外部アカウントや課金が必要なもの)については、その旨を明示したうえで公式ドキュメントに基づく二次情報として書きます。「実際に試した」と「資料で読んだ」を混ぜません。
これは連載の作法であると同時に、gem-hunter 自身の作法でもあります。あのリポジトリの ADR には「Free プランの CPU 上限が実測で確定するまで『月額 0 円で動く』と報告しない」という条件付き承認が書かれています。
読み終えると何ができるようになるか
- 実プロダクトのコードを層ごとに読み解ける。初めて見るリポジトリでも「これはどの層のファイルか」「この依存の向きは正しいか」を判断できます
- 自分のプロジェクトで境界を引き、それを機械で守れる。第 10 回で読む依存規則の検査は 440 行の Python スクリプトで、考え方はどの言語にも移植できます
- テストを層ごとに書き分けられる。「ここはフェイクを手書き、ここは MSW、ここは E2E」という判断基準を実例つきで持ち帰れます
- E2E を壊れにくく書ける。ロケータの選び方、待ち方、テストデータの作り方、そして「赤くなったテストをどう判定するか」まで
この連載の進み方
全 19 回、毎日 1 本 のペースで公開します。大きく 6 つのまとまりに分かれます。
| まとまり | 回 | 何をするか |
|---|---|---|
| はじめに | 1〜2 | 読み方を決め、実際に動かす |
| 全体像 | 3 | 何を解くアプリなのか、技術スタックが何を担当するのかを地図にする |
| Next.js 16 の基礎 | 4〜5 | ファイル規約と、サーバー/クライアントの境界 |
| クリーンアーキテクチャ | 6〜10 | 層を分ける理由と、5 つの層、そして規則を機械で守る仕組み |
| 画面と設計をつなぐ | 11〜12 | データが画面に出るまでの追跡、i18n とアクセシビリティ |
| テストと運用 | 13〜19 | テスト戦略・TDD・ユニット・MSW・E2E・a11y・セキュリティ・CI |
「Next.js の基礎(4〜5 回)を先に置き、そのあとで設計(6〜10 回)に入り、また Next.js に戻る(11〜12 回)」という並びには理由があります。第 4〜5 回の内容は設計の話を知らなくても読めますが、第 11 回「データが画面に出るまで」は 5 つの層をすべて通過する追跡なので、第 6〜10 回を読んでいないと「なぜこの関数がここで呼ばれるのか」が分かりません。依存があるものだけを後ろに置いた 結果、この並びになりました。
それでは
次回は、実際にリポジトリを手元に持ってきて動かします。外部サービスへのサインアップも API キーも要りません。git clone と npm ci だけで、テストが緑になるところまで行きます。
シリーズの前後の記事
-
⬅️ 前の記事: なし(この記事がシリーズの最初です)
-
➡️ 次回予告: 第 2 回 clone からテストが緑になるまで
教材リポジトリを clone して、設定ゼロのままユニットテストを緑にするところまでやります。
全 19 回をまとめて読みたい方は、同じ内容の Zenn Book(無料)へどうぞ。