89
JJUG CCC 2017 fall DDD x CQRS - 更新系と参照系で異なるORMを併用して上手くいった話 2017/11/18 株式会社ビズリーチ 松岡 幸一郎

DDD x CQRS 更新系と参照系で異なるORMを併用して上手くいった話

Embed Size (px)

Citation preview

Page 1: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

JJUG CCC 2017 fallDDD x CQRS - 更新系と参照系で異なるORMを併用して上手くいった話

2017/11/18株式会社ビズリーチ

松岡 幸一郎

Page 2: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

● 松岡 幸一郎

● 株式会社ビズリーチ

● @little_hand_s● #ccc_m4

発表者紹介

Page 3: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

最近こんなCMしてる会社です

自己紹介

Page 4: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

Java

技術遍歴

SIer時代

Page 5: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

Java

技術遍歴

SIer時代

Excel

Page 6: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

技術遍歴

Excelはもういい

Page 7: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

Java

技術遍歴

SIer時代

ExcelSpring BootDDD・CQRSVue.js

現職

Page 8: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

DDD x CQRS

更新系と参照系で

異なるORMを併用して

上手くいった話

テーマ

Page 9: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

DDD x CQRS

更新系と参照系で

異なるORMを併用して

上手くいった話

テーマ

Page 10: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

ブログで細々とDDD布教中

http://little-hands.hatenablog.com/

Page 11: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

なんですが

Page 12: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

DDD x CQRS

更新系と参照系で

異なるORMを併用して

上手くいった話

テーマ

←今日のメイン

Page 13: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

DDDとは

● Domain Driven Design(ドメイン駆動設計)の略称

● ドメインとは

○ 「アプリケーションの中心となる業務領域」のこと

● 原則

○ ドメインとドメインロジックを中心に設計する ( ≠ データモデル中心)○ 複雑なロジックをドメインモデルに寄せる (オブジェクト志向に則る)○ ドメインエキスパート(業務の専門家)と継続的にコミュニケーションし、モデルを改善し続ける

● メリット

○ ステークホルダー間のコミュニケーションが容易になる

○ ソースの可読性、変更容易性、メンテナンス性が高まる

● EricEvansによる定義

Page 14: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

DDDとは

● Domain Driven Design(ドメイン駆動設計)の略称

● ドメインとは

○ 「アプリケーションの中心となる業務領域」のこと

● 原則

○ ドメインとドメインロジックを中心に設計する ( ≠ データモデル中心)○ 複雑なロジックをドメインモデルに寄せる (オブジェクト志向に則る)○ ドメインエキスパート(業務の専門家)と継続的にコミュニケーションし、モデルを改善し続ける

● メリット

○ ステークホルダー間のコミュニケーションが容易になる

○ ソースの可読性、変更容易性、メンテナンス性が高まる

● EricEvansによる定義

Page 15: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

DDD x CQRS

更新系と参照系で

異なるORMを併用して

上手くいった話

テーマ

Page 16: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

まず、

CQRSに関する誤解を解きたい

テーマ

Page 17: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

まず、

CQRSに関する誤解を解きたい

テーマ

(※個人の見解です)

Page 18: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

CQRSはイベントソーシングと

セットで行う必要がある

テーマ

Page 19: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

CQRSはイベントソーシングと

セットで行う必要がある

テーマ

Page 20: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

CQRSはイベントソーシングと

セットで行う必要がある

テーマ

CQRSは単独で適用できます

Page 21: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

CQRSとは

Page 22: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

● CQRSとは

○ Command Query Responsibility Segregation コマンドクエリ責務分離

○ 書き込み用のモデルと読み込み用のモデルを分ける設計パターン

CQRSとは

Page 23: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

CQRSの提唱者

● Greg Young○ DDD + CQRS + イベントソーシングを推している

そのため、CQRSとイベントソーシングがセットで語られがち

→ 本質的には別のもの

Page 24: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

● なぜモデルを分ける必要があるのか?

● モデルを分けるとはどういうことなのか?

→ 背景から説明します

CQRS背景

Page 25: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

● 一般的なシステムでは、書き込みと読み込みの両方が、

単一のデータストアで同じモデルを使用する

● テーブルに対応したオブジェクトがあり、それを使用してCRUD操作する

CQRS背景

DBData ModelUser Interface

Application

IF

IF

Page 26: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

● システムが大きくなるほど、

○ 書き込み:制御は複雑になっていく

○ 読み込み:複数テーブルの情報をまとめて加工する必要性が高まっていく

→ 一つのモデルに関する処理がどんどん複雑になっていく

CQRSが必要な背景

