Skip to main content

第11章:境界線の引き方(判断軸だけ覚える)✂️🧠

testable_ts_study_011_boundary_cut.png

この章は、「どこでI/Oを切る?」を**迷わなくするための“判断軸”**を手に入れる回だよ〜!😊💖 (サンプルは、いま主流の TypeScript 5.9 系の書き味を前提に進めるね🧸📘) (TypeScript) 開発まわりも、最近は Vite 7 系+Vitest 4 系の組み合わせがとても一般的なので、この章の例もその“空気感”で書くよ〜!🚀🧪 (npm) (Node は v25 が Current、v24 が Active LTS の位置づけだよ〜🔧) (Node.js)


11.0 この章のゴール🎯😊章の終わりに、あなたがこう言えたら勝ち!

  • 「うん、境界は“変更理由”で引くんだよね✂️」
  • 「外部都合(API/DB/時刻など)を中心(ロジック)に入れないように線を引ける🚪」
  • 「同じ題材でも、3通りの線引き案を出して、どれが良いか比較できる🔁🧠」

11.1 そもそも“境界線”ってなに?🧃🚪### ✅ 境界線=「差し替えポイント」🎁

✨テスタブル設計で言う境界は、ざっくりこう👇

  • 中心(ロジック):ルール・計算・判断(テストしやすい💯🧪)
  • 外側(I/O):HTTP/DB/ファイル/時刻/ログ…外の世界🌍⚡
  • 境界(interface):中心から見た「こうしてね」の約束📜

中心は外側の“実装”を知らなくてOK🙆‍♀️ 中心は「インターフェース(約束)」だけを知って、外側がそれを満たす…ってイメージだよ〜🏠➡️🌍✨


11.2 境界線を引く“7つの判断軸”📐🧠✨

「どこで切る?」は正解が1つじゃない😵‍💫 でも、判断軸があると迷子にならないよ〜🧭💕

① 変更理由が違うか?(最重要)

🥇✂️境界を引く一番のコツはこれ!

  • “それが変わる理由”を一言で言ってみて🗣️
  • 理由が違うなら、分ける価値が高い✨

例👇

  • 割引計算:ビジネス都合で変わる🧾
  • 決済API呼び出し:外部仕様やネットワーク都合で変わる🌐 → 変わる理由が違う!だから境界!✂️

② 自分たちがコントロールできる?できない?🎮🙅‍♀️* 外部API:向こうの都合で変わる😇

  • DB:運用・速度・方言で変わる🗄️
  • 時刻:勝手に進む⏰ → コントロールできないものは、中心に入れないのが安定💎

③ テストで“止めたい/固定したい”ものか?🧊🧪* 時刻⏰、乱数🎲、ネット🌐、DB🗄️

、ファイル📁 → テストで固定できないと、**フレーク(たまに落ちる)**が発生しやすい😵‍💫


④ それ、遅い?不安定?高コスト?🐢💸* ネットワークは遅い&落ちる

  • DBは準備が重い → 中心テストを速くするため、境界で隔離しがち⚡

⑤ “データの形”は誰の都合?🧩

📦外部DTO(APIのJSONとか)って、向こう都合で変わるんだよね😇 中心に入れると、中心が汚れやすい🫠 → 境界で変換してから中心へが強い(この話は後半章でさらにやるよ✨)


⑥ エラーの種類が違う?🚨🧠* 仕様上の失敗(在庫なし等)

😇

  • インフラ都合(タイムアウト等)⚡ → 混ざると「例外が暴れる」😵‍💫 境界で“扱いやすい形”に整えると平和🕊️

⑦ チーム/責務の境目か?👥🧱* ドメイン担当:割引・判定ロジック

  • インフラ担当:API/DB/運用 → 境界があると分業も安全✨

11.3 “境界を引く候補”あるあるリスト👃

💨✂️次のワードが見えたら「境界候補かも!」って反射できるようにしよ〜🧠✨

  • fetch / HTTPクライアント🌐
  • DBアクセス🗄️
  • ファイル読み書き📁
  • 時刻(Date.now 等)⏰
  • 乱数🎲
  • 環境変数・設定⚙️
  • console/logging📝

11.4 境界線の引き方:迷わない“5ステップ”🧭✨### Step1:やりたいことを1文にする📝

例:「天気を見て、傘がいるか判定して表示する」☔🙂

Step2:その中の“外の世界”を丸で囲む⭕* 天気取得:外🌐

  • 判定:中心🧠
  • 表示:外🖥️

Step3:変わる理由をそれぞれ言語化🗣️* 天気API:外部仕様が変わる

  • 判定:ルールが変わる
  • 表示:UI都合で変わる

Step4:境界案を2〜3個出す🔁「線引き案A/B/C」を作って比較(次でやるよ💖)

Step5:中心→境界(interface)

だけに依存させる🏠➡️📜中心が concrete(実体)を知らないようにするのがコツ✨


11.5 ハンズオン:同じ題材を3つ線引きして比較🔁☔🧠題材はこれ!

testable_ts_study_011_bad_vs_good_map.png

👇 「天気を取得して、傘が必要か判定する」☔✨

