第3章:SoCとSOLID(特にSRP)をやさしくつなぐ🧩💖
(ここ、SoCの「判断基準」をゲットする章だよ〜!✨)
3-0. この章でできるようになること🎯✨
この章が終わったら、こんなことができるようになります👇😊
- 「SoCで分けたい…けど、何を基準に分ければいいの?」が分かる🧠💡
- **SRP(単一責任の原則)=“変更理由”**の感覚がつかめる📌
- コードを見て「これ、何個の理由で変更されそう?」って当てられる🔮✨
- 仕分け練習で、SoCの“現場力”がちょっと上がる💪🌸
3-1. まずSOLIDってなに?ざっくりでOK🙆♀️✨
SOLIDは「壊れにくくて、直しやすい設計」のための有名な考え方セットだよ🧱✨ (名前がゴツいけど、やることはシンプル寄り😇)
- S:Single Responsibility(単一責任)📌 ← 今日の主役👑
- O:Open/Closed(拡張に開いて修正に閉じる)🚪
- L:Liskov Substitution(置き換えても壊れない)🔁
- I:Interface Segregation(肥大インターフェースやめよ)🍱
- D:Dependency Inversion(依存の向きを整える)🧲
SOLIDの基本原則はRobert C. Martinが紹介し、SOLIDという略語はMichael Feathersが作ったと言われています。(ウィキペディア)
3-2. SRPってなに?「責務」=「変更理由」だよ📌✨