Page 27: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

● システムが大きくなるほど、

○ 書き込み:制御は複雑になっていく

○ 読み込み:複数テーブルの情報をまとめて加工する必要性が高まっていく

→ 一つのモデルに関する処理がどんどん複雑になっていく

CQRSが必要な背景

● そもそも、書き込み・読み込みで要件が大きく異なる

○ 整合性か、速度か

○ オブジェクトの形か、結合や集計した形か(モデル表現の違い)○ トラフィック数は圧倒的に読み込み処理が多い

○ パフォーマンス要件は異なることが多い

  → どこかにしわ寄せ、妥協が発生する

Page 28: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

CQRSのステップ

Page 29: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

CQRSのステップ

● 段階的CQRSa. 書き込み / 読み込みモデルを分離する

b. 書き込み / 読み込みデータストアを分離する

c. イベントソーシングと統合する

 → 要件を見極めてどうするか判断すればよい

Page 30: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

CQRS - 1.単一物理データストアモデル

● 書き込みと読み込みのモデルを別物として用意する

DBData Model

User Interface

Application

IF

IF

Page 31: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

CQRS - 1.単一物理データストアモデル

● 書き込みと読み込みのモデルを別物として用意する

DBData Model

User Interface

Application

IF

IFDB

User Interface

IF

IF

Write Model

Read Model

Page 32: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

CQRS - 1.単一物理データストアモデル

● 書き込みと読み込みのモデルを別物として用意する

● 書き込みモデル:

テーブル毎に対応したエンティティ等

● 読み込みモデル:

テーブルをJoinした結果、SQL viewの取得結果1行などを1モデルとする

 → それぞれ適したモデルを扱えるので、処理効率が良い書き方ができる

  コードがシンプルになる

DBData Model

User Interface

Application

IF

IFDB

User Interface

IF

IF

Write Model

Read Model

Page 33: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

CQRS - 2.複数物理データストアモデル

● 書き込みと読み込みのデータストアを物理的に分離する

DBUser Interface

IF

IF

Write Model

Read Model

Page 34: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

CQRS - 2.複数物理データストアモデル

● 書き込みと読み込みのデータストアを物理的に分離する

DBUser Interface

IF

IF

Write Model

Read Model

Write Data Store

Read Data Store

User Interface

IF

IF

Write Model

Read Model

Page 35: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

CQRS - 2.複数物理データストアモデル

● 書き込みと読み込みのデータストアを物理的に分離する

● Read Data Store○ シンプルなのはread-onlyのレプリカ

○ 全く別の機構を選択することも可能 (Readをelastic searchにするなど)

 → 参照/更新のストア分離により、それぞれの負荷に合わせたスケーリングが可能

  異なるアーキテクチャのデータストアを利用可能

DBUser Interface

IF

IF

Write Model

Read Model

Write Data Store

Read Data Store

User Interface

IF

IF

Write Model

Read Model

Page 36: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

● すべてのアクションを「イベント」として記録する設計パターン

既存のデータのupdateは一切しない

○ Writeモデルは「イベント」として記録される

○ Readモデルはそこから特定の形に変形される

MaterializedViewや物理的なデータを別途生成する、など

CQRS - 3.イベントソーシング

(@little_hand_s から参考資料たどれます「イベントソーシングの参考資料」 )

Page 37: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

Write/ReadでORMを分ける

Page 38: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

分離のメリット・デメリット

メリット デメリット

1.モデル分離 処理効率が良い書き方ができるコードがシンプルになる

readモデルにwriteモデルの制約を効かせられなくなる

2.データソース分離

Read / Writeを分離してスケールさせることができる設計の幅が広がる

データ同期の仕組構築 /メンテコストが必要

3.イベントソーシング

データ追跡しやすいミューテーション排除によるバグ抑止インピーダンスミスマッチとの決別

導入難易度がが一気に上がる導入・教育コストが高い

Page 39: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

分離のメリット・デメリット

メリット デメリット

1.モデル分離 処理効率が良い書き方ができるコードがシンプルになる

readモデルにwriteモデルの制約を効かせられなくなる

2.データソース分離

Read / Writeを分離してスケールさせることができる設計の幅が広がる

データ同期の仕組構築 /メンテコストが必要

3.イベントソーシング

データ追跡しやすいミューテーション排除によるバグ抑止インピーダンスミスマッチとの決別

導入難易度がが一気に上がる導入・教育コストが高い

→ ORMも適正に合わせて使い分ければ、このメリットをより大きくできるのではないか?

Page 40: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

CQRSのオプション

各オプションは要件に応じて好きに組み合わせて良い

