Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
メッセージ駆動が可能にする結合の最適化
Search
かとじゅん
November 21, 2025
Technology
7k
10
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
メッセージ駆動が可能にする結合の最適化
かとじゅん
November 21, 2025
More Decks by かとじゅん
See All by かとじゅん
TAKTでAI駆動開発の品質を設計する
j5ik2o
6
990
終盤で崩壊させないAI駆動開発
j5ik2o
3
2.7k
CQRS/ESになぜアクターモデルが必要なのか
j5ik2o
0
2k
曖昧なプロンプトでも正しいコードが書ける理由
j5ik2o
0
520
AIコーディングエージェントの現実と設計品質の重要性
j5ik2o
0
150
なぜイベント駆動が必要なのか - CQRS/ESで解く複雑系システムの課題 -
j5ik2o
17
8.3k
アクターシステムに頼らずEvent Sourcingする方法について
j5ik2o
8
1.7k
メッセージとイベントを中核に置いたシステム設計の有用性について
j5ik2o
12
4.4k
私のキャリアの旅路: 技術をきっかけに変化を楽しむ
j5ik2o
3
1.1k
Other Decks in Technology
See All in Technology
AI Testing Talks: Challenges of Applying AI in Software Testing: From Hype to Practical Use
exactpro
PRO
1
130
Mastering Ruby Box
tagomoris
3
150
Diagnosing performance problems without the guesswork
elenatanasoiu
0
170
地元にいないローカルオーガナイザーの立ち回り
uvb_76
1
730
探して_入れて_作って_使う_Agent_Skills___LT.pdf
peintangos
2
160
ポケモンの型をTypeScriptの型システムで表現してみた
subroh0508
0
330
Unlocking the Apps
pimterry
0
240
先取りMaven4 ~16年ぶりのメジャーアップデート、その進化とは?~
ogiwarat
0
140
10倍の生産性を実現するAI駆動並列エージェントのすべて
kumaiu
4
770
Ruby::Boxでできること、Refinementsでできること
joker1007
3
400
AI Adaptable なテストを整える工夫 / Ways to Make Your Tests AI-Adaptable
bitkey
PRO
3
220
EventBridge Connection
_kensh
4
590
Featured
See All Featured
Design in an AI World
tapps
1
220
[RailsConf 2023 Opening Keynote] The Magic of Rails
eileencodes
31
10k
Balancing Empowerment & Direction
lara
6
1.1k
StorybookのUI Testing Handbookを読んだ
zakiyama
31
6.8k
How to Think Like a Performance Engineer
csswizardry
28
2.6k
SEO Brein meetup: CTRL+C is not how to scale international SEO
lindahogenes
1
2.7k
Thoughts on Productivity
jonyablonski
76
5.2k
The B2B funnel & how to create a winning content strategy
katarinadahlin
PRO
1
380
Into the Great Unknown - MozCon
thekraken
41
2.5k
Principles of Awesome APIs and How to Build Them.
keavy
128
17k
The Anti-SEO Checklist Checklist. Pubcon Cyber Week
ryanjones
0
150
Exploring anti-patterns in Rails
aemeredith
3
390
Transcript
メッセージ駆動が可能にする 結合の最適化 スケールと変化に耐えるアーキテクチャの実践 スケールと変化に耐えるアーキテクチャの実践 Press Space for next page Architecture
Conference 2025 | 2025.11.20-21 加藤潤一 (@j5ik2o) IDEO PLUS 合同会社 代表
プロフィール 加藤潤一 経歴 10 歳からプログラミング開 始 2014-2024 年: kubell (旧
Chatwork ) 2025 年1 月: 独立 (IDEO PLUS 社) 専門分野 ドメイン駆動設計(DDD ) 関数型プログラミング 分散システム設計 15 年以上のDDD 実践経験 | Chatwork の大規模リアーキテクチャを主導
注意事項 タイトルを修正して、内容も変わりました。
今日伝えたい2 つの視点 本タイトルの「結合の最適化」とは、関心を分離して疎結合にすることを指しま す。 1️⃣ 分離( 疎結合) の 3 つの次元を理解する
⏰ 時間的分離 と 🗺️ 空間的分離 と 📖 読み書き の分離 3 つの次元すべてで分離(疎結合)を実現する必 要性 2️⃣ メッセージ駆動による 統合的解決 3 つの技術による包括的なアプローチ 非同期メッセージング(時間) 位置透過性(空間) イベント/CQRS (読み書き分離) 理論と実践: Chatwork での5 年以上の本番運用から学んだ知見を共有
前提:イベントとメッセージの関係 技術的な観点から メッセージの種類 コマンド(Command ) 命令・要求を表す コマンドリクエスト コマンドレスポンス イベント(Event )
過去に起きた出来事 ドメインイベント システムイベント イベント駆動とメッセージ駆動 関係性 メッセージ駆動: より広い概念 コマンドもイベントも使う イベント駆動: メッセージ駆動の一種 主にイベントを使う このセッションでは メッセージング全般(コマンドとイベントの両方)で結 合を最適化することに焦点を当てます
疎結合の2 つの分離軸 ⏰ 時間的分離 コマンドがいつ処理されるか 呼び手と受け手を処理タイミング(同時存在) への依存から切り離す 非同期化で待ち時間を断つ 分離を阻害するもの ⚠️
メソッド呼び出し(同期) 同期API 呼び出し ブロッキングI/O 🗺️ 空間的分離 コンポーネントがどこにあるか 呼び手と受け手を物理配置・デプロイ場所への 依存から切り離す デプロイメントトポロジー非依存 分離を阻害するもの ⚠️ 配置場所への依存 デプロイメント境界 ネットワーク依存性に縛られた実装 💡 重要: 空間と時間、両方の次元で疎結合を実現する必要がある
Part 1: 時間的分離とメッセージング
モジュラーモノリスの限界 🏗️ アーキテクチャ モノリスアプリケーション 認証モジュール 注文モジュール 在庫モジュール 決済モジュール 単一プロセス 論理的な分離のみ
できていること ✅ モジュール境界が明確 パッケージ/ レイヤーで分離 インターフェースで依存管理 できていないこと ❌ 物理的な空間分離: 単一プロセス → 独立デプロ イ不可 時間的分離: 同期呼び出し → 呼び出し元がブロ ック 😰 結果として スケールアップしかできない 一部の障害が全体に影響 デプロイが密結合(一括デプロイ) ⏰🗺️ 時間と空間
【最初に考えること】同期呼び出しのトレードオフ 同期API 呼び出し 同一故障単位(共倒れリスク) サービスC サービスB サービスA サービスC サービスB サービスA
A はブロック B もブロック 同期リクエスト 同期リクエスト レスポンス レスポンス ⚖️ トレードオフ ✅ 同一故障単位内なら最適 シンプルなプログラミングモ デル 強い整合性保証 トランザクション境界が明確 ⚠️ 故障単位を超えると課題 共倒れ(連鎖障害) 全サービス同時稼働必須 可用性への影響 ⏰ 時間的分離
メッセージ駆動による分離 非同期メッセージング サービスB の故障単位 サービスA の故障単位 サービスB メッセージ基盤 サービスA サービスB
メッセージ基盤 サービスA すぐに次の処理へ A への返信は不要 (Fire-and-Forget) メッセージ送信 他の処理を継続 メッセージ配信 処理実行 ACK 🎯 適用場面 故障単位を分離したい場合 共倒れを避けたい 独立した可用性が必要 段階的なデグレードを許容 🎯得られること 1. 障害の分離: B の障害がA に波及しな い 2. 独立した可用性: サービス個別に稼 働可能 3. 柔軟なスケーリング: 負荷に応じた 個別調整 ⏰ 時間的分離
イベント購読によるリードモデル構築 パターン サービスA の故障単位 サービスB の故障単位 A のリードモデル サービスA メッセージ基盤
サービスB A のリードモデル サービスA メッセージ基盤 サービスB B のデータを ローカル保持 B が故障 ドメインイベント発行 イベント購読 リードモデル更新 読み取り継続可能 🎯 API 呼び出しとの違い 従来(Pull 型): サービスA に都度問い合わせ A が故障すると読み取り不可 イベント購読(Push 型): イベントを購読してローカル保持 A が故障しても読み取り継続可能 得られること 1. 故障の分離: A の障害がB の読み取り に影響しない 2. 独立した可用性: B は自律的に稼働可 能 3. 結果整合性の受容: 若干の遅延を許 容して可用性を優先 ⏰ 時間的分離
Kafka/Kinesis などによるメッセージング 外部ミドルウェアの活用 サービスB メッセージ基盤(Kafka/Kinesis) サービスA サービスB メッセージ基盤(Kafka/Kinesis) サービスA 永続化
順序保証 複数の購読者 対応可能 イベント発行 イベント配信 処理実行 🎯 特徴 ミドルウェアに依存 永続化とリプレイ機能 高い信頼性とスケーラビリティ マルチテナント対応 配送保証: at-least-once (最低1 回配信) トレードオフ メリット 確実なメッセージ配信 複数購読者への配信 運用実績のある基盤 コスト インフラ運用の負荷 追加のミドルウェア依存 学習コストと複雑性 ⏰ 時間的分離
アクターシステムによるメッセージパッシング (1/2) コード例 送信側アクター 受信側アクター ⏰ 時間的分離 object OrderProcessManager {
sealed trait Command case class SubmitOrder( items: List[String] ) extends Command def apply( stockActor: ActorRef[StockActor.Command] ): Behavior[Command] = Behaviors.receiveMessage { case SubmitOrder(items) => val orderId = generateOrderId() // メッセージ送信(ノンブロッキング) stockActor ! ReserveStock(orderId, items) // 即座に次の処理へ Behaviors.same } } object StockActor { sealed trait Command case class ReserveStock( orderId: String, items: List[String] ) extends Command def apply(): Behavior[Command] = Behaviors.receiveMessage { case ReserveStock(orderId, items) => // 在庫引当処理(非同期) reserveStock(orderId, items) Behaviors.same } }
アクターシステムによるメッセージパッシング (2/2) メッセージ配信の仕組み 受信側アクター ディスパッチャ 受信側メールボックス 受信側アクター参照 送信側アクター 受信側アクター ディスパッチャ
受信側メールボックス 受信側アクター参照 送信側アクター 即座に制御を返す ( ノンブロッキング) ! メッセージ エンキュー デキュー メッセージ配信 処理実行 ( 非同期) ⏰ 時間的分離
Part 2: 空間的分離と位置透過性
従来のアプローチの問題 スケールアップ 問題点: 単一プロセス内でしか動作しない スレッド数の制限がある 分散環境では使えない スケールアウト 問題点: ネットワークエラー処理が必要 ローカルとは異なるAPI
レイテンシとタイムアウト管理 😰 根本的な問題 スケールアップとスケールアウトで異なるプログラミングモデル → コードの重複、保守性の低下、アーキテク チャ変更時の大規模な書き換え 🗺️ 空間的分離 final ExecutorService executor = Executors.newFixedThreadPool(10); CompletableFuture.supplyAsync(() -> { // 非同期でタスクを実行 return orderRepository.save(order); }, executor); HttpPost post = new HttpPost("http://host/api/orders"); post.setEntity(new StringEntity( toJson(order), ContentType.APPLICATION_JSON )); CloseableHttpResponse response = httpClient.execute(post);
統一的なプログラミングモデル 同一のコード ✨ メリット 1. アーキテクチャの進化 単一プロセス → 分散クラスタへコード変更なし 2.
スケーリング戦略の統一 垂直・水平スケーリングが同じモデル 3. 境界の柔軟な調整 モノリス ⇄ マイクロサービス間の移行が容易 🗺️ 空間的分離 object OrderActor { sealed trait Command case class CreateOrder(order: Order, replyTo: ActorRef[OrderCreated]) extends Command case class OrderCreated(result: Result) def apply(): Behavior[Command] = Behaviors.receiveMessage { case CreateOrder(order, replyTo) => val result = processOrder(order) replyTo ! OrderCreated(result) Behaviors.same } } // 配置方法が違っても同じコード orderActorRef ! CreateOrder(order, replyTo)
位置透過性とは コンポーネントの物理的な位置を意識せずに設計 できること 同一プロセス内(ローカル) 別プロセス・別マシン(リモート) なぜ重要か 1. デプロイメントトポロジーからの独立 同じコードで単一プロセス〜分散システム 2.
進化可能性 モノリス → マイクロサービス移行が容易 スケーリング戦略の変更が柔軟 3. コンテキスト境界の見直し 実装を変えずに境界を調整 🗺️ 空間的分離
位置透過性を支える仕組み(ローカル配送) アクターB (同一ノード) ディスパッチャ メールボックスB アクターA アクターB (同一ノード) ディスパッチャ メールボックスB
アクターA シリアライズ不要 / 最小レイテンシ メッセージ送信 デキュー 配信 処理実行 🗺️ 空間的分離
位置透過性を支える仕組み(リモート配送) システムB システムA アクターB ディスパッチャ メールボックスB リモート機構 ネットワーク リモート機構 アクターA
アクターB ディスパッチャ メールボックスB リモート機構 ネットワーク リモート機構 アクターA コードは同じ / 経路が自動で選択される メッセージ送信 シリアライズ・送信 受信 デシリアライズ デキュー 配信 処理実行 🗺️ 空間的分離
DDD の集約とアクターモデル モジュラーモノリス 同一プロセス ローカル配送 ローカル配送 «Actor» 注文プロセスマネージャ «Actor» 在庫集約
«Actor» 決済集約 クライアント マイクロサービス化 決済MS 在庫MS 注文MS リモート配送 リモート配送 «Actor» 注文プロセスマネージャ «Actor» 在庫集約 «Actor» 決済集約 クライアント ✨ 位置透過性の威力 注文プロセスマネージャのコードは全く変わらない: 在庫集約・決済集約の配置場所が変わっても、ActorRef が適切な経路を自動選択。 ビジネスロジックに一切の 変更が不要で、アーキテクチャが段階的に進化可能になります。 ⏰🗺️ 時間と空間の統合 stockAggregateRef ! ReserveStock(orderId, items) paymentAggregateRef ! ProcessPayment(orderId, amount)
Part 3: イベントによってクエリの結合度を下げる
クエリ要件がドメインモデルを複雑にする 保持 1 * GroupChat -GroupChatId id -GroupChatName name -Members
members -Messages messages +postMessage(content) Message +MessageId id +MessageBody content +Member author +DateTime sentAt 課題: メンバーやメッセージを集約内で無限に保持す ることはできない 特にメッセージ本文は容量が大きく、メモリを 圧迫する ジレンマ: 集約内に保持する → メモリ消費が増大、スケー ラビリティの問題 外部集約として分離する → 振る舞いの実装が複 雑化 解決の方向性: イベントを活用して読み取り責務を分離 する
集約アクターの設計 📥 コマンド(Command ) クライアントの意図を表現 検証が必要 例: PostMessage , DeleteMessage
, DeleteGroupChat 📤 イベント(Event ) 発生した事実を記録 過去形で命名 例: MessagePosted , MessageDeleted , GroupChatDeleted ⏰🗺️ 時間と空間の統合 集約アクターのインターフェースは、CRUD ではなくドメインの語彙で表現します。コマンドとイベントを通じて、 業務の意図を明確に伝えることができます。 クライアント CreateGroupChat PostMessage DeleteMessage DeleteGroupChat ≪Actor≫ GroupChat GroupChatCreated MessagePosted MessageDeleted GroupChatDeleted イベントストア
読み取り要求をイベントに委譲する 従来のアプローチ 保持 1 * GroupChat -GroupChatId id -GroupChatName name
-Members members -Messages messages +postMessage(content) Message +MessageId id +MessageBody content +Member author +DateTime sentAt 課題: 集約が Messages 全体を保持し、読み取り要求 に直接応答するため、メモリ消費とスケーラビリティに 課題がある。 読み取り要求を委譲するアプローチ 保持 1 * GroupChat -GroupChatId id -GroupChatName name -Members members -MessageIdAndAuthors messageAndAuthors +postMessage(content) : Tuple<GroupChatEvent, GroupChat> «interface» GroupChatEvent MessageIdAndAuthor +MessageId id +Member author +DateTime sentAt MessagePosted +EventId id +GroupChatId aggregateId +MessageId messageId +MessageBody content +Member author +DateTime sentAt 改善点: メッセージ本体をイベントに委譲。集約はID と 著者のみ保持し、読み取りはリードモデルで対応。 ⏰🗺️ 時間と空間 前述のイベントをうまく使えば、読み取り要求に伴う結合度を下げることができます。
CQRS( コマンド・クエリ責務分離) 非CQRS 特徴: 読み取りと書き込みが同じモデル シンプルだが、複雑化すると保守が困難 スケーリングの柔軟性が低い CQRS 特徴: 読み取りと書き込みを分離
それぞれ独立に最適化可能 複雑性は増すが、スケーラビリティが向上 ドメインモデルはドメイン状態を変える書き込み(コマンド)要求に集中し、読み取り(クエリ)要求は別のシステ ムに委譲することをCQRS と呼びます。
CQRS/Event Sourcing Command とQuery をEvent で結合するだけなら通常のPub/Sub と変わりません。Event Sourcing の本質は、集約の状 態そのものがEvent
に基づいて構築される点にあります。
Chatwork での実践例 Falcon プロジェクト 2016 年リリース: CQRS/ES システム 5 年以上の本番運用実績
Akka アクターベース Event Sourcing + CQRS 成果 高可用性とスケーラビリティを実現 モノリスから段階的にマイクロサー ビス化 メッセージ駆動の有効性を実証 ⏰🗺️ 実践
参考:実践で使えるツール Apache Pekko JVM 向けアクターシステム Akka のフォーク(Apache 2.0 ) 位置透過性、Event
Sourcing 対応 Scala/Java apache/pekko Proto.Actor (Go) Go 向けアクターシステム Go 言語で実装されたアクター 高性能・軽量 クラスタリング、リモーティング対応 asynkron/protoactor-go event-store-adapter アクターレスなCQRS/ES アクター不要のEvent Sourcing シンプルで導入が容易 Java/Scala/Kotlin/TS/PHP/Go/Rust j5ik2o/event-store-adapter fraktor-rs 🚧 Rust 版アクターシステム Pekko 相当の機能を提供予定 Rust 初の本格的実装(cluster 機能開発中) tokio/embassy/wasm 対応 j5ik2o/fraktor-rs 💡 **JVM**: Pekko / **Go**: Proto.Actor / ** 多言語ES**: Event Store Adapter / **Rust**: fraktor-rs (開発中)
ご清聴ありがとうございました 質疑応答 加藤潤一 (@j5ik2o) GitHub: https://github.com/j5ik2o X (Twitter): https://x.com/j5ik2o
None