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

第10章:イベントpayload設計のコツ(必要最小限)🎒✨

10.0 この章のゴール 🎯💡

  • イベントの payload(中身) を「最小で強い形」にできるようになる 💪✨
  • 「入れるべき情報」と「入れない方がいい情報」を理由つきで判断できるようになる 🧠🔍
  • OrderPlaced のpayloadを 2案 作って、良し悪しを比べられるようになる ⚖️🛒

10.1 payloadってなに?(イベントの“中身”)🧾👀

ドメインイベントは「何が起きたか」を表す 過去の事実 だったよね ⏳✨ そのイベントの中で、追加で伝えたい細かい情報 が payload(ペイロード)だよ🎒

例:OrderPlaced(注文が置かれた)

  • 「起きた事実」:注文が作られた ✅
  • 「payloadに入りうる情報」:orderIditemstotalAmount など 🧾

ここで超大事なのが👇 payloadは“入れれば便利”だけで増やすと、未来で地獄を見る 😇➡️😱


10.2 “薄いイベント”と“厚いイベント”⚖️🍱

payload設計には、よく出る2つの方向性があるよ〜!✨

A) 薄い(Thin / Notification)📣🪶

  • payloadは 最小限(IDとかキー中心)
  • 受け取った側が「必要なら自分で取りに行く」スタイル 🔎

「薄いイベント(通知イベント)」は、イベントの結合を弱くしやすいよ〜って整理されることが多いよ📌 (Solace)

B) 厚い(Thick / State Transfer)📦🍖

  • payloadに 必要な情報を多めに載せて渡す
  • 受け取った側は「イベントだけでだいたい処理できる」✨

ただし、厚いほど 互換性(変更)・サイズ・機密 の問題が出やすい⚠️ この章は「必要最小限にするコツ」を中心に扱うよ🎒✨


10.3 payloadを最小にしたい3つの理由 🧠✨

理由1:変更に強くなる(壊れにくい)🧱🔧

payloadが太いほど「誰かが使ってるフィールド」が増えて、ちょっと直すだけで破壊変更 になりがち💥 イベント・スキーマの進化では「追加は基本OK(できればoptional)」みたいな運用が強い味方になるよ✅ (Solace Documentation)

理由2:個人情報・機密が混ざると事故りやすい 🙅‍♀️🔐

イベントはログ・キュー・解析基盤など いろんな場所に複製されやすい。 だから payload に個人情報を入れすぎると、削除や保管期限(retention)まで含めて難しくなりがちだよ😵‍💫 (Event-Driven)

理由3:サイズが大きいと、インフラの上限に当たりやすい 📦💣

イベント基盤には「1イベント何KBまで」みたいな制限が普通にあるよ〜 例:EventBridgeは256KB上限があるし (Amazon Web Services, Inc.)、Pub/Subはメッセージサイズ10MBなどの制限があるよ (Google Cloud Documentation) Kafkaも「大きいメッセージは非推奨/アンチパターン」寄りに語られがち🧯 (Confluent)


10.4 payloadに「入れる / 入れない」の判断ルール ✅🧭

ルールA:payloadは「意味が成立する最小の事実」だけ 🧾✨

イベント名が OrderPlaced なら、payloadは「注文が置かれたと理解できる最小セット」だけでOK!

おすすめの最低ライン例👇

  • orderId(主キー)🆔
  • customerId(関係する主体)🙋‍♀️
  • items(何を買ったか:商品IDと数量)🛒
  • totalAmountAtThatTime(その瞬間の合計:あとで変わる可能性があるなら)💰

ルールB:「参照で取れる情報」は入れない 🔎🙅‍♀️

たとえば👇

  • 商品名(productName)は、商品マスタから取れるなら入れない
  • 顧客のメール(email)は、通知側が顧客DBから取れるなら入れない

入れるのは基本 ID でOK!🪪✨