単一物理ストア

複数物理ストア

データストア

Read / Write異なるモデル

Read / Write同じモデル

モデル

×

しない

する

イベントソーシング

×

単一ORM

複数ORM

ORM

×

Page 41: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

CQRSのオプション

各オプションは要件に応じて好きに組み合わせて良い

単一物理ストア

複数物理ストア

データストア

Read / Write異なるモデル

Read / Write同じモデル

モデル

×

しない

する

イベントソーシング

×

→ メリット/デメリット、コストを考慮し、今回はこのような構成を選択

単一ORM

複数ORM

ORM

×

Page 42: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

Write/Read ORMの要件

● Write Model ORM○ オブジェクト志向でモデルに振る舞いをもたせたい

・DDDの思想を反映

○ テーブルに対応したオブジェクト

Page 43: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

Write/Read ORMの要件

● Write Model ORM○ オブジェクト志向でモデルに振る舞いをもたせたい

・DDDの思想を反映

○ テーブルに対応したオブジェクト

● Read Model ORM○ 複数テーブルをjoinしたり、集計したりしたい

○ 効率の良いクエリが書きたい

○ 実行されるクエリを制御したい

Page 44: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

Write/Read ORMの要件

● Write Model ORM○ オブジェクト志向でモデルに振る舞いをもたせたい

・DDDの思想を反映

○ テーブルに対応したオブジェクト

● Read Model ORM○ 複数テーブルをjoinしたり、集計したりしたい

○ 効率の良いクエリが書きたい

○ 実行されるクエリを制御したい

 → この要件に合うようにORMを選定する

Page 45: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

ORMのパターン

中心 SQLロジックの組み込み方法 代表的プロダクト

オブジェクト中心

オブジェクトリレーショナルマッピングを通じてJavaに組み込む

Hibernate、(ActiveRecord)

SQL中心 Java外 - XMLなどの設定ファイル MyBatis、SQL view、ベンダー特有のストアドプロシージャ

Java内 - String文字列 JDBC、JPAネイティブクエリ

Java内 - 独自DSL jOOQ、JPQL

Page 46: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

ORMのパターン

中心 SQLロジックの組み込み方法 代表的プロダクト

オブジェクト中心

オブジェクトリレーショナルマッピングを通じてJavaに組み込む

Hibernate、(ActiveRecord)

SQL中心 Java外 - XMLなどの設定ファイル MyBatis、SQL view、ベンダー特有のストアドプロシージャ

Java内 - String文字列 JDBC、JPAネイティブクエリ

Java内 - 独自DSL jOOQ、JPQL

 → 先述の要件を踏まえ、3つのORMでメリット/デメリット検討

Page 47: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

jOOQとは

● DBスキーマからDBアクセス用オブジェクト生成

● DSL中心でクエリビルド、実行できるORM● かなりSQLに近い書き方、かつタイプセーフにSQLが書ける

Page 48: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

メリット デメリット

Hibernate(Spring Data JPA)

モデルに振る舞いを持たせやすいSpringDataJPAがDDDを想定した仕様

仕様の理解しにくさ、キャッシュなどのトラブ

ル、想定外の挙動 etc..

MyBatis SQLを直接かけるのでシンプル、安心 SQLがテキスト記述、XMLに設定を書くのは今時結構辛い

jOOQ タイプセーフなDSLで書きやすい、読みやすい

オブジェクトリレーションの表現に制限あり

ORM選定

Page 49: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

メリット デメリット

Hibernate(Spring Data JPA)

モデルに振る舞いを持たせやすいSpringDataJPAがDDDを想定した仕様

仕様の理解しにくさ、キャッシュなどのトラブ

ル、想定外の挙動 etc..

MyBatis SQLを直接かけるのでシンプル、安心 SQLがテキスト記述、XMLに設定を書くのは今時結構辛い

jOOQ タイプセーフなDSLで書きやすい、読みやすい

オブジェクトリレーションの表現に制限あり

ORM選定

→Writer Model ORMにHibernate、Read Model ORMにjOOQを採用

Page 50: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

CQRSを適用するためのアーキテクチャ

Page 51: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

● 一般的な3層アーキテクチャの問題

○ Write/Readのモデル使用範囲を明確に分けられない

○ BusinessLogic層がFatになりやすい

アーキテクチャ

Business Logic

Data

User Interface

Infrastructure

Page 52: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

● 業務ロジックをドメイン層に凝集する

● アプリケーションサービス層はドメイン層が許可した操作を必要に応じて呼ぶ

● この辺りはDDDの思想に基づいた設計

アーキテクチャ

Business Logic

