はじめに
今、多くの開発手法が存在している中でひときわ耳にするのが「ドメイン駆動開発」、通称DDDです。
このシリーズでは、「ドメイン駆動開発」とは一体どういったものであるか、実際に職場で使っている人、導入を検討している人など、より多くの開発者に向けてわかりやすく説明していきたいと思います。
このシリーズを読破すれば、「ドメイン駆動開発」の全容をざっくり理解できるでしょう。
記念すべき第一回のテーマは「値オブジェクトとエンティティ」です。
対象読者
- 職場でドメイン駆動開発を使って開発を行っている方
- これからドメイン駆動開発の導入を検討している方
- エリック・エバンスの本分厚いよー、ぴえん🥺って方
- その他、ドメイン駆動開発ってなにー??って方
ドメイン駆動開発とは
今回のテーマである「値オブジェクトとエンティティ」についての説明を行う前に、そもそもドメイン駆動開発がどんなものか、その概要と目的を簡単に解説します。まあ、それは大体わかってるよーって方は飛ばしてもらって構いません。
ドメイン駆動開発は2003年にエリック・エバンスが提唱したソフトウェア開発技法です。
ドメイン駆動開発の特徴は、顧客と開発者の間で共通言語となる「ユビキタス言語」を共有して、その中で重要な立ち位置をしめる概念(ex. ショッピングサイトにおける「ユーザ」とか「商品」、「カート」とか)をそれぞれドメインモデルに落とし込み、そこからドメインオブジェクトの実装に落とし込む、という点です。大きくやることとしては
「ドメインの概念」 -> 「ドメインモデル」 と 「ドメインモデル」 -> 「ドメインオブジェクト」の2つですね。それぞれ簡単に説明します。
「ドメインの概念」 -> 「ドメインモデル」
ここではドメインの概念を明確化した後に、アプリケーションに必要な知識だけをよりすぐります。と言ってもなにも難しいことではありません。
例えば、書籍通販サイトがあったとして、ここに登場するドメインには当然、「書籍」があります。しかし、「書籍」というものがもつ全ての情報がアプリケーションに必要な訳ではありません。「著者」や「出版社」、「値段」なんかはアプリケーションには必要そうですね。ですが、「使用紙」と言った、書籍の材質に関する情報は必要でしょうか?通常の書籍通販サイトにはおそらく不必要なものでしょう(まあ、100%必要ないとは言い切れませんが笑)。
このように、開発しようとしているアプリケーションに必要な知識だけを、ピックアップしていきます。
「ドメインモデル」 -> 「ドメインオブジェクト」
アプリケーションに必要なドメインとその属性が固まったら、次はそれを実装できるオブジェクトの単位にまで落とし込んでいきます。
この、ドメインモデルをドメインオブジェクトにまで落とし込む段階で登場するのが、この記事のテーマである、「値オブジェクトとエンティティ」です。
まあ要するにこの二つは、実際に設計から実装に移っていこうか、って時に対面するものな訳ですね。
ドメインモデルとして列挙される概念は、大まかこのどちらかに当てはまります。
ですが、この区別(ドメインモデルが値オブジェクトかエンティティなのか)の方法について、わかりやすく説明してる記事がまあ少ない。まじでなに言ってるかわかんないモノが多すぎる。ので世界一わかりやすく説明したいと思います。
値オブジェクトとエンティティ
値オブジェクト(別名:バリューオブジェクト)は、その名の通り「値」をオブジェクトとしたものです。
値オブジェクトには以下の特徴があります。
- 不変である
- 交換が可能
- 等価性によって評価される
- ライフサイクルを持たない
一方エンティティは値オブジェクトとは対をなすオブジェクトです。特徴は以下。
- 可変である
- 同じ属性であっても区別される
- 同一性によって評価される
- ライフサイクルを持つ
この中で、はじめの段階での判断に特に使えそうなのは3あたりでしょうかねぇ。
等価性と同一性による区別
等価性と同一性による区別において、便利なのは「そのオブジェクトの属性値が変わった場合、そのオブジェクトは変更の前後で別物とみなされるか」、「値が同一の場合、それらは同じものとみなされるか」という問です。その答えがYesならそのオブジェクトはエンティティです。
ここではショッピングサイトにおける、「ユーザ」というオブジェクトから考えます。
ユーザはいろいろな属性を持ちます。例えば、名前とか、住所とか、年齢とか。後、サービス上の識別子(userId)なんかも付与されるでしょう。これらのオブジェクトは、さて、一体どちらでしょうか。
user: {
name: "ruirui_official",
address: "虎ノ門ヒルズ最上階",
age: 22,
userId: 104
}
まず、user自体について考えましょう。ユーザのるいさんは人間です。るいさんの属性値(ここでいう、ユーザー名とか、住所とか)がもし仮に変更されたとしましょう。るいさんは"ruirui_official"ってユーザ名、有名人気取りでダサくね?って気付きます。そして"rui"に変更します。加えて、最近六本木ヒルズの最上階に引越しをしたので、住所も変更しておきます。
user: {
name: "rui",
address: "六本木ヒルズ最上階",
age: 22,
userId: 104
}
情報が変更されました。さて、ここで「そのオブジェクトの属性値が変わった場合、そのオブジェクトは変更の前後で別物とみなされるか」と問います。
もちろん答えはNoですね。サービスの登録名を変えようが、住む場所を変えようが、るいさんは同じるいさんです。
るいさん、すなわちユーザというオブジェクトはここではエンティティとなります。
次にuserIdというオブジェクトに着目してみます。uesrIdはアプリケーションに登録したユーザ全員に付与される固有の識別の値です。
ここでも同じ問いをします。すなわち、「そのオブジェクトの属性値が変わった場合、そのオブジェクトは変更の前後で別物とみなされるか」です。
userIdは各ユーザに割り振られるユニークな値です。もしこれが変更された場合、そのuserIdは同じものとしてみなされるでしょうか。
104のuserIdと105のuserIdは同一なものではありません。uesrIdとして完全に別物です。そして、104のuserIdと104のuserIdは同じものとしてみなされます。
つまり、userIdは値オブジェクトと言えます。
注意点
上記のやり方で、多くのオブジェクトがどちらに属しうるのかを判断できます。
しかし上記のやり方であるオブジェクトが値オブジェクトなのか、エンティティなのかを判断する時注意しなければならない点があります。
オブジェクトが「そのオブジェクトの属性値が変わった場合、そのオブジェクトは変更の前後で別物とみなされるか」、「値が同一の場合、それらは同じものとみなされるか」を考える時、なににとって別物とみなされるのか、もしくはなににとって同じものとしてみなされるのか、ということです。
私たちが今物事を考えている世界は、あくまでもアプリケーションの世界です。なので、そのアプリケーションにとって別物とみなされるか、同じとみなされるか、という視点で考えなければなりません。
分けた後の扱い
じゃあ、ちゃんとオブジェクトを分けました。分けたまではいいのだけれど、何のために分けたの?どう区別して扱うの?って疑問が浮上するでしょう。
それは値オブジェクトとエンティティの性質として上であげた4つのうち1,2,4あたりが使えます。
値オブジェクトはその名の通り、「値」です。皆さんは値を変更する時、どのように変更するでしょうか。
const age = 22
age = 23
console.log(age) // 23
こんな感じですかねえ。値を代入しています。
これは値オブジェクトの2の特徴である、「交換が可能である」ということの意味するところです。
値は交換して変更します。
これはエンティティの値の変更方法とは少し異なります。
user.changeName("rui")
これを値オブジェクトでやっては絶対にいけません。
値オブジェクトはあくまでも「値」です。つまりただのプリミティブな値、"mojiretu"だとか100だとかの単なる値と同じです。
100.changeValue(101)
とした場合、どうなるでしょう。100は101に変更されます、、、、コードの全てにおいて。
すなわち、const a = 100は実際のaは100ではなく101になっています。なかなかにカオスですね。
これが値オブジェクトの1番目の特徴である、「不変である」の意味するところです。
終わりに
いかがだったでしょうか?ドメイン駆動開発を学ぶにあたって、まずはじめに出てくるのがこの「値オブジェクトとエンティティ」の概念です。
実装できる単位のドメインオブジェクトの代表格とも言えるこれらの二つをマスターすることは、DDD理解の第一歩です。
ですがドメインオブジェクトとしてあげられるのはこの二つだけではありません。
次の講義では、この二つ以外のものについて簡単に説明したいと思います。お楽しみに。