Upgrade to Pro — share decks privately, control downloads, hide ads and more …

イベントソーシングによってインピーダンスミスマッチから解放された話

 イベントソーシングによってインピーダンスミスマッチから解放された話

イベントソーシング・CQRS勉強会 #1 - イベントソーシングやってみた報告会
https://sekiban.connpass.com/event/343929/

株式会社ジェイテックジャパン
Developer Advocate for Sekiban 川江貴志

イベントソーシングを使うと、OOP & RDB の開発で一般的に生じるインピーダンスミスマッチがなくなります。そう言える理由と、それがアプリケーション開発にもたらした効果を、実際の開発体験を基にご紹介します。

Takashi Kawae

March 14, 2025
Tweet

Other Decks in Programming

Transcript

  1. インピーダンスミスマッチの例 オブジェクト 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 は階層構造を直接表現できず、複数のテーブルに分割しなくてはならない
  2. インピーダンスミスマッチが引き起こす問題 パフォーマンスの低下 オブジェクトとデータベース間の変換処理のオーバーヘッド N+1 問題 (親オブジェクト取得後、子オブジェクトごとに追加クエリが発生) 複雑な階層や継承関係を持つデータモデルを SQL 変換する際の非効率性 開発効率の低下

    オブジェクトモデルとデータベースモデルの両方の実装・テストが必要 ビジネスロジックに直接関係のない変換コードの実装が必要 統合テストの複雑化や実行時間の増加 データベーススキーマ変更とオブジェクトモデル変更の同期も必要
  3. 緩和する手法と、それらの問題点 O/R マッパー 最適化されていない SQL クエリによるパフォーマンス低下の可能性 データ構造に変更が生じる場合、マッピング定義も作り直し DDD (ドメイン駆動設計) ドメインモデルを中心とした設計アプローチとリポジトリパターンにより、デー

    タアクセスロジックをビジネスロジックから分離することはできる インピーダンスミスマッチの「解消」ではなく「隠蔽」であり、インピーダンス ミスマッチはリポジトリの実装内部に移動しただけで依然として存在している → パラダイムの根本的な違いは解消できない
  4. オブジェクトの形式のままデータを保存してみたら? オブジェクト 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 でも構わない
  5. でも別の問題が... 保存されている 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 マッパーを使うのと変わらない
  6. なぜこうなってしまうのか? 原因: データの状態を保存しているから アプリケーションは通常、ある時点におけるシステムのデータの状態 (ステートデ ータ) を保存している (ステートソーシングと呼ぶ) ステートデータは、処理のパラメータやロジックに変更があった場合にその影響 を受ける、つまり変更が必要になる可能性がある

    アプリケーション開発中も開発後もビジネス環境は常に変化する、それゆえビジ ネスロジックも絶えず修正される可能性がある ステートデータをシリアライズして保存するやり方は、もともとのインピーダン スミスマッチはなくせても、変更容易性の問題が残る → ステートではなくイベントを保存すれば良い
  7. イベントとプロジェクション: 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" }[]; }
  8. イベントとプロジェクション: 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; }