Data

User Interface

Infrastructure

ApplicationService

DomainModel

User Interface

Infrastructure

(@little_hand_s から参考資料たどれます 「ドメイン駆動 + オニオンアーキテクチャ概略」 )

Page 53: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

interface

アーキテクチャ

ApplicationService

DomainModel

User Interface

Infrastructure

ApplicationService

DomainModel

User Interface

Infrastructure実装クラス

● ドメイン層をPOJOにするために依存関係を逆転する

● ドメイン層が公開したIFに対してインフラ層が実装する設計

Page 54: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

● ドメイン層をWrite Model (ドメインモデル)とRead Modelに分割する

● インフラ層の実装クラスをWriteとReadで異なるORMで実装する

アーキテクチャ

ApplicationService

DomainModel

QueryModel

User Interface

Infrastructure

ApplicationService

DomainModel

User Interface

Infrastructure

interface

実装クラス

Page 55: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

アーキテクチャ

ApplicationService

DomainModel

QueryModel

User Interface

Infrastructure

<<interface>>Repository

Entity

Repository Impl

<<interface>>Query Serivce

DTO

Query SerivceImpl

Page 56: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

Spring Data JPA

アーキテクチャ

ApplicationService

DomainModel

QueryModel

User Interface

Infrastructure

<<interface>>Repository

Entity

Repository Impl

<<interface>>Query Serivce

DTO

Query SerivceImplHibernate

Hibernate(アノテーションだけ )

Page 57: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

Spring Data JPA

アーキテクチャ

ApplicationService

DomainModel

QueryModel

User Interface

Infrastructure

<<interface>>Repository

Entity

Repository Impl

<<interface>>Query Serivce

DTO

Query SerivceImpl jOOQHibernate

Hibernate(アノテーションだけ )

Page 58: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

サンプルコード

Page 59: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

サンプルコード

TaskタスクIDタスク名ステータス

UserユーザーIDユーザー名

作成者

担当者

● シンプルなタスク管理アプリケーションを想定

● タスクの制約

○ 作成時の制約

■ 「作成者」と「担当者」として実在のユーザーを持つ

■ 常に「未完了」状態で作成

○ 変更時の制約

■ 名前は変更できない

■ 完了/未完了の制御だけできる

Page 60: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

アーキテクチャ

ApplicationService

DomainModel

QueryModel

User Interface

Infrastructure

<<interface>>Repository

Entity

Repository Impl

<<interface>>Query Serivce

DTO

Query SerivceImpl

Hibernate(アノテーションだけ )

Page 61: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

サンプルコード Domain Model(entity, repository)Entity

Repository

Page 62: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

ポイント

①entityが保持する値はUserのIdでLong型だが、

引数をUserにすることでTypeSafeになる

②必ず"未完了"の状態でインスタンス生成、

 という制約をコンストラクタで表現

③ステータス更新用のメソッドは公開されているが、

nameは更新用メソッドを公開しないことにより

「名前を変更できない」という制約を表現

コンストラクタと公開メソッドで制約を表現

コンストラクタ

状態遷移用メソッド

(@little_hand_s から参考資料たどれます 「モデルでドメイン知識を表現するとは何か」 )

Page 63: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

サンプルコード Application ServiceApplicationService(新規作成)

ApplicationServiceの引数 ApplicationServiceの戻り値

Page 64: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

サンプルコード Application ServiceApplicationService(更新)

Page 65: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

アーキテクチャ

ApplicationService

DomainModel

QueryModel

User Interface

Infrastructure

<<interface>>Repository

Entity

Repository Impl

<<interface>>Query Serivce

DTO

Query SerivceImpl

Page 66: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

ポイント

・POJOで引数と戻り値の関係だけ定義

→「ライブラリ何使うかは任せるけど、

この条件で指定したらこういう形で返してね」

という宣言をしている

サンプルコード queryServiceクエリモデルのサービス IF

引数

Page 67: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

アーキテクチャ

ApplicationService

DomainModel

QueryModel

User Interface

Infrastructure

<<interface>>Repository

Entity

Repository Impl

<<interface>>Query Serivce

DTO

Query SerivceImpl jOOQ

Page 68: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

サンプルコード queryServiceImplその他のクエリ部分

①クエリライクにJavaコードでselect, from, join条件を書いている

②resultをfetch後に戻り値の方に

mapしたものをreturnしている

③条件指定の方法は次のページ

Page 69: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

・引数オブジェクトの値をjOOQのConditionオブジェクトにマッピング

・引数オブジェクトのパラメータを取得するメソッドをOptionalで返せば、

 値があるときだけ条件指定、ということも可能

