little hands' lab

ドメイン駆動設計を布教したい

「DDDのモデリングとは何なのか、 そしてどうコードに落とすのか」資料 / Q&A

Mix Leap Study 特別編 - レガシーをぶっつぶせ。現場でDDD! コラボカンファレンス に登壇させていただいたのでで、その際の資料です。

また、当日sli.doでたくさんのご質問をいただいたので、まとめてお答えします。

発表資料

www.slideshare.net

Q&A

数が多いのでカテゴライズしました。

  1. 学び方
  2. ドメインモデリング
  3. 集約
  4. レイヤー/アーキテクチャ
  5. 実装パターン
  6. CQRS系の話題
  7. ライブラリ・フレームワーク
  8. テスト
  9. ツール
  10. 開発方法

ではどうぞ。


1.学び方

初心者向がDDDを勉強する上でオススメの学び方とかありますか? エヴァンス本は難しいので・・・

モデリング周りはあんまり良い日本語情報がなく、手前味噌ながら僕のブログを読んでいただけると嬉しいです。(^^;
実装パターンは@nrslibボトムアップドメイン駆動設計がとっつきやすくて良いです。

で、実装し始めて細かいことが気になり出したら実践ドメイン駆動設計は鉄板です。

この本単体だと正直最初のハードルはあるんですが、ある程度乗り越えてから読むと迷った答えがめっちゃ書いてある、すげー!となります。

DDDを人に伝える時、僕からは全体感を伝えて、あとはこの本読めばできるぐらいに持って行けるぐらいを目指せればいいのかなーと思っていたりします。

あと、割と英語でググった方がわかりやすい記事とかみつかるので英語がいけるのであればおススメです

little-hands.hatenablog.com

コーディングとモデリング力を相互に鍛えていくとのことでしたが、モデリング力を鍛える上で書籍等オススメの方法はありますか?それとも現場で試行錯誤していくのが唯一解なのでしょうか。

モデリング部分は僕もいいの見つけた事ないんですよね・・・見つけたらおしらせします。 逆にいいの見つけたら教えてくださいm( )m笑


2.ドメインモデリング

何らかのロジックを書くときに、それをドメインに実装すべきか、アプリケーション(ユースケース)に実装すべきかの判断が難しいんですが、なにか判断基準があれば教えて下さい

ユースケースとドメイン知識のそれぞれの定義によって分けるのが良いと思います。(ユースケース=アプリケーションとユーザーの相互作用)なのか、ドメイン知識=ドメインに存在するルール/制約)

こちらの記事も参考にされてください。

little-hands.hatenablog.com

モデリングにどれくらい時間をかけているか開発の規模に対して参考に教えていただけると嬉しいです

30分〜1時間ぐらいで時間決めてやるのがいいと思います。最初から完全なものはできないのでインクリメンタルにやるのがDDDのスタンスなので、最初の段階で時間をたくさんかけても効果は逓減します。

ドメインモデリングに凝りだすとキリがないのですが、ここまでやれば十分と判断する基準みたいなものはありますか?

一旦「実装は始められそう」となったらですかね。複雑な奴ほど、「よし、完璧なモデリングできた!」と思ってから実装始めるとすぐ足りないことにきづいたりするので、笑 ある程度まで行ったらコーディングして新しい発見を誘発した方が結果としてはよいです。最初には完璧なモデルを作れると思わないことが重要です。

時間的な要件があった時はドメインモデル図ではどう表現しますか?

まずドメインモデル図の吹き出しに書くことを考えますが、概念がうまく切り出せたらオブジェクトとして定義するかもしれません。 これは具体例をもらえればもう少し細かく答えられると思います。

ドメインモデル図を考えるときに無意識にDB構成とかの事を意識してしまっていることがあるのですが、どのようにすればこの呪縛から逃れられますか?

ビジネス側に説明なしで伝わる言葉を使っているか、気にしてみるとよいのではないでしょうか。説明が要るなら、技術的な用語を使っているかもです

アジャイルのような開発の場合、要件の変化により、モデル図の更新が頻発しないですか?その場合、モデル図の作成するタイミングはどのようなフェーズが良いとお考えですか?

多発します。モデル図を見ながらその場で要件変更に伴うモデル図更新をしたら良いと思います。 DDDではよりよいドメインモデルをドメインエキスパートと一緒に探求していきます。要件の変更がモデルにどう影響するか、一緒に話せると理想だと思います。

チケット料金モデリングをしようとしていたのですが,ドメインモデルの粒度をどれくらいにすればいいかわからず手が止まってしまいました.ビジネスロジック的に条件が分離している場合はどれだけクラスが増えても分離するべきですか?

どれくらいに、と一概に言えないので具体的に見せてもらったらコメントしやすいです。 目的意識なくルールとして「〜だったら〜しないといけない」みたいなのは基本ないです。分離する目的はなんでしょう?

