第1章 この章のゴール宣言 🎯✨
![hex_ts_study_001[(./picture/hex_ts_study_001_introduction_to_hexagonal_arch.png)
1. この章のゴール宣言 🎯✨
この章を読み終わったら、次の3つができるようになるのがゴールだよ😊💖
- 「Port/Adapterって何?」を、自分の言葉で説明できる🔌🧩✨
- “中心(ルール)を守る”ってどういう意味かが腹落ちする🏰🛡️
- **依存の向き(外→中はOK、中→外はNG)**を判断できる🧭🔥
そして、この後の章で作っていく“今日のゴールアプリ”は… **小さなToDo(追加・一覧・完了)**みたいなミニアプリ📝🍰✅ いきなり完璧を目指さず、小さく作って育てる方針だよ🌱✨
つまずきやすいポイントも先に言っちゃうね😊 概念だけ読むとふわっとする → だからこの章は「あるある → 1枚絵 → 合言葉 → 依存の向き」って順で、体感しながら整理するよ🧠💞
2. 直結コードの「つらさ」あるある 😵💫💥
設計をまだ本格的にやってない時って、ついこうなりがち👇😵
- 画面(UI)で入力チェックして
- そのままファイル保存して
- そのまま外部APIも叩いて
- ついでにログも混ぜて…
全部が1か所に混ざるやつ😇💣
何がつらいの?(痛みポイント)😱
- ちょっと仕様変えるだけで、どこが壊れるか分からない🧨
- テストしようとしても、DB/ファイル/ネットが絡んでムリゲーになる🧪💔
- “本来のルール”がどこにあるか迷子になる🌀
これ、根っこは超シンプル👇 **「依存が散らばってる」**から怖いの😵💫
3. ヘキサゴナルの1枚絵(城の中心を守る)🏰🛡️
ヘキサゴナルは、ざっくり言うとこう👇✨
- 中心(城の中):アプリのルール(ドメイン/ユースケース)🧠❤️
- 外側(城の外):入出力(画面・HTTP・DB・ファイル・外部API)🌐💾📄📨
そして大事なのはこれ👇 中心は、外側の都合を知らない🙅♀️ (中心は“ルール”に集中する✨)
「Ports & Adapters」って名前は、提唱者の Alistair Cockburn がこの考え方を説明するときに使った言い方だよ🔌🧩 (アリステア・コックバーン) AWSの解説も「UIやデータストアに依存せずにテストしやすくする」って趣旨で説明してるよ🧪✨ (AWS ドキュメント)
六角形(ヘキサゴン)の形が重要ってわけじゃないよ🙆♀️ **“中心を守る比喩”**として受け取ればOK😊✨ (アリステア・コックバーン)
4. まず覚える合言葉3つ 🗣️✨
この章は、まずこれだけ覚えたら勝ち🥇✨
- 中心を守る🛡️(ルールを外の都合から隔離する)
- 約束はPort🔌(中心が「こう呼んでね」と決める)
- 変換はAdapter🧩(外の都合を、中心の約束に合わせて翻訳する)
これ、あとで何回も効いてくるやつ🧠💞
5. Portってなに?(差し込み口=約束)🔌😊
Portは一言でいうと👇 **中心が外側にお願いするときの“契約(インターフェース)”**だよ📌✨
たとえば中心(ユースケース)が「ToDoを保存したい」とするよね? そのとき中心はこう言う感じ👇
- 「保存してね」
- 「一覧ちょうだい」
- 「IDで取ってきてね」
でも、中心は “どこに保存するか” は知らない🙅♀️ ファイル?DB?メモリ?クラウド?…それは外側の仕事🌍✨
だから中心は、必要最小限の約束だけ決めるのがコツ✂️✨
Portが増えすぎると死ぬ話⚠️😇
Portを「なんでもかんでも」作り始めると、
- 似たPortが乱立して
- どれ使うか分からなくなって
- 結局、設計が複雑化する 🌀
予防線はこれ👇 ✅ “中心が本当に必要な会話だけ” Portにする ✅ 1つのPortは、目的が1つ(会話がブレない) (アリステア・コックバーン)
6. Adapterってなに?(変換器=実装)🧩✨
Adapterは一言でいうと👇 **外側の都合を吸収して、Portの約束どおりに動かす“翻訳係”**🔁✨
例:
- DBの書き方(SQL/ORM)→ Portの形に合わせる💾🔁
- HTTPリクエスト → ユースケース入力に変換する🌐📮
- ファイル読み書きの例外 → 中心が扱える形に包む📄🧯
Adapterは「薄い」が正義🥗✨
Adapterにルール(業務判断)を入れ始めると…
- ルールが外側へ漏れて
- 中心が空っぽになって
- 差し替えもテストも微妙になる😱💔
Adapterは基本こう👇 ✅ 変換(入力・出力の形を整える) ✅ 呼び出し(DB/外部API/ファイルへ) ✅ 例外のラップ(外の失敗を扱いやすくする)
7. Inbound / Outbound の超やさしい整理 🚪➡️⬅️
ここ、最初に混乱しやすいけど、めっちゃ単純だよ😊✨
Inbound(外→中)🚪➡️🏰
外側が中心を呼び出す入口 例:CLI / HTTP API / 画面ボタン / バッチ実行 など⌨️🌐🖱️
→ 「ユーザー操作」や「外部からの入力」が来るところ✨
Outbound(中→外)🏰➡️🌍
中心が外側にお願いする出口 例:DB / ファイル / 外部API / メール送信 など💾📄📨
8. 依存の向き(これだけは最重要)🧭🔥
ヘキサゴナルで一番大事なの、ほんとにここ🥺✨
ルール:中心は外側を知らない 🙅♀️
- 中心(ドメイン/ユースケース)は HTTPもDBもファイル形式も知らない🙅♀️
外側は中心を知っていい 👌
- 外側(Adapter)は 中心のPortを実装するために中心を知ってOK😊
つまり依存はこう👇
- ✅ 外 → 中(OK)
- ❌ 中 → 外(NG)
これが守れると、何がうれしい?🎁✨
- UIをCLI→HTTPに変えても中心ノーダメ🌐🔁⌨️
- DBをファイル→SQLite→クラウドに変えても中心ノーダメ💾🔁📄
- テストで外側を差し替えて、中心だけ爆速で試せる🧪🚀
Nodeの世界でも、実運用はLTS系を使って周辺を入れ替えながら育てることが多いんだけど、入れ替えやすさがあるとこういう更新にも強くなるよ🔁✨(Nodeの現行/LTSの扱いは公式が整理してるよ) (Node.js)
ちょい実感:ミニ例で「混ざるとつらい」を見る👀💥
(※ここは“嫌な例”を一瞬見るだけ!すぐ戻るよ😊)
// 😵 ありがちな「全部入り」例(こうはなりたくない)
import { readFileSync, writeFileSync } from "node:fs";
function addTodo(title: string) {
if (!title.trim()) throw new Error("タイトル空はダメ");
// ファイル読み込み(I/O)
const json = readFileSync("todos.json", "utf-8");
const todos = JSON.parse(json) as { id: string; title: string; completed: boolean }[];
// ルール・状態作成
const todo = { id: crypto.randomUUID(), title, completed: false };
// 保存(I/O)
todos.push(todo);
writeFileSync("todos.json", JSON.stringify(todos, null, 2), "utf-8");
return todo;
}
このコード、今は短いけど、すぐこうなる😇
- HTTP化したい → この関数の中を改造する羽目🌐💥
- テストしたい → ファイル必須でつらい🧪📄
- ルール増えた → さらに巨大化😵💫
ヘキサゴナルの発想だと、こう分けるイメージ👇✨
- 中心:「タイトル空はダメ」「ToDoを追加する手順」🧠
- 外側:「保存先がファイルである」「JSONである」📄
AI拡張ちょい活用(この章向け)🤖💖
AIはこの章だと、理解の補助に使うのが超おすすめ😊✨ (設計の芯を丸投げはまだ早い⚠️)
そのまま使える質問テンプレ📝🤖
- 「Ports & Adapters を、ToDoアプリの例で“中心/外側”に分けて説明して」
- 「このコードは中心が外部都合を知ってしまってる?どこが境界?」
- 「Portを“必要最小限”にするために、会話(目的)を1つに絞って提案して」
最近はGitHub Copilot周辺も“提案”だけじゃなく“エージェントっぽい動き”まで広がってきてるから、レビュー相手として使うのもすごく相性いいよ🕵️♀️✨ (The Verge)
まとめ:第1章で持ち帰る3行 🎁💖
- 中心を守る🛡️(ルールを外の都合から隔離)
- 約束はPort🔌(中心が必要な会話をインターフェース化) (アリステア・コックバーン)
- 変換はAdapter🧩(外の都合をPortに合わせて翻訳)
かんたん理解チェック✅✨(3問だけ)
- 「中心はDBを知っていい?🙆♀️🙅♀️」
- Portは「実装」?それとも「約束」?🔌
- 「外→中」「中→外」どっちがOK?🧭
次章への予告🌉✨
次の章からは、もう少し手を動かしながら、 「直結のつらさ」→「分けたらラク!」を体験していくよ😊💻✨
(※ちなみにTypeScriptは現時点で npmの最新が 5.9.x 系として案内されてるよ📦✨) (npmjs.com)