・引数のパラメータを増やせば検索の拡張も簡単

サンプルコード queryServiceImpl条件指定部分

Page 70: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

Spring Data JPA

アーキテクチャ

ApplicationService

DomainModel

QueryModel

User Interface

Infrastructure

<<interface>>Repository

Entity

Repository Impl

<<interface>>Query Serivce

DTO

Query SerivceImpl jOOQHibernate

Hibernate(アノテーションだけ )

Page 71: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

導入結果

Page 72: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

導入結果

● 実際どうだった?

Page 73: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

導入結果

● 実際どうだった?

→かなり使い勝手が良いです!!

Page 74: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

導入結果

● 実際どうだった?

→かなり使い勝手が良いです!!

● Read Model○ Hibernateの得意な部分は活かしつつ、

参照系ではまりがちなHibernateの使い方に悩む時間を一切カット

(クエリを書いた通りに動く)○ 検索条件の拡張も快適

Page 75: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

● 実際どうだった?

→かなり使い勝手が良いです!!

● Read Model○ Hibernateの得意な部分は活かしつつ、

参照系ではまりがちなHibernateの使い方に悩む時間を一切カット

(クエリを書いた通りに動く)○ 検索条件の拡張も快適

● Write Model○ DDDとSpring Data JPAの相性は抜群

 RepositoryのIFだけ書けば実装クラスを作ってくれるのはとても楽

○ writeモデルに振る舞いを凝縮している安心感、読みやすさ◎

導入結果

Page 76: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

Q&A

Page 77: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

Q&A● そもそも2つのORM同時に使っていいの?

Page 78: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

Q&A● そもそも2つのORM同時に使っていいの?

→前述の通りjOOQ公式で紹介されている使い方

(@little_hand_s から参考資料たどれます 「jOOQ関連リンク」)

Page 79: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

Q&A● テストはどうしている?

Page 80: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

Q&A

● テストはアプリケーション層のメソッドに対して書く(ちなみにspock)

● メリット:○ 費用対効果が良い○ 内部のリファクタがしやすい○ アプリケーションサービス層で担保すれば、

呼び出し元がAPIだろうが画面だろうがwatcherだろうが安心感がある

● デメリット:○ テストでのDB実行環境構築が必要

ApplicationService

DomainModel

QueryModel

User Interface

Infrastructure

● テストはどうしている?

Page 81: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

Q&A● 要件として参照と更新を同時に行う必要がある場合は?

例:ある情報を参照したら参照ログを残したい

Page 82: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

Q&A● 要件として参照と更新を同時に行う必要がある場合は?

例:ある情報を参照したら参照ログを残したい

● 対策1:ApplicationServiceを複数呼び出す処理を書く

ファサードのようなレイヤなど。

通常のApplicationServiceとは区別するのがよい

● 対策2:ドメインイベントを発行してWatcherで拾い、非同期的に更新を行う

Page 83: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

Q&A● アプリケーションサービスはコマンド系・クエリ系でクラスを分ける?

ApplicationService

DomainModel

QueryModel

User Interface

Infrastructure

Page 84: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

Q&A● アプリケーションサービスはコマンド系・クエリ系でクラスを分ける?

→ 分けたほうが使用するモジュールにに間違いないことが判別しやすい。

でもどちらも可

ApplicationService

DomainModel

QueryModel

User Interface

Infrastructure

Page 85: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

● QueryServiceはUIから直接参照させてもよいのでは?

Q&A

ApplicationService

DomainModel

QueryModel

User Interface

Infrastructure

<<interface>>Query Serivce

DTO

Query SerivceImpl

Page 86: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

● QueryServiceはUIから直接参照させてもよいのでは?

→どちらでも可、要件次第。

 今回はApplicationServiceを挟んでよかった。

○ テスト単位がApplicationServiceというレイヤで統一できる

○ 単純な条件抽出以外の処理を分岐したい場合、

その制御をApplicationServiceに任せられる

■ 操作ユーザーの情報に応じて

処理を分岐

■ 権限制御、抽出条件変更など

Q&A

ApplicationService

DomainModel

QueryModel

User Interface

Infrastructure

<<interface>>Query Serivce

DTO

Query SerivceImpl

Page 87: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

まとめ

Page 88: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

まとめ

● CQRSは要件に応じて柔軟にやり方を変えられる

● Write/Readモデルを分けるときにORMを分けるのは、

モデルを分けるメリットを大きくできる

● ぜひご検討してみてください

● ご意見、ご質問は @little_hand_s もしくは#ccc_m4 まで

Page 89: DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話

ありがとうございました