ルールC:「あとで取れない瞬間の値」は入れていい ⏳🧊

イベントは“過去の事実”。 だから あとで参照すると変わっちゃう値 は、スナップショットとしてpayloadに入れる価値があるよ!

例👇

  • 購入時の単価(あとで値上げされるかも)💸
  • 適用したクーポン(後で無効化されるかも)🎟️
  • 税率(時期で変わるかも)🧾

ルールD:個人情報(PII)は原則入れない 🙅‍♀️🪪

特に👇は避けたい:住所・電話・氏名・メール・生年月日など イベントは保存・転送・分析されやすいから、扱いが難しくなるよ🧯 (Event-Driven)

ルールE:巨大データは入れない(画像/全文/大量配列)🐘❌

「イベントは軽く」が基本! 大きいものは 別ストレージに置いて参照(ID/URL/キー) が定番だよ📦➡️🗃️ (イベント基盤のサイズ制限にも引っかかりやすいよね) (Amazon Web Services, Inc.)


10.5 例:OrderPlaced payloadを2案で比べよう ⚖️🛒✨

Payload 設計:太い情報(持ちすぎ)と薄い情報(最小限)の比較

ここでは “太い案”“最小案” を並べるよ〜!

まずイベント型(第9章の共通フォーマットのイメージ)🧾🛡️

export type DomainEvent<TType extends string, TPayload> = Readonly<{
eventId: string;
occurredAt: string; // ISO文字列 (例: "2026-01-27T12:34:56.789Z")
aggregateId: string; // ここでは orderId を入れる想定
type: TType;
payload: TPayload;
}>;

案1:太いpayload(やりがちだけど危険)🍖💥

type OrderPlacedPayload_Fat = {
order: {
orderId: string;
status: "PLACED";
customer: {
customerId: string;
name: string;
email: string;
phone: string;
};
shippingAddress: {
postalCode: string;
prefecture: string;
city: string;
line1: string;
line2?: string;
};
items: Array<{
productId: string;
productName: string;
unitPrice: number;
quantity: number;
imageUrl?: string;
}>;
totalAmount: number;
note?: string;
};
};

😱 これの問題は…

  • 個人情報がごっそり(メール・住所)🚨
  • 商品名や画像URLなど「参照で取れる」ものが多い 🔎
  • 依存が増える → 変更が怖い → schema地獄 💥
  • サイズも太りやすい 📦

案2:最小payload(おすすめ)🪶✨

「通知+必要最小限の事実」だけに寄せるよ🎒

type Money = { amount: number; currency: "JPY" };

type OrderPlacedPayload_Slim = {
orderId: string;
customerId: string;

// “意味が成立する最小の中身”
lineItems: ReadonlyArray<{
productId: string;
quantity: number;

// “あとで取れないかも”なら snapshot としてOK(例:購入時単価)
unitPriceAtThatTime?: Money;
}>;

// 合計も「購入時点の記録」として価値があることが多い
totalAmountAtThatTime: Money;

// PIIは避けて「参照キー」に寄せる
shippingAddressId?: string;

couponCodeApplied?: string;
};

✨ この案が強いところ

  • 個人情報を持ちにくい(住所はID参照)🔐
  • 利用側が「自分の都合で必要な情報を取りに行ける」🔎
  • producer側が「誰が何を欲しがるか」を背負いすぎない 🧠
  • 将来の利用者が増えても、イベントが太りにくい 🎈

「基本はキー中心にして、必要なら利用側が参照する」という考え方は、結合を増やしすぎない方向としてよく語られるよ📌 (Solace)


10.6 “参照で取れる情報” vs “今この瞬間の情報” 🔎⏳

参照で取れる情報(基本入れない)🙅‍♀️

  • productName(商品マスタ)
  • customerEmail(顧客DB)
  • categoryName(分類マスタ)
  • stock(在庫の現在値)

理由:イベントpayloadに入れると「未来の変更」で壊れたり、二重管理になりやすい💥