出したい結果:

  • 雨っぽいなら「傘いる!」
  • それ以外なら「いらないかも!」

線引き案A:境界なし(全部ごちゃ混ぜ)

😱🌀「早く作れる」けど…テスト地獄になりやすいタイプ💥

// ❌ 例:中心の中で外部APIも時刻も触る(境界なし)
export async function shouldTakeUmbrella(city: string): Promise<boolean> {
const res = await fetch(`https://example.com/weather?city=${city}`);
const json = await res.json(); // 外部DTOそのまま
const hour = new Date().getHours(); // 時刻直取り

const rainy = json.forecast?.chanceOfRain >= 50;
const commutingTime = hour >= 7 && hour <= 10;

return rainy && commutingTime;
}

つらみポイント😵‍💫

  • テストが「ネット必要」「時間に左右される」→不安定
  • 外部DTOの形が変わったら中心が壊れる
  • どこがロジックでどこがI/Oか見えない👀💦

線引き案B:HTTPだけ境界にする(DTOは中心へ持ち込む)

🙂🌐「境界の第一歩」って感じ!👏✨

// ✅ HTTPだけ差し替え可能にする
export interface WeatherApi {
fetchWeather(city: string): Promise<{ chanceOfRain: number }>;
}

export function createUmbrellaJudge(api: WeatherApi) {
return async (city: string): Promise<boolean> => {
const dto = await api.fetchWeather(city); // DTOの形が中心へ
return dto.chanceOfRain >= 50;
};
}

良いところ😊

  • テストで api をスタブできる🧸
  • ネット不要でテストできる🧪✨

惜しいところ🫠

  • DTO(外の形)が中心に入ってる → API都合の変更が中心に波及しやすい🌊

線引き案C:境界で変換して“中心はドメイン型だけ”💎🧠このコース的に「気持ちいい線引き」✨

中心は 純粋に判断だけ になるよ〜🍰

// ✅ 中心が扱う“きれいな型”
export type Weather = {
chanceOfRainPercent: number;
};

// ✅ 中心が求める約束(ポート)
export interface WeatherPort {
getWeather(city: string): Promise<Weather>;
}

// ✅ 中心:判断だけ(めっちゃテストしやすい)
export function decideUmbrella(weather: Weather): boolean {
return weather.chanceOfRainPercent >= 50;
}

// ✅ ユースケース:外からWeatherをもらって中心へ渡す
export function createUmbrellaUseCase(port: WeatherPort) {
return async (city: string): Promise<boolean> => {
const weather = await port.getWeather(city);
return decideUmbrella(weather);
};
}

超いいところ😍

  • 中心は「Weather」という自分ルールの型だけを見る
  • 外部JSONが変わっても、直すのは“外側アダプタ”だけで済みやすい
  • decideUmbrella は純粋関数なのでユニットテスト最強💪🧪

3案の比較まとめ📊✨

観点A: 境界なし😱B: HTTPだけ🙂C: ドメイン型まで💎
テストのしやすさ🧪最悪そこそこ最高
外部変更への強さ🛡️弱い中くらい強い
実装スピード🚀速い速い最初だけ少し遅い
長期の安心感😌低い高い

11.6 ミニ演習(あなたの手で線を引く)

✍️🖍️### 演習1:変更理由を1行で書く📝

次をそれぞれ一言で👇

  • 天気API呼び出し:________
  • 「傘いる?」判定:________
  • 画面表示:________

💡コツ:「それが変わるのは何のせい?」って聞くと書きやすいよ😊


演習2:境界案を“最低2つ”出す🔁あなたが作るならどっち?🙂

  • 案Bみたいに「HTTPだけ境界」
  • 案Cみたいに「ドメイン型まで境界で整える」

その理由を一言で✂️🧠✨


演習3:テスト観点で選ぶ🧪「このテスト、書きやすいのどれ?」って観点で選んでみてね😊

  • テストで固定したいもの:ネット🌐、時刻⏰、乱数🎲…どれ?

11.7 AI(Copilot/Codex)

に頼ると速いところ🤖🎀AIに任せやすいのはここ👇

  • 境界案A/B/Cの候補出し🔁
  • interface名の案出し📜
  • テストケース洗い出し🧪
  • DTO→ドメイン型変換のたたき台🧩

使えるプロンプト例(そのまま投げてOK)💌✨

  • 「この処理の“変更理由”を列挙して、境界候補を3案出して」
  • 「中心に入れるべきでないI/Oを洗い出して」
  • 「最小のinterfaceに削って提案して」

⚠️ただし!「どこに境界を引くかの最終決定」はあなたが握るのが大事だよ〜🧠💪 (AIは勢いで雑な境界を作りがちだから、判断軸でチェック✅)


11.8 まとめ🎁

✨(今日の持ち帰り)* 境界は I/Oだから 引く…だけじゃなくて、変更理由が違うから 引く✂️

  • 同じ題材でも 2〜3案を出して比較 すると判断が急にうまくなる🔁🧠
  • 迷ったら「中心は何を知るべき?」→ 外部都合は中心に入れない 🚪✨

次の第12章では、今日の判断軸を使って、実際に 「押し出す→薄くする」 の手順で安全に分離していくよ〜🧹➡️🧩💖