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
ゆるふわCQRS入門
Search
y_ahiru
May 15, 2023
2
530
ゆるふわCQRS入門
スタフェス Meetup #3 (
https://stafes.connpass.com/event/279671/
) で発表した内容です
y_ahiru
May 15, 2023
Tweet
Share
More Decks by y_ahiru
See All by y_ahiru
フロントエンドエンジニアも知っておきたい HTTP/3 で変わること
yahiru
16
12k
設計におけるソリューションドメイン
yahiru
3
1.5k
PHPで始めるGitHub Actions
yahiru
1
690
5ヶ月でカバレッジを20%から90%にあげた話
yahiru
2
6.5k
入門ミューテーションテスト/ A bigginer's guide to Mutation testing
yahiru
0
1.4k
Eloquentに別れを告げるタイミングについて考えた
yahiru
2
1.9k
DDDについて勉強したので5分でまとめる
yahiru
0
290
Featured
See All Featured
The Illustrated Children's Guide to Kubernetes
chrisshort
48
48k
RailsConf & Balkan Ruby 2019: The Past, Present, and Future of Rails at GitHub
eileencodes
131
33k
[RailsConf 2023 Opening Keynote] The Magic of Rails
eileencodes
28
9.1k
Unsuck your backbone
ammeep
668
57k
Save Time (by Creating Custom Rails Generators)
garrettdimon
PRO
27
790
Adopting Sorbet at Scale
ufuk
73
9k
How to Think Like a Performance Engineer
csswizardry
19
1.1k
What's in a price? How to price your products and services
michaelherold
243
12k
YesSQL, Process and Tooling at Scale
rocio
167
14k
VelocityConf: Rendering Performance Case Studies
addyosmani
325
24k
Put a Button on it: Removing Barriers to Going Fast.
kastner
59
3.5k
How To Stay Up To Date on Web Technology
chriscoyier
788
250k
Transcript
ゆるふわ CQRS 入門 吉田あひる
自己紹介 名前: 吉田あひる (@strtyuu) 所属: スターフェスティバル株式会社 仕事: KAGUYAというチームで設計したり、コード書いたり PHPer Tea
Night というイベントを共同運営しています。 2
はじめに 時間の関係上、前提知識の説明をたくさん省いています!(ごめんなさい!) 不明な点があれば是非あとから質問いただけると嬉しいです! 3
お品書き 商品申請機能開発中のお悩み紹介 CQRSの紹介 ゆるふわCQRSの紹介 メリット・デメリット 4
商品申請の開発例をご紹介 商品申請機能開発中のお悩み紹介 5
商品申請とは? ごちクルなどへ商品を展開するために、管理基盤に商品登録をする必要がある レギュレーションをクリアした内容になっていることを保証したいため、登録・ 変更に社内審査を挟みたい 商品申請機能開発中のお悩み紹介 6
集約の設計時の悩み 商品申請機能開発中のお悩み紹介 7
極力小さい集約にしたい 集約の大きさ = トランザクションの大きさなので、トランザクションを小さくし たい 申請のトランザクションに商品を含めたくない 申請の段階では気にしなくていい属性も商品には含まれる そういった商品側の属性の増減で申請ドメインに影響が出る可能性を減らし たい 商品申請機能開発中のお悩み紹介
8
// 商品申請集約 type ProductApplication = { id: string; productId: string;
// 申請と紐づく商品 reviewStatus: ReviewStatus; // 審査ステータス content: ApplicationContent; // 申請内容色々 } // 商品集約 type Product = { id: string; productName: string; // ... } 商品申請機能開発中のお悩み紹介 9
表示のことを考えると、大きい集約の方が楽... だいたい申請と商品をペアで表示したくなるんだよなぁ 一覧表示のときとかにアプリケーション側でガチャガチャしたくないなぁ // 商品申請集約 type ProductApplication = { id:
string; product: Product; // 商品Entity を丸ごと持つ reviewStatus: ReviewStatus; content: ApplicationContent; } 商品申請機能開発中のお悩み紹介 10
どうしようかな? トランザクションの大きさを妥協する? 表示のときにアプリケーション側でがんばって複数の集約を組み合わせる? 商品申請機能開発中のお悩み紹介 11
解答の1つが CQRS CQRSの紹介 12
CQRSとは? Command and Query Responsibility Segregation (コマンドクエリ責務分離) Greg Young氏が提案 書き込み(Command)と表示(Query)で要求が異なる点に着目
書き込みのモデルと表示のモデルをそれぞれ別に構築しよう、というアイデア CQRSの紹介 13
要求の違い 書き込み データの整合性を正しく保ちたい 書き込みの競合などが極力起こらないようにしたい 表示 高速にレスポンスしたい データを様々な表現に変換したい CQRSの紹介 14
15
CQRSのメリット 書き込みと読み込みをそれぞれ最適化された構造にできる Event Sourcingで組めば書き込みもRDBよりも圧倒的にスケール可能に でも大変じゃ...? Event Sourcing 大変じゃない...? RDB一つだけでやりくりしたいんですけど! Kafka
とかドキュメントDBとかよくわかんないよ〜! CQRSの紹介 16
ゆるく CQRS のエッセンスを取り入れよう ゆるふわCQRSの紹介 17
ゆるふわ CQRS アプリケーションモデルにだけエッセンスを適用 書き込みと読み込みでモデルを分ける データソースは RDBMS 1つ Event Sourcingは採用しない! 読み込みモデル
-> 書き込みモデルの依存は少しなら許しちゃう ※ Segregation出来てないので、 CQRSと呼んだら怒られるかもしれない ゆるふわCQRSの紹介 18
19
書き込みモデルの構成要素 Entity 集約 Repository Value Object Domain Service etc... 今日は割愛
ゆるふわCQRSの紹介 20
読み込みモデルの構成要素 ReadModel Presenter QueryService ゆるふわCQRSの紹介 21
ReadModel 単なるDTO 値をどのような表示にするかはユースケースによって結構変わるので 値の加工の責務はPresenterに寄せた方が拡張性高いと思う 表示の都合に合わせたデータ構造を取る 個人的にはREST APIのリソースと1:1で対応づけるイメージで作ることが多い 他のReadModelもプロパティとして持っていてもOK ゆるふわCQRSの紹介 22
ReadModel type ProductApplication = { id: string; product: Product; //
Read Model の商品 reviewStatus: ReviewStatus; content: ApplicationContent; } ゆるふわCQRSの紹介 23
Presenter Presentationロジックの責務を担当するやつ 任意の値を受け取り、別の表現に変換する ReadModel -> HalJSON 複数の注文ReadModelを注文日ごとにまとめる etc 純粋関数にする I/Oを発生させない
内部でQueryServiceを呼ばない(引数として渡す形にしよう) 表現の種類ごとに自由に作っていい ゆるふわCQRSの紹介 24
Presenter type HalJsonProductApplication = { id: string; // ... _embedded:
{ product: HalJsonProduct; }; _links: { self: { href: string }; }; } class HalJsonProductApplicationPresenter { make(application: ProductApplication): HalJsonProductApplication { // 変換処理 } } ゆるふわCQRSの紹介 25
QueryService データストアからReadModelを取得する君 RDBMS Elasticsearch Web API etc Interface を切ってもいいし切らなくてもいい モックしたいなどの要求があれば
Interface にしておいた方が無難 ReadModel と 1:1 で作るイメージ ゆるふわCQRSの紹介 26
QueryService interface ProductApplicationQueryService { // 詳細画面などで必要になる、ID が一致するReadModel を引っ張ってくるやつ findOne: (id:
string) => Promise<ProductApplication | null>; // 一覧画面のページネーションとかで必要になるやつ paginate: (page: number, perPage: number) => Page<ProductApplication>; // 複雑な検索などもOK relatedApplications: (id: string) => Promise<ProductApplication[]> } // インターフェースを切るならデータソースごとに実装を作るイメージ class MySqlProductApplicationQueryService implements ProductApplicationQueryService { // ... } class InMemoryProductApplicationQueryService implements ProductApplicationQueryService { // ... } ゆるふわCQRSの紹介 27
Controller などはこんな感じに... class ShowProductApplicationAction { private readonly presenter: HalJsonProductApplicationPresenter; private
readonly queryService: ProductApplicationQueryService; async invoke(req: Request): Promise<Response> { const application = await this.queryService.findOne(req.query('id')); if (application === null) { return new Response(404); } const jsonString = JSON.stringify(this.presenter.make(application)); return new Response(200, jsonString); } } ゆるふわCQRSの紹介 28
メリット アプリケーションレベルのモデルは分離されているため、書き込みと読み込みそ れぞれの要求に対してシンプルな構造をとれる ガチCQRSに比べて実装コスト/インフラコストを抑えられる 集約の設計に悩む時間が減る ゆるふわCQRSの紹介 29
デメリット 構成要素が増えるため、それぞれの構成要素がシンプルでも全体の複雑性は上が りがち 最近よく聞く Complex と Complicated の違い CRUD+αくらいで済むならActive Recordとかで普通に作る方が良さそう
あくまでなんちゃってのゆるふわなので、CQRS本来の目的をすべて達成できるわ けではない ゆるふわCQRSの紹介 30
まとめ Active Recordは避けたい、けど読み込みと書き込みで違う構造を取りたいという 時の選択肢の一つ 一部のモデルにだけ適用するというのも可能 これはガチCQRSもそう デメリットももちろんあるのでご利用は計画的に おわりに 31