Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

ドメインモデルとは(「現場で役立つシステム設計の原則」より)

More than 1 year has passed since last update.

現場で役立つシステム設計の原則 を読んで、
その内容をチームに伝えるようにまとめたメモです

ドメインモデルとは

業務で扱う(最小)単位でデータとロジックをひとまとめにして整理する技法

ドメインモデルが実現する世界

  • どこに何のロジックが書いてあるか、(ソースを見るだけで)わかる
  • 改修しやすいプログラム

ドメインモデルで解決したい問題

どこに何のロジックが書いてあるかわからない問題
- わからないから適当に書く→適当に書くからわからない → わからないから・・・
- ある修正をしようと思っても、どこまでが影響範囲かわからない
- 使用している箇所全grepして調査
- 同じような処理、分岐が重複してしまう

これを解決したい。これだけを考える。

なぜ(今までのシステムは)改修は難しいのか

  • 機能クラスと、データクラスをわけてしまうから
    • そのデータクラスを呼べる箇所 = 業務ロジックがかけてしまう
      • どこになにが書いてあるかわからなくなる
  • 共通クラス、Utilクラスを作ってしまうから
    • 誰でも呼べる = そのクラスを使って何をやっているのか、影響範囲が大きくなる
    • 微妙にニーズが違うせいで、関係ない分岐まで混ざってしまう
    • 肥大化して結局どこに何が書いてあるかわからなくなる
    • 重複コードが生まれる

Untitled Diagram (1).jpg

じゃあどうすればいいのか

  • データとロジックを一体化することで、業務ロジックを整理する
  • 三層+ドメインモデルの構成にする
  • すべての業務ロジックをドメインモデルに集める
  • 他の層は、判断、加工、計算はドメインモデルに任せる

Untitled Diagram (1).png

実装する上でのポイント

値オブジェクトを使う

なにかの料金クラスがあるとして、そのインスタンス変数をInteger moneyとしない。
専用の型(クラスやインターフェース)で、不適切な値は混入させない。
値オブジェクトを用意すると、コードが安定する。

値オブジェクトは不変にする

値が書き換わるときは、別オブジェクトをnewして返すこと
内部で値を書き換えてしまうのは副作用が大きすぎる。
コードを読むときに、この変数はどこで値が変わっているか、を気にしないといけなくなる
→改修の手間(調査)が一気に面倒になる

変数の値は変わらない。だからコードが安定する。

悪い例

Money price = new Money(3000);

price.setValue(2000);  // × 値は書き換えている
price = new Money(4000); // 1つの変数に別の値を代入している

良い例

Money basePrice = new Money(3000);
Money discount = basePrice.Minus(1000); // minusメソッドは、内部で引数を元に計算した値をnewして返す

Money option = new Money(2000); // 変数は上書きしない

クラスを使う側にロジックを一切書かせない

従業員 classがあった場合

Employee class {
    // 入社日時 (データ
   JoinDate joinDate;

    // 生年月日 (データ
   Birthday birthday;

    // 職種 (データ
   JobType jobType;

    // 年齢取得メソッド(ロジック
   Integer Age () {
        // 生年月日から年齢を返す
   }

    // エンジニア判定メソッド
    boolean isEngineer () {
        // jobTypeがエンジニアならtrueを返す
    }

    // クリエイター判定メソッド
    boolean isCreator () {
        // jobTypeがエンジニアorデザイナならtrueを返す
    }    
}

getterは基本的に使わない
→利用するクラスはgetしたデータでなにをしようとしている?
→データを使って、加工/判断/計算をする場合は、ドメインオブジェクトに必ず閉じ込める
→そのデータを使うロジックは、ドメインオブジェクトを見ればわかるようにする

利用側のクラスが↓こんな感じで判断してはいけない

if (employee.getJobType() == エンジニア) {
    XX処理
}

クラスをデータの入れ物として考えない

あるクラスを「データの入れ物」と考えてしまうと、そのクラスからデータをgetして、自分でロジックを書くのが当たり前になってしまう

データを持つクラスのメソッドを「ロジックの置き場所」と考えれば、そのクラスが、判断、加工、計算までやってくれる、便利な部品になる

メソッドの書かれている場所の妥当性を考える

インスタンス変数を使わないメソッドは、そのクラスのメソッドとして不適切
(インスタンス変数でない引数だけを使うメソッドはだめ→どこに何が書いてあるかわからなくなる原因)
→つまりそれは、その処理で使っているクラスのメソッドであるべき(データの近くにロジックを置く)

その他実装のポイント

  • 短いメソッド、小さいクラスを使って、コードを整理する
  • 値オブジェクトを作る
  • クラス名や、メソッド名を業務の用語と一致させる

クラスがたくさんできる、、、?

できます。
クラスがたくさんできるので、それをパッケージ(ディレクトリ)に分けて、管理しましょう。
パッケージが増えてきたらサブパッケージで管理。

階層が増えてきたり、ある階層には1クラスしかない、とかもあると思う。
けど、それでもよい。
大事なのは、どこに何があるかをわかりやすくすること。

その階層を見るだけで、どこに何が書かれているかわかるようにする。

また、パッケージをわけ、それの使う、使われるの依存関係をはっきりすることで、ロジックの重複を避けることができる
知らないでいいことを知りすぎたり、責任を誰が持つのかを明確にする

補足

  • 設計初期の段階から、完璧なドメインモデルは作れない
    • むしろ作りながら、育てていく(リファクタしまくろう

ドメインモデルの設計が難しいと言われるのはなぜか

  • オブジェクト指向の経験不足
    • 手続き型の発想から抜け出せていない。
    • データとロジックを1つのクラスにまとめる考えのメリットが感覚的にわかっていない
  • 要件定義、分析のやり方が間違っている

まとめ

  • 変更が大変になるのは、データクラスと機能クラスをわけるから
  • データクラスを使うと、業務ロジックの重複が増える
  • 全体の見通しを良くするために、データとロジックを一体にする設計を徹底する
  • 業務ロジックは、業務データの近くにまとめる(ドメインオブジェクト
  • ドメインモデルは、画面やDBの都合から独立させる
  • 他の層は、業務的な判断/加工/計算ロジックをドメインモデルに任せることでシンプルになる

謝辞

この記事を書いたところ、この本「現場で役立つシステム設計の原則」の著者である増田さんからお褒めの言葉を頂きました。
https://twitter.com/masuda220/status/1191627212903964673
こちらこそ大変参考にさせて頂いております。素晴らしい本を書いてくださりありがとうございます。

jimpei
五島列島出身。福岡在住エンジニア
yahoo-japan-corp
Yahoo! JAPAN を運営しています。
https://www.yahoo.co.jp
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away