lvncerpedia

CQRS(Command Query Responsibility Segregation)

名前は難しそうだけど、考え方はシンプル! 「データを変更する処理」と「データを読む処理」を完全に分けようぜってパターン。

普通の構成(CQRSなし)

Controller
  ↓
Service(読み書き両方やる)
  ↓
Repository
  ↓
DB

CQRSありの構成

Controller
  ↓           ↓
Command     Query
(書き込み)  (読み取り)
  ↓           ↓
WriteDB     ReadDB(別でも同じでもOK)

なんで分けるの?

  • 読み取りは速さ重視、書き込みは整合性重視って要件が全然違う
  • 読み取り専用モデルを最適化できる(JOINしまくった重いクエリとか)
  • スケールもしやすい

正直なところ

これ、小〜中規模なら過剰設計になりがちだよ笑 大規模なシステムやマイクロサービスで真価を発揮する感じ!

ORMで知っておいた方がいいこと

1. N+1 問題

ORMあるあるの罠でめちゃくちゃよく踏む!

// ❌ N+1になるコード
const users = await User.findAll();
for (const user of users) {
  const posts = await user.getPosts(); // ユーザーの数だけクエリが走る!
}
// 100人いたら101回クエリが飛ぶ😱

// ✅ Eager Loadingで解決
const users = await User.findAll({
  include: [Post], // JOINして1回で取ってくる
});

2. Lazy Loading vs Eager Loading

LazyEager
タイミング必要になったとき最初にまとめて
クエリ数多くなりがち少ない
使いどき関連データを使わない場合絶対使うとわかってる場合

N+1はLazyが原因になることが多いよ!

3. トランザクション管理

// 複数のDB操作をまとめてやるとき必須!
await sequelize.transaction(async (t) => {
  await User.create({ name: "Alice" }, { transaction: t });
  await Profile.create({ userId: 1 }, { transaction: t });
  // どっちかが失敗したら両方ロールバック✅
});

途中で失敗したときに中途半端な状態にならないようにするやつ!

4. マイグレーション

DBのスキーマ変更をコードで管理する仕組み。

migrations/
  20240101_create_users.ts
  20240202_add_email_to_users.ts  ← カラム追加とかをコードで記録

手動でSQL叩いて変更するんじゃなくて、チームで変更履歴を共有できるのが大事!

5. インデックスを意識する

ORMは簡単にデータ取れちゃうから、速度のことを忘れがちなんだよね。

// このクエリ、emailにインデックスないと全件スキャンになるよ!
await User.findOne({ where: { email: "test@example.com" } });

まとめると優先度はこんな感じ

優先度トピック
🔴 今すぐN+1問題、トランザクション
🟡 わりと早めにEager/Lazy Loading、マイグレーション
🟢 規模が大きくなったらCQRS、イベントソーシング

N+1だけは本当によく踏むから先に覚えておいて損なし! 何か使ってるORMとか言語ある?もっと具体的に説明できるよ〜!