Togetter/min.tを安心してお使い頂くためのガイドラインを公開しました。

BirthdayというValueObjectはありなのか?杉本氏(@sugimoto_kei)の考えのまとめ

杉本氏( @sugimoto_kei )によるBirthdayというValueObjectの是非、業務ロジックとそれへの入力値の依存関係からの考察
7
たなかこういち @Tanaka9230

BirthdayにasLocalDateとcalcAgeというメソッドを生やすか、getDateとgetAgeというメソッドを生やすか、インターフェースなら、後者の方が本来"正しい"のでしょう。 twitter.com/sugimoto_kei/s…

2019-09-06 22:38:14
杉本啓 @sugimoto_kei

@Tanaka9230 僕としては、Birthdayというクラスを設けることについて、基本的には、違和感を感じます。誕生日は、人の集合を定義域とし日付の集合を値域とする関数であって、値集合ではないと思うからです。

2019-09-07 09:12:48
たなかこういち @Tanaka9230

@sugimoto_kei 杉本さんのお考えは、次の疑似コードの(A)であるべきで(B)は違うと思う、 ということだと捉えて合ってるでしょうか? pic.twitter.com/KCPFWlPdPa

2019-09-07 11:56:32
拡大
杉本啓 @sugimoto_kei

@Tanaka9230 はい、合っています。Dateには時分秒の情報は含まれていない前提で。

2019-09-07 12:34:25
杉本啓 @sugimoto_kei

@Tanaka9230 誕生日を日付と別のクラスにするのは年齢計算などをメソッドとしたいからでしょうが、年齢計算には満年齢や数え年、保険年齢などもありドメイン依存です。Personはそれら複数のドメインから依存されます。Personが依存する Birthday にこれらの概念を含めるとなれば、依存性の観点からも問題ですね。

2019-09-07 12:53:20
杉本啓 @sugimoto_kei

@Tanaka9230 年齢計算のような、日付を使う特殊目的の計算にあっては、ドメイン依存の計算方式が、より一般的な概念である日付に依存するのであって、その逆ではないということです。だから、計算を日付に含めると、不必要な依存設計が持ち込まれるかあるいは、認識上の価値が少ない無数の日付クラスが生まれます。

2019-09-07 12:58:52
たなかこういち @Tanaka9230

@sugimoto_kei 先ほど例示はtrait Personとしましたが、より実践的にはtrait Insuredとかなはずで、単なるPersonなど実践的には出てこないはずで、そうするとgetAgeの内容変わるはずでしょ? といった理解で合ってるでしょうか?

2019-09-07 13:19:40
杉本啓 @sugimoto_kei

@Tanaka9230 合っています。まあ、満年齢はどこでも使うでしょうから、Personに含めても問題ではないでしょうが、基本の話をすれば、年齢計算はドメイン依存ではないかと思います。

2019-09-07 13:23:44
杉本啓 @sugimoto_kei

@Tanaka9230 DCIでいうならば「オブジェクト」ではなく、「ロール」の実装に含めるべき、ということになるかもしれませんね。

2019-09-07 13:25:33
たなかこういち @Tanaka9230

@sugimoto_kei さらに考えまして、次の三つの論が展開されると思いました。 (1)ドメイン依存の関数をよりLeaf側のtraitに装備することの一般的な良し悪し (2)「ドメイン依存」の件、実は例示のコード全体がnamespace LifeInsurance配下にあることが前提だった場合 (3)何せ誕生日から年齢が導出できるとする関係の是非

2019-09-08 12:43:06
たなかこういち @Tanaka9230

@sugimoto_kei 杉本さんは(1)は一般には、意味論的価値の薄いtraitを増産してしまいがちでよくない、という意見と理解しました。 (2)は、この前提があれば、例示の(B)でも意味論的に散乱してないと言えないでしょうか?

2019-09-08 12:50:58
杉本啓 @sugimoto_kei

@Tanaka9230 (1)の「Leaf側」という言葉が理解出来ていません。どんな意味でしょうか?

2019-09-08 12:52:18
たなかこういち @Tanaka9230

@sugimoto_kei 木構造があったときのRoot->Branch->Leafという意味でした。 PersonとBirthdayの例の(B)は、より葉側にはより一般なものを、より特殊なものはより根側に置かれるべきが、そうなってない例、という理解です。

2019-09-08 13:15:12
杉本啓 @sugimoto_kei

@Tanaka9230 理解できました。ちょっと整理してお答えしますね。

2019-09-08 13:22:38
がくぞ @gakuzzzz

BtoCだとbirthdayの内部表現はEither[LocalDate, MonthDay] にしたい場合があり、age はOption[Int] にしたくなる場合があったりする

2019-09-08 13:58:40
杉本啓 @sugimoto_kei

