「ドメイン駆動設計読書会@大阪」第8回復習メモ
プログラミング道場生 kumamidori です。
「ドメイン駆動設計」読書会の第8回に参加しました。内容は、こちらのWikiにまとめられています。
この記事の前半では、復習として、関連、エンティティ、値オブジェクトについて、自問自答のQ&A形式でメモします。この道の先人から教えて頂いた内容が多いです。教えて下さった方、ありがとうとざいました。後半で、勉強会後に見つけた値オブジェクトのPHPサンプルを引用して紹介します。
Q&A
関連
Q. 情報と情報の関連は、(双方向ではなく)できるだけ一方向にすること、関連を限定することにより、 管理しやすくなる、という話があった。適切に関連を見出せるかどうかは、業務分析にかかっている。間違いなくそれを行うことはかなり難しいのでは?
A. 一発ではできない。設計、実装しながらモデルを直していく(蒸留)。 関連は、業務のユースケースにより決まってくる。ユースケースのイベントにおいて関連は発生する。実装としては、ORMレベルでは双方向で定義しておき、それを利用するときに、関連の方向、I/Fを検討すれば良いかもしれない。
エンティティ
Q. データモデリング分野では、エンティティはどう定義されているか?
A. これについては、astah*チュートリアルに書いてあった内容を、下記に一部改変して要約します。
一部改変した要約:ここから
・・・・・・・・・・・・・・・・・・・・
エンティティとは、システムの管理対象となりうるもので、人、モノ、イベント、履歴などがある。 エンティティには、リソース系、イベント系、サマリー系などがあり、以下のように分類される。- リソース系: 会社、顧客、商品など。
- イベント系: 会員登録・変更・削除、発注、出荷など。
- サマリー系: 購入履歴、出荷履歴など。
「発注日」、「出荷日」のように、「〜日」を付けることができるのがイベント系エンティティと言える。
・・・・・・・・・・・・・・・・・・・・
一部改変した要約:ここまで- 例1:DoorKeeperのような勉強会告知サイトのシステムを運用するとして、ユーザが勉強会に参加申込をする、参加申込をキャンセルする、という機能があるとすると、これらの機能を実現するにあたって、「参加申込」情報はシステムの管理対象、エンティティとして扱います。 「参加申込エンティティ」は、動詞的な行為の記録であり、イベント系エンティティに見えます。
- 例2:家計簿アプリを作るとして、月次予算・実績サマリー一覧画面があり、詳細画面へと遷移すると、該当月の予算を変更することができる、というような機能が必要だとします。ここでは、「月次収支サマリ」がエンティティとして扱われることになります。
Q. Web開発の現場において、エンティティは実装としてはどう扱われているか?
A. ORM的なライブラリを使っていることが多く、これがエンティティとして扱われている。 モデルを実装コードに反映するテクニックとして、たとえばユーザIDをそのままむき出しで表示側や業務ロジックで扱うのではなく、 いったんユーザエンティティに変換してから使うという工夫が採られる。DTO的な考え方。
値オブジェクト
Q. 「エンティティ」はみんな昔から使っているけれども、モデル駆動設計の構成要素として挙げられているパターンである「値オブジェクト」は、開発現場で見たことが無い。 値オブジェクトは、属性セットから成るが、DTOとはどう違うのか?
A. 値オブジェクトは、エンティティと同様に、モデルを表現し、ユビキタス言語とひも付く。情報を保持する役割を持つ。
エンティティと異なる点は、同一性、連続性を必要としないこと(識別が不要であること)。
いわゆるDTOとは文脈が違う。属性セットが主役になるという意味で、コードの見た感じがたまたまDTOと似た形になることもあるというだけ。
Q. 値オブジェクトの分かりやすい具体例としては、何が挙げられるか?
A. ユーザプロフィールの「住所」、「趣味」。eコマースの「Money」。レストランレビューサイトの「評価」。
Q. エンティティと値オブジェクトは関連するのか?
A. 値オブジェクトは、エンティティと直接関連する形になることが多いけれども、そうでないものもある。
値オブジェクトのPHPサンプル
PHPだと、初期化やバリデーションを考えると、気軽に値オブジェクトを定義する道具立ては無いそうです。現状のライブラリだと、自ドメインの値オブジェクトをエンティティにカスタム定義することは、できなくはないけれども、手間がかかるとのことです。(モデル側でのビューモデルや、ビュー側でのヘルパー機能を使って、ある程度はすでに工夫できているのが実情かもしれないと思いました)。調べたらサンプルが見つかったので、翻訳引用の形で紹介します。
PHPサンプル:Timeオブジェクト
Mathias Verraes さんがTimeオブジェクトのサンプルコードを紹介しています。元記事は名前付き引数についての考察なのですが、値オブジェクトと関係あるところだけを翻訳引用します。
翻訳引用ここから(原文記事リンク:Named Constructors in PHP)
・・・・・・・・・・・・・・・・・・・・
(中略)
ユビキタス言語
コードは良い具合にきれいになってきました。時刻クラスは、大変使いやすい初期化の方法を持つようになりました。設計が良くなって、以前の設計上の欠陥はなくなり、分かりやすくなっています。時刻のインターフェイスを見てみて下さい。
<?php $time = Time::fromValues($hours, $minutes); $time = Time::fromString($time); $time = Time::fromMinutesSinceMidnight($minutesSinceMidnight);
気づきましたか?3つの言葉を混同することがなくなりました。
- fromString は、PHP実装の詳細です。
- fromValues は、一般的なプログラミングタームでの並べ方です。
- fromMinutesSinceMidnight は、ドメインの言語の一部です。
言語ギーク、DDDファンとしては、まだ納得がいきません。時刻はドメインの一部なので、ユビキタス言語によって取得を分けるスタイルの方が私は好きです。
- fromString => fromTime
- fromValues => fromHoursAndMinutes
文字数が長くなってしまうことを気にする人がいるかもしれないけれども、タイピングする必要は無いのです。コンテキストでコード補完のできるエディタを使いましょう。
・・・・・・・・・・・・・・・・・・・・
翻訳引用ここまで
※上記で翻訳紹介した「時刻」は、DDDの主戦場(?)であるアプリケーションのコアドメインではなくて、汎用的なライブラリ(コアドメインから利用される、外側のパッケージに属している)と思います。汎用ライブラリを自分がこれから作る、という際には、そこが自分にとってドメインとなるのかなと思っています。
参考記事
振り返り
読書会も8回を経て、さすがに基礎部分の理解はできてきました。(これはDDD本に直接書いてあることではないけれど、)読書会で聞いた「共通性」、「可変性」という言葉を知っているだけでも、実務でのクラス設計が良い方に変わったのを感じます。今回のような復習ブログを書くのは、今後はお休みにして、以降はもう少し、自分自身がコード等でアウトプットできるような形でやっていけたら良いなと思っています。(現状だと、ブログを書くと、誰かの引用だけで力尽きてしまう感じなので、そこからもう一歩進められたらと)。