Skip to main content
© BIGLOBE Inc.
ドメイン駆動設計
失敗したことと成功したこと
2018/9/26
ビッグローブ株式会社
小林 映里奈
2 © BIGLOBE Inc.
自己紹介
小林 映里奈(こばやし えりな)
• @eri_twin
• 2015年度入社の4年目
所属、担当業務
• ITシステム本部 ビッグローブ光開発チーム
• ビッグローブ光の業務システムの開発
3 © BIGLOBE Inc.
今日のテーマ
失敗したことと成功したこと
4 © BIGLOBE Inc.
ドメイン駆動設計への愛をこめて!
5 © BIGLOBE Inc.
agenda
◆ ドメイン欠乏症との戦い ~実践と結果~
◆ つらい現実からドメインを守る 3つの盾
・Form
・Converter
・Adapter
6 © BIGLOBE Inc.
ドメイン欠乏症との戦い ~実践と結果~
「ドメインにロジックを書く」を実現するための試行錯誤
7 © BIGLOBE Inc.
ドメイン欠乏症
手続き型のイメージで、ドメイン以外にロジックを書いてしまう
↓アプリケーション層にロジックがある例
8 © BIGLOBE Inc.
ドメイン欠乏症
ドメインが出てこない…
👉 業務ロジックなど、業務の関心事は
ドメインに書こう!
たとえば、
前提条件のチェックは業務ロジック。
他には?
9 © BIGLOBE Inc.
なにをドメインに書くべきか
Q. どこまでがドメイン(=業務ロジック)なのか
業務ロジックに該当しそうなもの、たとえば
- 業務に関連するドメインを取得する
- 各ドメインが業務の前提条件を満たしているかチェック
- ドメインの状態を変更する
- 変更された状態のドメインを永続化する
etc…
10 © BIGLOBE Inc.
なにをドメインに書くべきか
Q. どこまでがドメイン(=業務ロジック)なのか
業務ロジックに該当しそうなもの、たとえば
- 業務に関連するドメインを取得する
- 各ドメインが業務の前提条件を満たしているかチェック
- ドメインの状態を変更する
- 変更された状態のドメインを永続化する
etc…
A. 関連ドメインの取得・チェック・状態変更までを
ドメインで扱う業務とする
11 © BIGLOBE Inc.
ドメインの責務はここ
12 © BIGLOBE Inc.
ドメイン欠乏症対策
👉 関連ドメイン取得のためドメイン層でリポジトリのDI
👉 前提条件チェック、変更後のエンティティ返却がドメインの責務
↓ドメイン層のOrderDateにロジックを委譲
13 © BIGLOBE Inc.
ドメイン欠乏症対策
👉 業務ロジックをドメインに委譲
👉 if文がなくなって見通しが良くなった
↓アプリケーション層はシンプルに
14 © BIGLOBE Inc.
ドメイン欠乏症対策
🙆 ドメイン層にビジネスロジックがまとまる
🙆 アプリケーション層の見通しを良くできた
🙅‍リポジトリへのアクセスが入るため副作用が多く、
ドメイン層のテストがつらい
→ モック化すればいいと思ってたけど、それも大変だった
→ 実際やってみると、ドメインに集めすぎた感がある
どこまでを業務(ドメイン)とみなすか、考え直す
15 © BIGLOBE Inc.
続:なにをドメインに書くべきか
Q. どこまでがドメイン(=業務ロジック)なのか
業務ロジックに該当しそうなもの、たとえば
- 業務に関連するドメインを取得する
- 各ドメインが業務の前提条件を満たしているかチェック
- ドメインの状態を変更する
- 変更された状態のドメインを永続化する
etc…
A. リポジトリへのアクセスはアプリケーション層へ
チェック・状態変更までをドメインで扱う業務とする
16 © BIGLOBE Inc.
続:ドメインの責務はここ
17 © BIGLOBE Inc.
ドメイン
👉 必要な情報を受け取り、
イベント = 永続化に必要な情報をまとめたクラス を作る
👉 ドメインの内部に閉じたロジックはドメインに書く
18 © BIGLOBE Inc.
ドメインサービス
👉 ドメインサービス(ドメイン層)
👉 ドメインサービスは業務の単位で分けている。
例では申込のユースケースなので、申込に関する業務ロジックを持つ
19 © BIGLOBE Inc.
APサービス全体像
イベントを作る
永続化
20 © BIGLOBE Inc.
ドメイン欠乏症対策2
🙆 サービス層の見通しが良い
🙆 リポジトリアクセス→イベント作成→永続化 の流れが
わかりやすい
🙆‍副作用がないので、ドメイン層はテストしやすい
これが今のところ良いかんじなので、継続中
Q.‍最初にすべてのエンティティを取得するのは冗長?
余計なリポジトリアクセスが多いのでは?
A. そうでもない。
想定よりも無駄になるエンティティはかなり少なかった。
21 © BIGLOBE Inc.
中身のつまったドメインをつくろう!
22 © BIGLOBE Inc.
つらい現実からドメインを守る 3つの盾
内部ではドメインを充実させていく一方、
外部からはさまざまな現実が攻めてきます
23 © BIGLOBE Inc.
現実の世界
domain
service
infrastructure
現実は想定外のさまざまなことが起きる
お店の説明と違う!
クーリングオフ
したいです
オーダー修正する時は
XXのYYをZZZにして
さらにAAAのBBを…
このパターンの番号体系
が昨⽇日からS-­inしました
K社
N社
ドメインの状態にはないけど、
契約開始後キャンセルが必要…
⼀一旦オーダーを取消
してやりなおします
ええー!!
24 © BIGLOBE Inc.
domain
application
infrastructure
user
interface
DB
formAPI
File
converter adapter
つらい現実からドメインを守る💪
3つの盾
👉 form/request: 複雑なバリデーションルールから守る
👉 converter: 複雑なIF仕様から守る
👉 adapter: 物理データ構造から守る
25 © BIGLOBE Inc.
domain
application
infrastructure
user
interface
DB
formAPI
File
converter adapter
つらい現実からドメインを守る💪
3つの盾
👉 form/request: 複雑なバリデーションルールから守る
👉 converter: 複雑なIF仕様から守る
👉 adapter: 物理データ構造から守る
26 © BIGLOBE Inc.
複雑なバリデーションルールのつらみ
😭 APIが多すぎる!
→ 申込経路によってサービス仕様が違う
→ 経路Aと経路Bでバリデーションを分けたい
→ APIをユースケース単位で作成するので数が増える
→ 同じ商品の新規申込APIが50本とかあるような規模
😭 ドメイン層がバリデーションルールで汚れてしまう
🎈 form/requestとは
バリデーションルールをユーザインターフェース層に出し
APIや経路ごとにバリデーションを管理する!
27 © BIGLOBE Inc.
Form
👉 @Patternにオブジェクトのバリデーションルールを書く
👉 ドメインオブジェクトに変換して返すgetterを用意
パラメータごとにXX(ドメイン名)Formを作る
ex) ユーザIDを受け取るためのUserIdForm
28 © BIGLOBE Inc.
Request
👉 先述のFormクラスをまとめたリクエストクラス
API単位のリクエストパラメータを表現している
👉 API固有のパラメータの必須/非必須や相関チェックをここで管理する
👉 Formの値がnullだとオブジェクトをgetしたときに例外になるので
nullの場合はバリデーションエラーにするアノテーションを作った
(@ValueObjectNotEmpty)
29 © BIGLOBE Inc.
API
👉 リクエストで受け取ったパラメータを
バリデーション済みのオブジェクトとして取り出す
30 © BIGLOBE Inc.
domain
application
infrastructure
user
interface
DB
formAPI
File
converter adapter
つらい現実からドメインを守る💪
3つの盾
👉 form/request: 複雑なバリデーションルールから守る
👉 converter: 複雑なIF仕様から守る
👉 adapter: 物理データ構造から守る
💪
守った!
31 © BIGLOBE Inc.
domain
application
infrastructure
user
interface
DB
formAPI
File
converter adapter
つらい現実からドメインを守る💪
3つの盾
👉 form/request: 複雑なバリデーションルールから守る
👉 converter: 複雑なIF仕様から守る
👉 adapter: 物理データ構造から守る
💪
32 © BIGLOBE Inc.
複雑なIF仕様のつらみ
😭 大企業様から提供されるIF仕様書はむずかしい
→ 写し間違えて項目ずれるの怖い
→ 項目数が多いと確認も大変
😭 社内と社外では世界観が違う
→ ドメインモデルと異なる形のデータ群を求められる
→ せっかく作ったモデルはIF仕様に引っ張られたくない…
🎈 Converterとは
複雑なIF仕様をそのままコードに落とす!
外部都合のIFから自分たちのドメインモデルを守る!
33 © BIGLOBE Inc.
Converter: 複雑なIF仕様から守る
IF仕様書をそのままコードに落とす
👉 外部のドメインと自分たちのドメインの言葉を紐付ける
このクラスのコードが言葉の差を埋める設計資料そのものになる
👉 どのエンティティのフィールドかひと目でわかる
ex) v.会員()は会員エンティティにあるデータを使っている
👉 サンプルデータをそのまま期待値に使ったテストが書ける!
↑外部のドメイン
⾃自分たちのドメイン
34 © BIGLOBE Inc.
Converter: 複雑なIF仕様から守る
ドメインモデルの世界観を守る
👉 IFの契約者住所はアパート名を含むが、モデルは住所とアパート名が別
👉 converterで住所とアパート名を繋げたデータを作る
👉 モデルは変えず、IF仕様との世界観の差はconverterが吸収する
35 © BIGLOBE Inc.
domain
application
infrastructure
user
interface
DB
formAPI
File
converter adapter
つらい現実からドメインを守る💪
3つの盾
👉 form/request: 複雑なバリデーションルールから守る
👉 converter: 複雑なIF仕様から守る
👉 adapter: 物理データ構造から守る
💪 💪
36 © BIGLOBE Inc.
domain
application
infrastructure
user
interface
DB
formAPI
File
converter adapter
つらい現実からドメインを守る💪
3つの盾
👉 form/request: 複雑なバリデーションルールから守る
👉 converter: 複雑なIF仕様から守る
👉 adapter: 物理データ構造から守る
💪 💪
37 © BIGLOBE Inc.
物理データのつらみ
前提:O/RマッパーはMyBatisを使っている
😭 データをエンティティに変換するマッピングがつらい
38 © BIGLOBE Inc.
物理データのつらみ
Q. Javaのクラス自動生成してくれるO/RM使わないの?
A. 扱うデータ量が非常に多いので、チューニング必須。
SQLをカスタマイズしやすいO/RMを使いたい。
MyBatisのよいところ
👉 SQLを直に書ける(チューニングできる) (超大事)
MyBatisのつらいところ
👉 xmlでマッピングを書くのが大変
👉 コンパイルエラーにならないので実行時エラーが出る
👉 エラーがわかりにくいので原因の調査に苦労する
🎈 Adapterとは
よいところだけを活かす!
MyBatisを使いながらマッピングはJavaで行う!
39 © BIGLOBE Inc.
Adapter
Javaの基本的なクラスでDBから値を取得する。
👉 VARCHER型はString, DATE型はLocalDate(LocalDateTime)
👉 値を取得する段階ではバリューオブジェクトへの変換処理はしない
バリューオブジェクトへの変換ロジックはadapterに集める
40 © BIGLOBE Inc.
domain
application
infrastructure
user
interface
DB
formAPI
File
converter adapter
つらい現実からドメインを守る💪
3つの盾
👉 form/request: 複雑なバリデーションルールから守る
👉 converter: 複雑なIF仕様から守る
👉 adapter: 物理データ構造から守る
💪 💪 💪
41 © BIGLOBE Inc.
domain
application
infrastructure
user
interface
DB
formAPI
File
converter adapter
つらい現実からドメインを守る💪 まとめ
3つの盾
👉 form/Request: 複雑なバリデーションルールから守る
👉 converter: 複雑なIF仕様から守る
👉 adapter: 物理データ構造から守る
💪 💪 💪
42 © BIGLOBE Inc.
こうしてドメインは守られた…☆
43 © BIGLOBE Inc.
ドメインに愛をこめて!
業務ロジックで充実&外部要因から守る!
44 © BIGLOBE Inc.
まとめ
これまでの取り組みの一部を紹介しました。
5年を経た失敗と成功をまとめましたが、
これは『今のところ』の結果です。
明日にはまたより良い成功が見つかるかもしれません。
社内ではいろんなチームが日々、試行錯誤しています。
ビッグローブの失敗と成功はまだまだ続いていきます。
一緒にDDDやっていきましょう 💪
BIGLOBE Internal Use Only