@Tanaka9230 ① 基本的な考えは、最初のツイートに書いた通りです。僕は、誕生日は値集合ではなく、Personを定義域としDateを地域とする関数なので、値クラスとしてモデリングすることに違和感があります。モデルは私たちの認識の構造を出来るだけ素直に映すべきと思うのです。

2019-09-08 14:00:58
杉本啓 @sugimoto_kei

@Tanaka9230 ②それを踏まえても、「誕生日としてあり得る日付の集合」をBirthdayクラスとしてモデル化することは可能です。しかし、それが表すのは日付の集合(Date)と変わらないし、妥当性検証ルールや文字列表記ルールなど、通常、値オブジェクトに結び付けられる知識に関しても両者に違いはないので、..(続)

2019-09-08 14:02:00
杉本啓 @sugimoto_kei

@Tanaka9230 ...一般的に言えば Birthday クラスを設けるメリットはないと思います。

2019-09-08 14:02:48
杉本啓 @sugimoto_kei

@Tanaka9230 ③ そうなると、ポイントは、年齢計算のような「ビジネスロジック」をどこに置くか、ということに絞られてきます。まず、日付(Date)クラスに含めるべきではないと思っています。年齢計算の方が日付より特殊なドメイン(コンテキスト)に属するからです。

2019-09-08 14:03:34
杉本啓 @sugimoto_kei

@Tanaka9230 ④次に、年齢計算を含むBirthdayというクラスをここで再び登場させることも可能です。ただ、「誕生日」クラスを立てたとしても、年齢計算が誕生日(+その他の情報)に依存しているのであって、その逆ではないという点は同じだ思うのですよね。誕生日は年齢計算の一要素に過ぎません。...(続く)

2019-09-08 14:04:43
杉本啓 @sugimoto_kei

@Tanaka9230 ...だから Birthdayクラスに年齢計算を含めるのは(僕的には)NGなのです。Birthdayではなく、単に AgeCalcみたいなクラスを設ければよいと思います。AgeCalc.calculateAge(誕生日,現在日,年齢計算方法)みたいな、あるいは ageCalcMethod.calculateAge(誕生日,現在日)。

2019-09-08 14:06:07
杉本啓 @sugimoto_kei

@Tanaka9230 ⑤その上で、たなかさんのご質問ですが、まず、基本として、たなかさんの先に出された例が traitであることを重く見ていませんでした。この点に起因して、やや、理解に混乱があるかもしれません。まず[1]について、ドメイン依存の関数を含むtraitを設けることは一般論として悪くないと思います。..(続)

2019-09-08 14:07:41
杉本啓 @sugimoto_kei

@Tanaka9230 ...[2]については、namespace はドメインを区切るための仕組みであって、ドメインそのものではないので、むしろ、 AgeCalcみたいなクラスは、積極的に別の namespaceに配した方がよいと思います。...(続)

2019-09-08 14:09:22
杉本啓 @sugimoto_kei

@Tanaka9230 ...[3]については、重要なポイントで、誕生日計算や納期計算みたいな「ビジネスルール」が、誕生日や納期といった単一の項目に基づくのはむしろ特殊例なのではないかと思います。基本的にはビジネスルールが複数の種類の値に依存するのであって、その逆ではないと思います。

2019-09-08 14:11:19
杉本啓 @sugimoto_kei

@Tanaka9230 以上、以前から思っていたことを整理するよい機会になりました。たなかさん、良いご質問をありがとうございました。誤解があればご指摘を^^

2019-09-08 14:13:27
残りを読む(11)

コメント

tarosuke @tarosukenet 2019年9月9日
オブジェクトとしては人物か何かのプロパティになり得るが単体では存在し得ず、しかし実装としてはクラスが丁度いいのでオブジェクトだと思いがちだが実際には値。まぁ日付型でいんじゃね?
0
yyyy @yyyy30090024 2019年9月10日
@sugimoto_keiさんはDSL開発の立場から語っているのでは?DSL開発の場合コードから読み取れるのは最終的なソフトの仕様ではなくそのDSLの仕様だけど、DSLでない場合はコードから最終的なソフトの仕様を読み取りたいものではないのだろうか。DDDを語るのにDSLを前提として語ると混乱を齎すのでは。
0
BABA Motoharu @calc3 2019年9月11日
特殊例ですが結婚式場の管理システムで要求される新郎新婦の年齢表記は挙式日時点の満年齢になります。そういう例まで含めて考えると年齢計算のロジックは年齢表記を要求する情報のクラスに付けるのが妥当、ということになりますね。まあ、実際には日付クラスに汎用性のある年齢計算ロジックを積んでおいてその中からチョイスする実装になるでしょうが。
0