SRPはよく「1つのクラスは1つのことだけやれ」って説明されがちなんだけど、それだと誤解が起きやすいの🥺💦 大事なのはこっち👇
✅ SRPの超重要フレーズ(超採用✨)
- “1つのモジュールは、1つの理由で変更されるべき”
- つまり 責務(responsibility)=変更理由(reason to change) 💡
この定義はUncle Bob(Robert C. Martin)本人も繰り返し説明しています。(blog.cleancoder.com) さらに分かりやすい言い換えも有名👇
同じ理由で変わるものは集める。違う理由で変わるものは分ける。 (blog.cleancoder.com)
そして最近(2025年末)でも「SRPの誤解されやすさ」は日本語でも整理されていて、学び直しにすごく良い流れになってます。(Zenn)
3-3. SoCとSRPの関係🧩✨(ここが核心だよ〜!)
SoC(関心の分離)🧠
- 「関心(concern)」ごとに分けようね、っていう考え方🌱
- 例:UI、業務ルール、通信、保存、ログ…を混ぜない🙅♀️
SoCは昔からある設計原則で、**“問題の側面ごとに分けて扱えるようにする”**のがポイント。(ウィキペディア)
SRP(単一責任)📌
- SoCをやるときの “分け方の採点基準” みたいなもの💯✨
- 「それ、変更理由が同じ?違う?」で判断できる🔍
✅ まとめると…
- SoC=分ける行為(現場のやり方)
- SRP=分ける基準(何を一緒にしてよいかのルール)
この2つがつながると、SoCが「なんとなく分ける」から **「理由で切る」**に進化するよ〜〜〜🧠✨
3-4. 例で体感!「学園カフェ注文」☕🎓(混ぜるとツラいやつ)
😇 Before:ぜんぶ混ぜ(あるある)
「注文ボタン押したら、1つの関数が全部やる」
- 入力チェック(必須・数値)🧾
- 合計金額の計算(割引・税)🧮
- API送信🌐
- 画面表示(成功/失敗メッセージ)🖥️
- ログ出力📈
これ、変更理由がバラバラすぎるのがポイント💥 たとえば…
- 税率変わる → 計算が変わる🧮
- 画面の文言変わる → UIが変わる🖥️
- APIのURL変わる → 通信が変わる🌐
全部別の理由だよね?😵💫 つまりSRP的に「同居させない方が安全」ってこと!
3-5. ✅ After:SRPで“変更理由”ごとに分ける✨
「変更理由」で分けると、だいたいこうなるよ👇😊
- 入力の解釈/UI:フォームから値を取り出す🖥️
- 業務ルール:合計金額を計算する(税・割引)🧮
- アプリの流れ:計算→送信→結果で分岐🧭
- 外部とのやりとり:API呼び出し🌐
- 通知:成功/失敗メッセージ表示💬
ミニコード(雰囲気だけつかもう✨)
(※ここは“完成品”より「分け方の匂い」を吸う場所だよ〜🌸)
// ✅ 業務ルール(Domain寄り):計算だけ(変更理由:価格ルールが変わる時)
export function calcTotalYen(items: { priceYen: number; qty: number }[], taxRate: number) {
const subtotal = items.reduce((sum, it) => sum + it.priceYen * it.qty, 0);
return Math.floor(subtotal * (1 + taxRate));
}
// ✅ 外部I/O(Infra寄り):送信だけ(変更理由:API仕様が変わる時)
export async function postOrder(payload: unknown) {
const res = await fetch("/api/orders", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify(payload),
});
if (!res.ok) throw new Error("Order failed");
return res.json();
}
// ✅ アプリの流れ(Application寄り):つなぐだけ(変更理由:手順が変わる時)
export async function submitOrderFlow(input: {
items: { priceYen: number; qty: number }[];
taxRate: number;
}) {
const totalYen = calcTotalYen(input.items, input.taxRate);
return postOrder({ ...input, totalYen });
}
この分け方の気持ちよさは👇✨
- 税率変更 →
calcTotalYenだけ見ればいい🧮 - API変更 →
postOrderだけ直せばいい🌐 - 手順変更 →
submitOrderFlowだけ直せばいい🧭
変更の爆発が止まるのが最大のご褒美だよ🎁✨
3-6. ミニ練習①:「変更理由」あてクイズ🔍🎮
次の変更が来たとき、どの関心(責務)が変わる?仕分けしてみてね😊✨
- 「送信先のAPIが
/api/orders→/v2/ordersに変わった」🌐 - 「“注文成功!”の表示を、トーストUIにしたい」🖥️
- 「学割で合計から10%引き」🎓🧮
- 「ログに注文IDも出したい」📈
- 「注文前に確認ダイアログを挟みたい」✅
✅ こたえ(目安)
- 通信(Infra)🌐
- UI🖥️
- 業務ルール(Domain)🧮
- ログ(横断的関心)📈
- アプリの流れ(Application)🧭
「変更理由が違う=置き場所を分ける」って感覚、ちょっときた?😆✨
3-7. ミニ練習②:SRPチェックの“魔法の質問”🪄💬
コードを見たら、これを自分に聞いてね👇😊
✅ 魔法の質問
-
「このコードが変更される理由、何個ある?」
- 2個以上あるなら、分離候補✨
さらに強い版👇
-
「誰(どの立場/役割)が変更を要求する?」
- 役割(actor)で割る発想はSRPの大事な解釈として語られます。(ウィキペディア)
例:
- 経理「税の計算変えて」🧾
- デザイナー「表示変えて」🎨
- バックエンド「API変えた」🛠️
→ それぞれ別のactorだから、同じ場所にいると揉めやすい😇💥
3-8. AI(Copilot/Codex系)に手伝ってもらうコツ🤖✨
AIは、SoC/SRPの練習相手としてかなり優秀だよ🎮✨ ポイントは「変更理由で分解して」って頼むこと!
使えるプロンプト例🎁
- 「この関数の“変更理由”を3つ挙げて、責務ごとに分割案を出して」🧠
- 「UI/業務ルール/通信/ログに分けるなら、ファイル構成も提案して」📁
- 「今の設計がSRP違反か、違反なら理由とリファクタ手順を短く」✂️
注意(AIあるある回避)⚠️😇
- 分割しすぎて“細切れ地獄”になることがある → 変更理由が同じなら分けないでOK🙆♀️
- まずは「2〜3ブロック」に分けるだけで勝ち🏆✨
3-9. 今日のまとめ🌸✨
-
SoCは「混ぜない」ための考え方🧠
-
SRPは「どう切る?」の採点基準📌(責務=変更理由)(blog.cleancoder.com)
-
コードを見たら
- 「変更理由は何個?」
- 「誰が変更を要求する?」 で分離ポイントが見える🔍✨
ちなみに本日時点のTypeScriptは npm上のLatestが 5.9.3 です。(NPM) そしてTypeScript 6/7に向けた大きめの動きも進行中だよ〜(性能面などの話題)。(Microsoft for Developers)
次の第4章は「TypeScriptの世界の“関心”を棚卸し」🧺✨ ここまでの“変更理由で切る感覚”があると、棚卸しが一気にラクになるよ😊💖