今この瞬間の情報(入れてOKになりやすい)✅

  • 購入時単価
  • 税率
  • 割引後の価格
  • 適用クーポン

理由:あとで変わると困る「履歴の真実」だから🧾✨


10.7 payload設計の“よくある事故” 😵‍💫💣

事故1:Entity丸ごとpayloadに詰める 🧺💥

  • 便利そうに見えるけど、フィールド追加・変更が全部破壊変更になりがち😱
  • 個人情報も混ざりやすい🚨

✅ 対策:ID+瞬間スナップショットだけ に寄せる🎒


事故2:計算済みフィールドを大量に入れて「二重の真実」になる 🧮😇➡️😱

例:totalAmount, subtotal, discountTotal, taxTotal を全部入れる → どれかズレたら、どれが正しいの?ってなる💥

✅ 対策:

  • “基準”を1つ決める(例:totalAmountAtThatTime
  • ほかは必要になったタイミングで追加(しかもoptional)📌 (Solace Documentation)

事故3:payloadが大きすぎて運用が詰む 📦🧯

基盤の上限・遅延・コスト・再送など、地味に効いてくるよ〜😵‍💫 (サイズ制限があるサービスも多い) (Amazon Web Services, Inc.)

✅ 対策:巨大データは外に置いて参照キーでつなぐ🗃️🔗


10.8 演習:OrderPlaced payloadを2案作って比較しよう ✍️⚖️✨

Step 1:まず“太い案”をあえて作る 🍖

  • 顧客情報、住所、商品名、画像…「便利そう」を全部入れてみる

Step 2:次に“最小案”を作る 🪶

  • ID中心にする(orderId, customerId, productId など)
  • “あとで取れない瞬間値”だけ残す(単価、税率、割引など)

Step 3:チェックリストで採点 ✅🧠

各項目「YESなら危険かも⚠️」って思ってね!

  • そのフィールド、別DB/別APIから取れない?(取れるならIDでよくない?)🔎
  • 個人情報が混ざってない?🙅‍♀️
  • それ、将来の仕様変更で変わりやすくない?(商品名とか)🌀
  • イベント1個が重くなりすぎてない?📦
  • “購入時点の真実”として残す価値ある?⏳
  • 利用側が増えたとき、producerが毎回イベントを変える羽目にならない?😵‍💫

10.9 AI活用(Copilot/Codex)でpayloadをスッキリさせる 🤖🪄💖

目的:まず“欲しがりリスト”を出してもらう 📝

OrderPlaced の利用者(通知/ポイント/在庫/分析)が
「欲しがりそうな情報」を列挙して。
そのうえで、payloadに入れるべき最小セット案を提案して。
PIIっぽい項目があれば指摘して。

目的:太いpayloadを“削ぎ落とし”してもらう ✂️🎒

この payload は太すぎるので、
(1) 参照で取れる情報
(2) 個人情報・機微情報
(3) 二重の真実になりそうな計算値
に分類して、最小payload案にリライトして。

目的:将来の変更に強い進化案(optional追加)を考える 🧱✨

スキーマ進化では「追加はoptionalに」が実務でよく推奨されるよ📌 (Solace Documentation)

今の OrderPlacedPayload_Slim に対して、
互換性を壊しにくい拡張(optional追加)案を3つ出して。
フィールド名の変更や削除は避けて。

10.10 まとめ 🎁✨

  • payloadは「便利」より「未来の壊れにくさ」優先が基本だよ🎒🧱
  • 参照で取れる情報はIDであとで取れない瞬間値はスナップショットで ⏳🔎
  • 個人情報は原則入れない(イベントは複製されやすい)🙅‍♀️🔐 (Event-Driven)
  • スキーマ進化は「optional追加」が強い味方✅ (Solace Documentation)
  • “薄いイベント”は結合を増やしにくく、変更にも強くしやすい🪶✨ (Solace)