ドメインモデリングで、モデル図はクラス図で表現されることが多いと思いますが、他に何を使いますか? 特に動的側面はどのくらい分析し、どういう図を作成しますか?

状態遷移図は使いますね、実装にも落としやすいので相性が良いです。動的側面というのはシーケンス図のようなイメージですかね? シーケンス図とかはルール制約というよりは処理の順番だったりするのでドメイン知識というよりapplication層の設計という位置付けですかね。ドメインモデリングとは違うかもしれませんが、そこは必要に応じてやればいいと思います。

動的側面、というのが違うものを指しているのであればまたご質問ください!

色々なモデルの中からユースケース図を選んだ理由があれば教えてください!

ドメインモデル図から始めた時、「これなにに使うやつの話…?」となってかなり議論がぼやけたので、その解消のためにフィットしたやつを選びました。

ぜひ、ユースケース図ありの場合、なしの場合で比べてみてください!

ドメインモデル図で一箇所にやたらルールが多いとかあれば、集約を分割すべきとか判断できそう

質問ではないですが、まさにその通りだと思います!


3.集約

集約を考える際にDBトランザクションのことを考えてしまうのですが、この時点では意識すべきではないでしょうか?

実践DDDでは、「トランザクションの設計をしないと集約の設計はできない」と書いてあります。よって、トランザクションを意識するとことは必要なことだと思います。

集約の具体例が聞きたいです。

一言では説明できないので、今度記事書きます。


4. レイヤー/アーキテクチャ

リポジトリのインターフェースはApplication層に置くと思ったのですが,Domain層に置く理由ってあるのですか?

1.repositoryのメソッドはentityやvoをどの単位で永続化するかを定義しており、集約の範囲の明示という意味があるため 2.domainServiceから使用できるように

です。application層に置くとdomainServiceから参照できなくなりますね。

ドメイン層の中でも ドメインモデルを表現するもの:entity, vo, domainEvent ドメインモデルを使用するもの:repository, factory, domainService があります。この中で「使用するもの→表現するもの」という一方通行の依存はあるので、これをドメイン層の中で2層にわけても構いません。

オニオンアーキテクチャの元ネタの記事ではそこがdomainModelとdomainServiceにわけられてます。 application→domainService→domainModel という一方方向の依存とするのであれば、違和感はないのではないでしょうか。

Controllerはアプリケーション層に置き、プレゼンテーション層はUI的な物を置くイメージでした。 なぜControllerがプレゼンテーション層になるのでしょうか?

プレゼンテーション層はアプリケーションの外と中の入出力を定義するもので、controllerはその責務だけ行って欲しいからです。 application層のメソッドは外側からのリクエスト(エンドポイント定義や、パラメータをurlで渡すかリクエストbodyで渡すかなど)を知りたくありません。またレスポンス(jsonで返すかHTMLレンダリングして返すか)も同等です。その決定はcontrollerがすればよいのです。 そうすれば、HTMLレンダリングするエンドポイントのcontrollerと、jsonで返すエンドポイントのcontrollerで、applicationのメソッドを共有できます。 ちなみに、controllerとhtmlのテンプレートは両方プレゼンテーション層のものになります。 railsのmvcとは切り方が違うので混乱するかもしれませんが、責務で判断してもらえるとわかりやすいと思います。

Taskアプリケーションのこの構造はクリーンアーキテクチャではないと思ったのですが,採用しなかった理由は煩雑さを防ぐためですか?それとも普段から使用していないですか?

あの事例はオニオンアーキテクチャです。僕は普段これを推してます。 理由はシンプルでわかりやすく、チームに広めやすいからです。

よろしければこちらご覧ください。

little-hands.hatenablog.com

ロギングってどこにパッケージを切ってますか?また、JavaのSpringだとAOPを使うことが多いかと思いますが、他の解決策などあれば教えてください。

暗黙的なのはAOPで、application層で明示的にログを仕込みたい場合はapplication層のsharedみたいなパッケージ切っちゃうかもですね。

DDDとしてコードを落とす際に、利用しているフレームワークとの責務の境界(どこまでフレームワークでやるか、アプリ側でやるかなど)が難しいなと思うのですが、そのあたりを考える際の判断基準はあったりしますか?

ドメイン層はなににも依存せず、application層だけdiとかに依存するぐらいでpresentation層とインフラ層がwebフレームワークに依存する形にします。 迷うものを具体的に質問してもらえれば答えられるかもしれません。


5.実装パターン

すべてを値オブジェクトにするのが好ましいのはわかっているのですが、時間や工数的な意味合いである程度端折ってプリミティブ型を使うことも多いのですが、松岡さんの周りではどのようにしていますか?

