メインコンテンツまでスキップ

第1章:CQSってなに?超ざっくりつかむよ〜🧸✨

0. 今日のゴール🎯✨

今日のゴールはこれだけです👇 「CQSの一文ルール」を、口に出して言えるようになること📝💕

Command(命令):状態を変える(でも値は返さない)🔧 Query(質問):値を返す(でも状態は変えない)📖 この考え方は Martin Fowler の説明が分かりやすいよ〜! (martinfowler.com) (由来は Bertrand Meyer の考え方としてもよく紹介されます🧠) (ウィキペディア)


1. まず言葉だけ!Command / Queryってなに?🧸💬

Command(コマンド)🔧💥

「何かをする」「変更する」系だよ〜✨ 例👇

  • ToDoを追加する ➕📝
  • 完了にする ✅
  • 保存する 💾
  • 削除する 🗑️

👉 ポイント:**“世界(状態)を変える”**感じ🌍✨

Query(クエリ)🔍📖

「知りたい」「取得したい」系だよ〜✨ 例👇

  • ToDo一覧を取得する 📋
  • 完了済みだけ数える 🔢
  • 1件だけ探す 🕵️‍♀️

👉 ポイント:**“見るだけ(副作用なし)”**が理想🍃


2. 「混ぜると事故る」直感を作ろう💥😇→😱

Side Effect Trap

事故のにおい①:読んだだけなのに、何かが変わる👀💦

たとえば「一覧を表示する」つもりで呼んだ関数が… 実は裏で ログ保存 したり カウンタ増やしたり してたら?📈🧨

  • 何回呼ぶと何が起きるの?😵
  • テストで同じ結果にならない…😱
  • 表示の順番変えただけで動きが変わる…🌀

Fowler も「状態を変えない問い合わせ(Query)は安心して差し込める」みたいな話をしてるよ🍃✨ (martinfowler.com)


3. いちばん大事:CQSの一文ルール📝💖

はい、今日の主役きた〜!!🧸✨

✅ CQSの一文ルール

CQS Separation Concept

  • Command:状態を変える。値を返さない(基本は void)🔧
  • Query:値を返す。状態を変えない(副作用なし)📖

「質問しただけで答えが変わるのは変だよね?」という直感が根っこだよ〜🌱 (ウィキペディア)


4. ちっちゃいTypeScript例で体感しよ〜🧪✨

4-1. 混ぜ混ぜ(事故りやすい)例😱🧨

let todos: { id: number; title: string; done: boolean }[] = [];
let nextId = 1;
let viewCount = 0;

// 😱 Queryっぽい名前なのに、裏で状態が変わる!
export function getTodos(): { id: number; title: string; done: boolean }[] {
viewCount++; // ← えっ、見ただけなのに増えた…😇
return todos;
}

これ、呼ぶたびに viewCount が増えます📈💦 「一覧を表示するだけ」のはずなのに、副作用が混ざってるのがポイント😵‍💫


4-2. CQSっぽく分けた例🥳🍃

let todos: { id: number; title: string; done: boolean }[] = [];
let nextId = 1;
let viewCount = 0;

// ✅ Command:追加する(状態を変える)
export function addTodo(title: string): void {
todos.push({ id: nextId++, title, done: false });
}

// ✅ Query:一覧を返す(状態を変えない)
export function getTodos(): { id: number; title: string; done: boolean }[] {
return todos;
}

// ✅ Command:閲覧数を増やす(やるなら“増やす専用”に)
export function incrementViewCount(): void {
viewCount++;
}

// ✅ Query:閲覧数を見る
export function getViewCount(): number {
return viewCount;
}

こうすると…

  • 「見るだけ」関数は安心して何回でも呼べる🍃✨
  • 「増える」系は増えるって分かる🔧✨
  • デバッグがラクになる🧩💖

5. 今日のミニ演習🎮✨(5〜10分)

演習A:これはどっち?仕分けしよう🎯

次の関数名、Command / Queryどっちっぽい?🤔💭

  1. saveUserProfile() 💾
  2. getUserProfile() 👀
  3. calculateTotalPrice() 🧮
  4. markAsRead()
  5. searchTodos() 🔍

答え(目安)👇

  • 1 Command / 2 Query / 3 Query / 4 Command / 5 Query 🥳 (※ calculate... は “計算するだけ” なら Query だよ〜🍃)

演習B:混ぜ混ぜを分けてみよう✂️✨

次の “混ぜ混ぜ関数” を 2つに分割してみてね🧸💖

let todos: { title: string; done: boolean }[] = [];
let lastUpdated: Date | null = null;

// 😇 取得っぽいのに、更新もしちゃう!
export function getTodosAndTouch(): { title: string; done: boolean }[] {
lastUpdated = new Date(); // ← 状態が変わる
return todos;
}

ヒント🧠✨

  • getTodos()(Query)
  • touchLastUpdated()(Command) に分ければOK〜🙆‍♀️💕

6. AIに手伝ってもらうコーナー🤖🪄(超おすすめ!)

そのままコピペ用プロンプト💌✨

  • 「この関数、CQS違反してる?理由も教えて。直すならどう分ける?」🧸🔍
  • 「このコードを Command と Query に分割して、関数名も提案して」✂️📛
  • 「副作用になりそうなところ(Date/乱数/グローバル更新)を指摘して」👀⚠️

AIは“分割案”を出すの得意だけど、最後の判断はあなたがルールで握るのがコツだよ〜🎮✨


7. 今日のまとめ🌸💻✨

  • Command=変える🔧(基本、値を返さない)
  • Query=読む📖(基本、状態を変えない)
  • 混ぜると「え、これ呼んだら何起きるの?」が増えてしんどい😱💦
  • 今日の合言葉:“聞くだけで世界を変えない🍃”

8. おまけ:今のTypeScriptの小ネタ🫘✨

いま最新の TypeScript は 5.9 系として案内されてるよ〜(インストール案内でも “currently 5.9” って書かれてる)📦✨ (typescriptlang.org) あと、TypeScript の“ネイティブ化”の流れ(TypeScript 7 のプレビュー系)も公式から出てて、ビルド高速化が話題になってるよ🚀 (Microsoft Developer)


次の第2章では、わざと「混ぜ混ぜ関数」を作ってから、どんな事故が起きるか体験するよ〜😇→😱🎮✨