Slide 1

Slide 1 text

イベントソーシングによって インピーダンスミスマッチから 解放された話 イベントソーシング・CQRS 勉強会 #1 2025年 3月 14日 株式会社ジェイテックジャパン Developer Advocate for Sekiban 川江貴志

Slide 2

Slide 2 text

インピーダンスミスマッチとは? 元々は電気工学用語、信号の経路にインピーダンスが異なる箇所があると、信号 の一部が伝達されない現象 ソフトウェア開発では、異なるモデルや技術間 (境界面) においてデータの表現や 処理方法に不整合が生じることで発生する問題のこと 特に、オブジェクト指向プログラミング (OOP) とリレーショナルデータベース (RDB) の間の不一致を指して良く使われる O/R (Object-Relational) インピーダンスミスマッチとも呼ばれる

Slide 3

Slide 3 text

インピーダンスミスマッチの例 オブジェクト interface Person { id: number; name: string; addresses: { postalCode: string; address: string; addressType: "home" | "work" }[]; } リレーショナルデータ Address ID (PK) PersonID (FK) PostalCode Address AddressType Person ID (PK) Name オブジェクトは階層的なデータ構造にできる RDB は階層構造を直接表現できず、複数のテーブルに分割しなくてはならない

Slide 4

Slide 4 text

インピーダンスミスマッチが引き起こす問題 パフォーマンスの低下 オブジェクトとデータベース間の変換処理のオーバーヘッド N+1 問題 (親オブジェクト取得後、子オブジェクトごとに追加クエリが発生) 複雑な階層や継承関係を持つデータモデルを SQL 変換する際の非効率性 開発効率の低下 オブジェクトモデルとデータベースモデルの両方の実装・テストが必要 ビジネスロジックに直接関係のない変換コードの実装が必要 統合テストの複雑化や実行時間の増加 データベーススキーマ変更とオブジェクトモデル変更の同期も必要

Slide 5

Slide 5 text

緩和する手法と、それらの問題点 O/R マッパー 最適化されていない SQL クエリによるパフォーマンス低下の可能性 データ構造に変更が生じる場合、マッピング定義も作り直し DDD (ドメイン駆動設計) ドメインモデルを中心とした設計アプローチとリポジトリパターンにより、デー タアクセスロジックをビジネスロジックから分離することはできる インピーダンスミスマッチの「解消」ではなく「隠蔽」であり、インピーダンス ミスマッチはリポジトリの実装内部に移動しただけで依然として存在している → パラダイムの根本的な違いは解消できない

Slide 6

Slide 6 text

オブジェクトの形式のままデータを保存してみたら? オブジェクト const taro: Person = { id: 1, name: "Taro Yamada", addresses: [ { postalCode: "100-0005", address: "東京都千代田区丸の内", addressType: "work" } ] } JSON { "id": 1, "name": "Taro Yamada", "addresses": [ { "postalCode": "100-0005", "address": "東京都千代田区丸の内", "addressType": "work" } ] } オブジェクトをシリアライズして保存すれば不整合はほぼなくなる 保存するデータベースは RDB でも NoSQL でも構わない

Slide 7

Slide 7 text

でも別の問題が... 保存されている JSON { "id": 1, "name": "Taro Yamada", "addresses": [ { "postalCode": "100-0005", "address": "東京都千代田区丸の内", "addressType": "work" } ] } 変更されたオブジェクト定義 interface Person { id: number; familyName: string; firstName: string; addresses: { postalCode: string; address: string; addressType: "home" | "work" }[]; email: string; } オブジェクトの定義が変更されるとデシリアライズできなくなる可能性がある カスタムコンバーターを作るのなら、O/R マッパーを使うのと変わらない

Slide 8

Slide 8 text

なぜこうなってしまうのか? 原因: データの状態を保存しているから アプリケーションは通常、ある時点におけるシステムのデータの状態 (ステートデ ータ) を保存している (ステートソーシングと呼ぶ) ステートデータは、処理のパラメータやロジックに変更があった場合にその影響 を受ける、つまり変更が必要になる可能性がある アプリケーション開発中も開発後もビジネス環境は常に変化する、それゆえビジ ネスロジックも絶えず修正される可能性がある ステートデータをシリアライズして保存するやり方は、もともとのインピーダン スミスマッチはなくせても、変更容易性の問題が残る → ステートではなくイベントを保存すれば良い

Slide 9

Slide 9 text

イベントソーシングだったら? インピーダンスミスマッチはなくなる イベントデータはシリアライズして保存される イベントデータは必ずデシリアライズできる (というよりできなくてはいけない) 既存イベントの定義は変更しない イベントデータは追加のみ、変更や削除は許されない 変更に強い 更新系処理の変更には、新たなイベント定義を追加して対応する 読み取り系処理の変更には、プロジェクションの変更や追加で対応する

Slide 10

Slide 10 text

イベントとプロジェクション: 1 イベント interface PersonCreated { id: number; name: string; } interface AddressAdded { postalCode: string; address: string; addressType: "home" | "work" } プロジェクション interface Person { id: number; name: string; addresses: { postalCode: string; address: string; addressType: "home" | "work" }[]; }

Slide 11

Slide 11 text

イベントとプロジェクション: 2 イベント interface NameSplit { familyName: string; firstName: string; } interface EmailAdded { email: string; } プロジェクション interface Person { id: number; familyName: string; firstName: string; addresses: { postalCode: string; address: string; addressType: "home" | "work" }[]; email: string; }

Slide 12

Slide 12 text

実際にイベントソーシングで開発をした感想 開発の作業量がかなり減った DB のスキーマ設計、パフォーマンスチューニング リポジトリ、DAO、O/R マッピング... インピーダンスミスマッチがあるために、ビジネスロジックではないコードをど れだけ沢山書いていたかが実感できる 仕様の追加や変更が辛くなくなった コードの修正が少なくなり、追加で対応できることが多いので、対応しやすい

Slide 13

Slide 13 text

終わりに Sekiban でイベントソーシング&CQRS をぜひ体験してみてください! https://github.com/J-Tech-Japan/Sekiban