値オブジェクトにする目的を明確にし、それに合うなら値オブジェクトに、合わないならプリミティブ型を使う、といった判断ができればいいと思います。 どのようなシチュエーションでも値オブジェクトを使ってはいけないとは個人的には思いません。

ApplicationService からではなく、Entity から Repository を利用して、DBからの取得や更新などの操作をしても良いものでしょうか?

entityが複数の責務を持つことになるので、オススメしません。一般的に責務が増える、ということは凝集度が下がり、可読性やメンテナンス性を下げます。

コンストラクタ内に制約条件(初期状態)を記載した場合、DBから取ってきた値はどのように反映するのですか?教えていただけると有り難いです。

おっしゃる通り!これは避けて通れないんですよね。答えは、専用のコンストラクタを設ける、です。そのコンストラクタはパッケージプライベートにして同じパッケージのrepositoryからは呼べますがApplication層からは呼べないようにします。このコンストラクタは全ての属性の値をそのまま受け取り、バリデーションをしないものです。

javaのようなパッケージプライベートがあればよいですが、そうでない場合はfromRepositoryとかreconstructとか、そういう命名規則で運用する事になると思います


6.CQRS系の話題

N+1クエリやDBトランザクションの都合に合わせて、泣く泣くドメインモデルを変更することがあるのですが、そのようにアプリケーション全体の観点から仕方なくドメインモデルを曲げられることはありますか?

ドランザクションの都合にわけて、というのは、それにあわせて集約の範囲を検討することは必要なことなので、よくあります。 ただし、N+1問題に引きずられるのはよくないので、CQRS(参照系で複雑なところはSQLを直接書いたものをクエリモデルとして扱う)をつかいます。

集約内の要素である大きめのValueObject的な部分だけのデータを取得したい場合はどのように取得するようにしていますか? 参照だけですのでRepositoryを用意するようなレベルではない場合です

具体例がないので一般論ですが、参照系でパフォーマンスを考慮して一部だけ撮りたいときはCQRSでクエリを直接書いてしまいます。

(速度面などの問題で)データキャッシュが必要となった場合、Infra層で頑張るのでしょうか?

キャッシュなどのクエリ要件が高まってきたら、CQRSを検討します。


7.ライブラリ・フレームワーク

OMRはどんなものを使うのが良いですか?

オブジェクトをマッピングするタイプとクエリビルドするタイプがありますが、どちらでもインフラ層のRepository実装クラス以外から使われないのであればなんでも問題はありません。

MyBatis良いですか?

使った事なくて、jOOQを愛用しています。 jOOQはスキーマ情報をもとに自動生成するクラスを使ってタイプセーフにクエリをかけるのが便利です。一方 MyBatisは調べたところによるとSQLをテキストでXMLに書くと聞いているので、選びませんでした。

railsのARはテーブルと1対1なので、DDDやりづらい?

基本的に、テーブルと1対1のORMを使う場合はrepositoryの中でプレーンなentityに詰め替えすればなんのORMでもできます。ただrailsはrails wayから外れるとやりづらいらしいので、向いているかというと向いてないと思います。


8.テスト

ドメイン駆動設計で作ったアプリケーションに対して単体テストを行うとき、意図して取り組んでいることはありますか? ドメインは重点的にテストする、プレゼンテーションではエラーハンドリングを重点的にテストする等、何かあれば、教えてください

基本application層で結合テストを書きます。 ドメイン層のentityなどは複雑になったら必要に応じて書くようにしています。 テストのROIを考慮して色々試した結果それが一番よかったためです。

アプリ層のSpecってどう書いていますか?ドメインモデルを呼び出すだけなので、あまりテストをする意味がなさそうに感じました。

更新系は、テスト対象のメソッドを実行して何かを永続化し、そのあとテストコード内でrepositoryからエンティティ取得して結果を引数とくらべてアサートします。 参照系はテストデータを入れて特定の条件指定したさいに結果が期待値通りかをアサートします。


9.ツール

モデリングに何かおすすめのツールはありますか?

plantumlです。テキストでクラス図とかかけるのでそのままgit管理もやりやすいです。

同じく、モデリング(ダイアグラム)とかに使うのにおすすめなツールがあれば知りたいです!(共同編集や、打ち合わせで使うときにシェアのしやすさ、など)

plantuml以外は特にこれといった便利ツールは知らないですねー。僕もオンライン協業に便利なのがあったら知りたいです!


10.開発方法

リファクタリングの話はトランザクションスクリプトが既にあることが前提ですか? それとも、新規作成でもいったんトランザクションスクリプトっぽいものを作ってからドメインモデルに移していくのですか?

どちらでも大丈夫です。最初から綺麗にかけるならあえて手続き的に書く必要はないです。 ただし、applicationServiceでTDDにして1ステップを小さくコーディングしていくと、ドランザクションスクリプトっぽいところから書くことになるかもしれません。それもありです。

とにかくやりやすいようにやってもらってokです。