出てくるエンティティ・値オブジェクトを筋トレに絡めて解説する今までにない新しい記事がこれです。前提知識Kotlinの基本的な知識(サンプルコードはKotlinです)この記事で取り上げることエンティティ値オブジェクトドメイン駆動設計ドメイン駆動設計とは、システム化対象ビジネスの複雑性と戦う道具の一つです。複雑性と戦うために、ドメイン駆動設計では対象ビジネスの概念、考え方をドメインモデルとしてモデル化します。このドメインモデルがとても重要です。ドメイン駆動設計ではドメインモデルを中心に設計開発を進めていくためです。学習をしていくうえでドメインモデルを一番最初に学ぶことは難易度が高いように思います。ドメインモデルは対象に応じて様々な形態をとるものであり、説明も概念的になりがちです。これは筋トレに例えると、最初からデッドリフトに挑戦するようなものです。デッドリフトでは正しいフォームが大切であり、初心者が最初から手を出しても効果的なトレーニングが行えません。同じ様に、ドメインモデルを最初から学ぼうとしてもあまりに概念的すぎて学習が効率的、効果的にならない可能性があります。そこで、本記事では実装上の課題をドメイン駆動設計ではどのように解消するのかを中心に説明し、どのような実装になるかを示します。説明の際にはドメインモデルの内容を省きます。本記事がドメイン駆動設計の学習の入り口(もしくは筋トレに興味を持つきっかけ)として役に立てればと思います。エンティティジムのユーザ管理システムを構築する場合、ユーザー一人一人を区別して管理する必要があります。田中さん、佐藤さん、鈴木さんといった人がそのジムに登録した場合、システムはこれらの人を区別して管理する必要が出てきます。この場合、それぞれの人をどのように区別するべきでしょうか?一つの案として筋肉量を比較するというものがあります。この場合、それぞれのユーザの筋肉量が異なれば、それぞれを別の人物として管理することができるでしょう1(田中さん50%、佐藤さん45%、鈴木さん55%)しかし、ここで田中さんが追い上げて筋肉量が55%になってしまうとそれぞれのユーザーを区別することが出来なくなってしまいます。(鈴木さんと同じになってしまう)筋肉量はその人の一生を通じて固定したものではありません。筋肉は鍛えていれば増加します。プロテインを飲めばなおさらです。属性があったとしても、それらは誰かを表すものではなく、ある時点でのその人の情報でしかありません。年齢は毎年増えますし、名前も変えることが出来ます。ユーザーの属性が変化しても、同じ属性を持つユーザーがいたとしても、それぞれを区別・追跡する必要があります。属性だけでは区別できない性質は同一性と言われ、同一性を持つものはエンティティと言われます2。エンティティの比較エンティティ同士はそれらが持つ同一性をもとに比較する操作が必要です。エンティティに対して識別子(ID)を割り振り、それをもとに比較することがよく行われます。IDは一度割り当てたら再度変更することが出来ないようにします。値自体をイミュータブルにし、JavaやKotlinでSetterをIDのフィールドにつけないようにします。システムがエンティティをどのような形態(Javaオブジェクト、Kotlinオブジェクト、JSON、DBスキーマ)にしたとしても、IDの表現形態が変わったとしても、IDによる比較が適切に行われるようにします。"999"}ID同士を比較することで同一性を比較出来ます。比較の方法は様々ですが、私がよくやるのは、エンティティを表現するレイヤースーパータイプを用意し、エンティティの比較方法をシステムの中で統一させてしまいます。}}IDは上記のようにLong型としてエンティティに持たせることもできますが、特に理由がない限り、IDは後述する値オブジェクトを使うことをオススメします。識別子に関する操作を値オブジェクトの中に集約できますし、実際のIDの値が何なのか(String、Long、Int...)について気にしなくてよくなります。IDの表現形式を変更しても、システム全体に影響が及びません。筋肉は主にたんぱく質、糖質、ミネラルから生成出来ますが、エンティティの識別子はどのようにして生成するのでしょうか。考えられる方法としては以下のものがあります。ユーザーが指定するアプリケーションが自動生成する永続化システムが自動生成するユーザーが指定するとは、ジムの入会などでユーザがIDを直接入力し、それをユーザの識別子として利用すると言うことです。この場合、システムが識別子を自動生成する仕組みを持つ必要はありませんが、システム内でユーザが指定してきた識別子に一意性があることを保証する必要が出てきます。なければ同じユーザIDを持つジム会員が発生してしまいます。(みんな他人の請求書を受け取りたくはないはずです)アプリケーションが自動生成する方法は様々なものがあります。(ID生成大全)例えば、UUIDを使用して識別子を自動生成する場合以下のように実装できます。"8383")注意すべき点は、アプリケーションが自動生成する識別子は人間にとって読みやすいものではないということです。識別子を直接表示したり、識別子の入力を求めたりすると使いづらいシステムになってしまいます。最後の永続化システムが自動生成するとはデータベースのシーケンス値といったものを使用するということです。永続化システムにエンティティクラスごとのシーケンスジェネレーターを用意し、アプリケーションからその値を採番することで識別子を生成します。この方法のメリットは採番した値に一意性があることを保証しやすい点です。デメリットは識別子を取得するのに時間がかかることです。エンティティを生成するたびに永続化層にアクセスしに行く必要が出てくるからです。このデメリットの回避策としてシーケンス値をキャッシュする方法が考えられます。永続化システムが再起動するなどしてキャッシュが破棄されてしまうと、使用されない値が生まれてしまう点に注意してください。値オブジェクトジムでダンベルを上げるときはその重さに注意を払います。自分の負荷が低すぎると当然筋肉が引き締まりません。高すぎると怪我をします。今のコンディションにフィットする重さを選ぶよう注意しましょう。重さが同じであれば、どのダンベルを使っても大丈夫です。このようなある対象の属性や、何らかの物事を記述することにしか関心がない場合、値オブジェクトとして扱うことが出来ます。値オブジェクトが表現するのは何であるか(ダンベルの重さ、たんぱく質量、消費カロリー)だけを表現し、どれであるか(XX社が2018年に発売した製造番号YYYYYのダンベル)は表現しません。同一性は与えず、比較を行うときには属性のみ対象にするようにします。値オブジェクトは基本的に以下の特徴があります。システム化対象領域のある概念の、計測したり、定量化したり、説明したりする。完全に置き換えることができる。不変なものとして扱うことができる。等しい値かをオブジェクト同士で比較できる。ジムのユーザー管理システムが、それぞれの人がその日使用したダンベルを記録するようになったとしましょう。その時、ダンベルの重さにしか注目する必要がないとします。この場合、ダンベルを以下のうように値オブジェクトとして実装できます。}}値オブジェクトはイミュータブル値オブジェクトは状態を変更出来ないようにします。Setterを用意したり、メソッドの中で値の更新などを行なってはいけません。このようなことをするのでしょうか。値オブジェクトが変更可能である場合、開発者はその値オブジェクトの状態を常に気をつけていかなくてはなりません。鈴木さんが50kgのダンベルでアームカールを50回行なったとしましょう。その場合、以下のように書くことが出来ます。50)この後、dumbell変数に対して重さの変更を行ってしまったとすると、鈴木さんが100kgのダンベルでアームカールを50回行ったことになってしまいます。筋トレとしては素晴らしい成果ですが、不正な記録です。ダンベルの重さを100kgに変更dumbell.changeWeight(100)こういった状況はシステムが複雑になればなるほど管理するのが難しくなっていきます。思わぬところでバグを作ってしまったり、システムの理解容易性や拡張性を妨げる原因にも繋がります。値オブジェクトの状態を変更できないようにすると、上記で挙げたようなことは起こらなくなります。開発者は値オブジェクトを扱う時、どのインスタンスなのかを気にする必要がなくなります。その値オブジェクトが別のインスタンスからも同時に参照されていようが、あるいはサブルーチンに参照ごと渡そうが、値オブジェクトの状態は変更されることがありません。このようにすることで、システムをよりシンプルに、より理解しやすくできます。上記で示したように値オブジェクトは不変値である必要があります。値オブジェクトが示す値を変更したい(ダンベルの重さが50kgから100kgになったとか)場合もあるはずです。その場合、値オブジェクト全体を置き換えることで対応します。50kgでトレーニングしていると思ったら実は100kgだったなんてことがあったとします。その場合は50kgを表すダンベルの値オブジェクト自体を100kgで置き換えます。Dumbell(100))この時のユーザクラスは以下のようになっています。Int)}終わりにこの記事ではドメイン駆動設計のエンティティ・値オブジェクトについて説明しました。ドメイン駆動設計ではこの他にもサービス、リポジトリ、ファクトリ、集約などの実装パターンがあります。これらについては今後少しずつ説明できたらなと思っています。〜変更を楽で安全にするオブジェクト指向の実践技法ここで言う筋肉量とは全体重のうち筋肉が占める重量の割合です。