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
LINEヤフーTech (LY Corporation Tech)
PRO
January 16, 2025
Technology
17
3.5k
ドメイン駆動設計の実践により事業の成長スピードと保守性を両立するショッピングクーポン
ドメイン駆動設計の実践により事業の成長スピードと保守性を両立するショッピングクーポン
LINEヤフーTech (LY Corporation Tech)
PRO
January 16, 2025
Tweet
Share
More Decks by LINEヤフーTech (LY Corporation Tech)
See All by LINEヤフーTech (LY Corporation Tech)
LINEスキマニにおけるフロントエンド開発
lycorptech_jp
PRO
1
1.3k
Yahoo! ズバトクにおけるフロントエンド開発
lycorptech_jp
PRO
0
630
LINE Developersプロダクト(LIFF/LINE Login)におけるフロントエンド開発
lycorptech_jp
PRO
0
750
AI時代のデータセンターネットワーク
lycorptech_jp
PRO
1
530
連合学習を用いたスタンプの推薦
lycorptech_jp
PRO
2
75
Will Positron accelerate us?
lycorptech_jp
PRO
2
180
Try using dbtplyr
lycorptech_jp
PRO
0
28
EthernetベースのGPUクラスタ導入による学びと展望
lycorptech_jp
PRO
2
1.1k
LY Accessibility Guidelines @fukuoka_a11yconf_前夜祭
lycorptech_jp
PRO
1
190
Other Decks in Technology
See All in Technology
日経電子版 x AIエージェントの可能性とAgentic RAGによって提案書生成を行う技術
masahiro_nishimi
1
260
Kubernetes x k6 で負荷試験基盤を開発して 負荷試験を民主化した話 / Kubernetes x k6
sansan_randd
2
700
AndroidデバイスにFTPサーバを建立する
e10dokup
0
220
【Developers Summit 2025】プロダクトエンジニアから学ぶ、 ユーザーにより高い価値を届ける技術
niwatakeru
2
350
生成AIの利活用を加速させるための取り組み「prAIrie-dog」/ Shibuya_AI_1
visional_engineering_and_design
1
130
Platform Engineeringは自由のめまい
nwiizo
3
1.6k
MC906491 を見据えた Microsoft Entra Connect アップグレード対応
tamaiyutaro
1
410
技術負債の「予兆検知」と「状況異変」のススメ / Technology Dept
i35_267
1
950
Culture Deck
optfit
0
250
リーダブルテストコード 〜メンテナンスしやすい テストコードを作成する方法を考える〜 #DevSumi #DevSumiB / Readable test code
nihonbuson
11
4k
Bounded Context: Problem or Solution?
ewolff
1
210
Active Directory の保護
eurekaberry
7
3.9k
Featured
See All Featured
Designing Dashboards & Data Visualisations in Web Apps
destraynor
231
53k
Making the Leap to Tech Lead
cromwellryan
133
9.1k
Optimizing for Happiness
mojombo
376
70k
Gamification - CAS2011
davidbonilla
80
5.1k
Exploring the Power of Turbo Streams & Action Cable | RailsConf2023
kevinliebholz
29
4.6k
Designing Experiences People Love
moore
139
23k
How GitHub (no longer) Works
holman
313
140k
Speed Design
sergeychernyshev
25
780
Rebuilding a faster, lazier Slack
samanthasiow
79
8.8k
Typedesign – Prime Four
hannesfritz
40
2.5k
Measuring & Analyzing Core Web Vitals
bluesmoon
6
240
How to Ace a Technical Interview
jacobian
276
23k
Transcript
© LY Corporation Public LINEヤフー株式会社 山口直人 / 塩川賢太郎 ドメイン駆動設計の実践により事業の成長スピードと 保守性を両立するショッピングクーポン
© LY Corporation Public 自己紹介 DDDを採用した背景 DDDの実践 あれから5年 これからのショッピングクーポン 2
Agenda
© LY Corporation Public 山口 直人 LINEヤフー株式会社 コマースカンパニー ショッピング統括本部 プロダクション2本部
デジタルマーケティング技術部 部長 3 2013 ヤフー株式会社新卒入社 様々なBEシステムの刷新を担当 2016 オフショア開発(ベトナム駐在) 2018 クーポンシステムチームのリーダーへ昇格 (ドメインエキスパート) 2022 デジタルマーケティング技術部長へ昇格
© LY Corporation Public 塩川 賢太郎 LINEヤフー株式会社 コマースカンパニー ショッピング統括本 部
プロダクション2本部 デジタルマーケティング技術部 インセ ンティブ基盤技術1チーム 4 2019〜 SIer系の企業に新卒入社 自社サービスのアプリ基盤(frontend,backend)を担当 2022〜 ヤフー株式会社に中途入社 ヤフーショッピングのインセンティブ基盤を担当 重要案件のTLを担当
© LY Corporation Public DDDを採用した背景 5
© LY Corporation Public 6 クーポンの自由度 クーポン 注文個 数条件 注文金
額条件 スタン プカー ド 購入完 了画面 決済方 法指定 タイム セール 併用可 併用不 可 商品指定 ユーザ指 定 モール クーポン ストア クーポン 顧客ス テージ モール 内全品 ストア 内全品 商品タ グ指定 商品 コード 指定 JAN コード 指定 ブラン ド指定 カテゴ リー指 定 プレミ アム会 員 新規 ユーザ 通常 モール CRMクー ポン (パーソナ ライズ) STORE ’s Roo (パーソ ナライ ズ) ストア クリエ イター Pro 新規プ レミア ム会員 ユーザ ストア さまざまな購入体験 多様な購買施策
© LY Corporation Public 7 クーポンの自由度 ① モールクーポン ② カテゴリ指定
③ 対象ストア指定 ④ 注文金額条件 ① モールクーポン ② モール全品指定 ③ 新規ユーザ ④ 注文金額条件 ① ストアクーポン ② ストア全品指定 ③ プレミアム会員
© LY Corporation Public 8 システムは複雑になっていく。。
© LY Corporation Public 9 関係者 営業 マーケ ティング 企画
開発
© LY Corporation Public 10 コミュニケーション面 何がコストになっていたか • コミュニケーション面 •
言葉の違いから認識の違い、コミュニケーションコストの増 • マーケ⇔技術で話すときに言葉の変換したり、、 • システム面 • 1クラスに全ての処理が書いてある • 処理のシーケンスが複雑で見通しが悪い • 仕様の複雑性 = システムの複雑性でない • 仕様の複雑性 < システムの複雑性となっている
© LY Corporation Public 11 複雑さに立ち向かうためのDDD
© LY Corporation Public 12 レガシーシステムからの脱却 当時の背景 言語 PHP 5.7
FW 独自FW アーキテクト モノリシック コンピューティング IaaS ライブラリ 社内製 言語 java FW Spring Boot アーキテクト マイクロサービス コンピューティング PaaS(CaaS) ライブラリ OSS
© LY Corporation Public 13 事前にやったこと コード設計について • 既存コードの可視化 •
各アプリケーションでどのような開発が多いかの理解 • DDDについての学習 • 講師を招いてのハンズオンセミナー
© LY Corporation Public 14 コンセプトや意識したこと コード設計について • 配属直後の新卒がすぐに理解できる容易性 •
単純明瞭な処理フローの設計 • クラス名、変数名などを業務用語に合わせる
© LY Corporation Public 15 当時の体制 コード開発について • ペアプロ(ドメインエキスパートと若手エンジニア) •
一旦書いてみる • 書いては捨て、書いては捨てを繰り返す ドメインエキスパート 若手エンジニア
© LY Corporation Public DDDの実践 16
© LY Corporation Public 17 参考文献 • 現場で役立つシステム設計の原則 変更を楽で安全にするオブジェクト指向の 実践技法
増田亨/著 • エリック・エヴァンスのドメイン駆動設計 ソフトウェアの核心にある複雑さ に立ち向かう (IT Architects’ Archive ソフトウェア 開発の実践) エリック・エヴァンス/著 今関剛/監訳 和智右桂/訳 牧野 祐子/訳 • 実践ドメイン駆動設計 エリック・エヴァンスが確立した理論を実際の設計に 応用する (Object Oriented SELECTION) Vaug hn Vernon/著 高木正弘/訳
© LY Corporation Public 18 システムの関心事(=モデル)を分析する カート クーポン イベントストーミング 開発担当
ドメインエキスパート ドメインエキスパート イベントストーミング
© LY Corporation Public 19 システムの関心事(=モデル)を分析する
© LY Corporation Public 20 表示における関心事 を扱う • ルーティング •
HTTPリクエスト レスポンス • バリデーション • UI ドメインオブ ジェクトを利用 してソフトウェ アが行うべき仕 事を表現する 業務ロジックや業務 ルールを表現する データの永続化処理や外部 サービスとのやりとりを行う • DB • REST API • MQ • gRPC レイヤードアーキテクチャによる関心の分離でドメインロジックを疎結合にする Domain(業務ロジック)は 他のどの層にも依存していない
© LY Corporation Public 21 ドメインモデルを部品として成熟させることで高凝集性を獲得する ドメインモデルにロジックを寄せることでデータとロジックを一体化させる DDD実践前 • ドメインエンティティ的なものは存在してい
たが、あくまでロジックがないデータの入れ 物(DTO)だった • サービスクラスは存在していたが、業務ロ ジックはそこに凝集し、肥大化していた 実装方針 1. 分析モデルと対応するドメインモデルクラスをつくる i. 業務知識とソースコードを結びつける 2. そのモデルのデータを使って、ユースケースを満たすために必要なロジックを実装する i. データとロジックがモデルの中で共存する 3. 実装したロジックをできるだけ小さく分解する i. 部品が再利用性を獲得する 4. 実装できなかったドメインロジックはドメインサービスへ実装することで妥協する 5. 再びコードを眺めてみて、ドメインモデルに寄せられるロジックを探して移してみる 6. 3.~5.を繰り返す ドメインモデルのあるべき姿 ✓ データとロジックを一体にするべき ✓ 業務ロジックと扱うデータは同じクラ スにあるほうがよい ✓ ドメインモデルを部品として成熟させるべき ✓ ドメインモデルに業務ロジックが凝集 されるべき メリット ✓ 単体テストが実装しやすくなる ✓ メソッドあたりの責務が小さくなるため、テストすべき関心の対象も小さくなる ✓ ロジックの凝集性があがる ✓ データとロジックが一体になる ✓ 実装者は「今作ろうとしている部品はどのモデルに実装するべきか」ということ を考えるようになる ✓ 実装スピードが上がる ✓ 小さな部品を作るという意識は、エンジニアの実装タスクを分解する作業 ✓ 人間はタスクが分解されると作業の見通しが良くなる
© LY Corporation Public 22 ドメインモデルを部品として成熟させることで高凝集性を獲得する 副作用のない関数にすることで 「状態」という悩みを払拭する import lombok.Builder;
@Builder public class Coupons { List<Coupon> couponList; /** * 変更不可なコレクションを返す * @return List<Line> */ public List<Coupon> asList() { if(couponList == null) { return Collections.emptyList(); } // addやremoveが利用できなくなる return Collections.unmodifiableList(couponList); } /** * クーポンの追加(副作用なし) * @param coupon * @return Coupons */ public Coupons addCoupon(Coupon coupon)) { // Listオブジェクトを新たに生成し直す List<Coupon> couponList = new ArrayList<>(this.couponList); couponList.add(coupon); // Couponsモデルも新たに生成し直す return Coupons.builder().couponList(couponList).build(); } } 例: コレクションに要素を追加する Point 1. コレクションを操作するメソッドを Couponsに実装する 2. コレクションの参照をそのまま外部に提供 するのではなく変更不可にして返す 3. コレクションの状態を変える場合は新たに オブジェクトを生成し直す
© LY Corporation Public 23 ドメインモデルを部品として成熟させることで高凝集性を獲得する 副作用のない関数にすることで 「状態」という悩みを払拭する import lombok.Builder;
// toBuilderを使えるようにする @Builder(toBuilder = true) public class Coupon { CouponId couponId; String title; String description; ... Integer useCount; ... public Coupon updateUseCount(int useCount) { // toBuilderによってオブジェクトからBuilderを生成する return this.toBuilder() .useCount(useCount) .build(); } } 例: あるオブジェクトの一部の状態を 変更する Point 1. オブジェクトの一部の状態を変更したい場 合は、その部分を再指定したオブジェクト を新たに生成する
© LY Corporation Public 24 ドメインモデルを部品として成熟させることで高凝集性を獲得する 閉じた操作にすることで 部品を組み立てやすくする public class
Lines { ...... public Lines simulate(Coupons storeAllCoupons, Coupons targetItemCoupons, Coupons postageCoupons) { List<Line> lineList = this.asList() // 各商品ラインを数量1の商品ラインに分割する .splitToSingleQuantityLines() // 計算の前処理として商品単価でソートする .sortByItemPrice() // ストア全品クーポンの適用をシミュレートする .tryToApplyStoreAllCoupons(storeAllCoupons) // 商品指定クーポンの適用をシミュレートする .tryToApplyTargetItemCoupons(targetItemCoupons) // 分割したラインをまとめる .summarize() // クーポン適用されたラインを優先的にソートする .sortByCouponApplyState() // 送料値引きクーポンの適用をシミュレートする .tryToApplyPostageCoupon(basket, postageCoupons); return Lines.builder().lineList(lineList).build(); } } 例: 商品ラインにクーポンの組み合わせ を適用させ計算を行う Point 1. 「閉じた操作」 i. あるドメインモデルのメソッドの戻り値 がそのドメインモデル自身になること 2. メソッドチェーンによって流れるように実装 i. コードが仕様書のように流暢に読める
© LY Corporation Public 25 その他に採用したデザインパターン シナリオクラスによって ユースケースの流れを整理する public class
CandidateScenario { ...... public Coupons candidate(User user, Basket basket) { // 1. ドメインサービスの呼び出し CompletableFuture<Coupons> futureCoupons = couponService.fetch(user, basket); CompletableFuture<Basket> futureBasket = basketService.complete(basket); CompletableFuture<User> futureUser = userService.complete(user); // 2. 三者のドメインモデルがそろうまで待機 CompletableFuture.allOf(futureCoupons, futureUser, futureBasket).join(); // 3. ドメインモデルを取得 Coupons coupons = futureCoupons.join(); Basket completedBasket = futureBasket.join(); User completedUser = futureUser.join(); // 4. パーソナライズ判定による判定を行う(マイクロサービスへ) Judgements judgements = judgementService.judge(coupons, completedBasket, completedUser); // 5. 「適用可能なクーポン一覧」という集約を生成する CandidatesAggregate aggregate = CandidatesAggregateFactroy.create(coupons, completedBasket, completedUser, judgements); return aggregate; } } Point 1. 複数ドメインサービスやモデルファクトリーの 呼び出し処理 2. 非同期処理によるフロー制御 メリット ✓ Controllerクラスがビジネスロジックの呼び出し処 理でファットになることを防げる ✓ ビジネスロジックの順序性が明確化され、実装の 見通しが良くなる ✓ 新しいビジネスロジックをどこに挿入すれ ばよいか ✓ パフォーマンス由来でビジネスロジックの 順序変更をしたい
© LY Corporation Public 26 その他に採用したデザインパターン Enumの多態性によって ロジックの分岐を整理する // 1.
商品ターゲティング種別を表すEnumを定義する public enum ItemTargetStrategy { // 2. ストア内全品対象 STORE_ALL { @Override public List<Line> getTargetLineList(Coupon coupon, Basket basket) { return basket.asLineList(); } }, // 3. 商品コード指定 ITEM_CODE { @Override public List<Line> getTargetLineList(Coupon coupon, Basket basket) { // 商品コードリストに含まれているものを抽出 return basket.asLineList() .stream() .filter(line -> coupon.getTargetItemCodeList() .contains(line.getItem().getItemCode())) .collect(Collectors.toList()); } }, ...... // 5. 「適用対象となる商品一覧を取得する」という処理の抽象メソッドを定義 public abstract List<Line> getTargetLineList(Coupon coupon, Basket basket); ...... } Point 1. 各列挙子では、5.で定義した抽象メソッドを オーバーライドすることで「分岐」を表現する メリット ✓ ネストされたif/switch分岐の排除による可読性の 向上 ✓ 分岐一つ一つのロジックに対するテストコードの 書きやすさ ✓ 分岐の追加・修正・削除に対する変更容易性の向 上
© LY Corporation Public 27 結果:DDDによってビジネス要求への追従スピードは爆速になった • チームの誰もがコア部分への改修に取り組めるようになった ソースコードと業務知識が一体化 •
開発要望に対して、受け入れテストなど含めて約1カ月程度で行えるように なった 開発スピードの向上 • ソースコードの見通しがよくなり、バグの混入が減少した 事故発生率の改善
© LY Corporation Public あれから5年 28
© LY Corporation Public 29 クーポンの自由度 クーポン 注文個 数条件 注文金
額条件 スタン プカー ド 購入完 了画面 決済方 法指定 タイム セール 併用可 併用不 可 商品指定 ユーザ指 定 モール クーポン ストア クーポン 顧客ス テージ モール 内全品 ストア 内全品 商品タ グ指定 商品 コード 指定 JAN コード 指定 ブラン ド指定 カテゴ リー指 定 プレミ アム会 員 新規 ユーザ 通常 モール CRMクー ポン (パーソナ ライズ) STORE ’s Roo (パーソ ナライ ズ) ストア クリエ イター Pro 新規プ レミア ム会員
© LY Corporation Public 30 クーポンの自由度 クーポン 注文個 数条件 注文金
額条件 スタン プカー ド 購入完 了画面 決済方 法指定 タイム セール 併用可 併用不 可 商品指定 ユーザ指 定 モール クーポン ストア クーポン 顧客ス テージ モール 内全品 ストア 内全品 商品タ グ指定 商品 コード 指定 JAN コード 指定 ブラン ド指定 カテゴ リー指 定 プレミ アム会 員 新規 ユーザ 通常 モール CRMクー ポン (パーソナ ライズ) STORE ’s Roo (パーソ ナライ ズ) ストア クリエ イター Pro 新規プ レミア ム会員 決済方 法指定 モール 商品タ グ指定 SMID 指定 Y!マー ト 出前館 まとめ 割 メー カー 案件種別 プロ モー ション パッ ケージ インテ リジェ ント 出前館 ユーザ Y!マー トユー ザ PayPa yカー ド会員 Y!モバ イル会 員 Y!モバ イルプ レミア ムバン ドル会 員 スマー トログ イン設 定 PayPa yID連 携 レ ビュー 対象 アプリ ダウン ロード LINE ユーザ LINE- YID連 携 LINEO A友達 登録 LINEO A友達 登録 購入回 数上限 下限 購入回 数開始 終了 可変 Enjoy パック 日替わ り 週替わ り PayPa y日用 品ミニ アプリ アプリ 新規向 け ルー レット くじ LYP入 会イン せ カート Wボタ ン 定期購 入 CRMラ イト育 成 デイ リー ボーナ ス SHPミ ニアプ リ LYPプ レミア ム会員 CRM初 期稼働 サービ ス登録 OA TOP訴 求 デ ビュー カウン トダウ ン ストア ラリー ゾロ目 優良配 送
© LY Corporation Public 31 ドメインの複雑さは増している
© LY Corporation Public 32 だがしかし
© LY Corporation Public 33 ドメインの複雑さが増しても 開発スピードは落ちていない
© LY Corporation Public 34 Point:ドメインモデルとレイヤードアーキテクチャの恩恵 • 新機能の追加がしやすい • レビューがしやすい
• バグの発生が少ない 開発メンバーがどこに手を入れれば良いか共通認識を持つことができる • コードが読みやすい • コードを読むことで自然とドメイン知識が身についていく感覚が得られる • コードの与える影響がわかりやすい 効率の良いキャッチアップで早く戦力になれる
© LY Corporation Public これからのショッピングクーポン 35
© LY Corporation Public 36 Monorepoの導入
© LY Corporation Public 37 Multirepoの運用コストが高い マイクロサービス: ユースケース単位 リポジトリ:マイクロサービス単位 •
速い開発が可能(開発とデプロイの独立) • スケーラビリティ • 技術スタックの多様性 • 分離されたコードベース • ユースケースごとにドメインモデルを最適化できる メリット • 依存関係(ライブラリ・セキュリティ)の更新コストが高い デメリット Coupon ..40repo 適用可能クーポン取得 repository 利用候補取得 repository 検索 repository 詳細取得 repository Team Other ..30repo Other ..8repo Other ..4repo Other ..1repo ..83repo ・ ・ マイクロサービスの影響 Multi Repoの影響
© LY Corporation Public 38 Monorepoの導入 • 複数のリポジトリ • 複数のマイクロサービス
Multirepo Coupon 適用可能クーポン取得 repository 利用候補取得 repository 検索 repository 詳細取得 repository Coupon 適用可能クーポン取得 repository 利用候補取得 検索 詳細取得 ・ ・ ・ ・ • 単一のリポジトリ • 複数のマイクロサービス Monorepo 依存関係の 一元管理 Parent • 独立した開発とデプロイによる開発の 速さ • スケーラビリティ • 分離されたコードベース • ユースケースごとにドメインモデルを 最適化できる • 依存関係の管理が容易 メリット • CI/CD性能の低下 • ビルド/デプロイラインをマイクロ サービス単位のままにすることで解決 • 技術スタックの多様性 • Monorepoが複数あってもいい デメリット
© LY Corporation Public ご清聴ありがとうございました ぜひYahoo!ショッピングで オトクなお買い物体験を! 39
© LY Corporation Internal Use Only