nakajima jun
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights New
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    DDD本 6章:ドメインオブジェクトのライフサイクル === ###### tags: `エヴァンス本読書会` # Discord開始位置 - [前半](https://discordapp.com/channels/432531367427964929/740202765619429487/779602083502358529) - [後半](https://discord.com/channels/432531367427964929/740202765619429487/784734506174382110) : p.146 リポジトリ〜 # 自己紹介 - なかじま(J.Nakajima) - ファシリ役 - タイムキーパー役 - こばやし(t2_Kobayashi) - 読み上げ役 # 参加の仕方 - マイクはいつでもONにして、話に参加して結構です。 - テキストチャットでも、コメントやリアクションでどんどん参加してください!ラジオ担当が拾っていきます。 - 聞いているだけの方もOKです! # ディスカッションをより豊かにするためのグランドルール - 参加者は毎回任意 - 今回は不参加、次回は参加をするといった気軽な感じ - 途中参加も断然OK! まずは聞くだけでも大丈夫です! - フィードバックを恐れない - マサカリは怖いと思いますが、アウトプットからのフィードバックを受け、学びを深めていきましょう - アウトプット7割:インプット3割の気持ちで臨みましょう! - 経験の有る無しは気にしない - 自分はDDD非経験者だから……と気後れする必要はないです - 堂々と意見や疑問を語りましょう - ページ数と同時に、節のタイトルやキーワードを言って頂けるとスムーズです - 電子書籍で読んでいるとページ数ではわからないため - 話していない人が、率先してメモしましょう - このHackMDはみんなのものです。どんどん書いていきましょう:+1: :+1: - 気になる質問や同感するものには :+1: を末尾につけてください。 # タイムテーブル | 時間 | 所要時間 | 内容 |備考 | | -------- | -------- | -------- | -------- | | 19:50- | - | 集合開始 | | | 20:00-20:10 | 10分 | 当日の流れとグランドルールの共有 | | | 20:10-20:25 | 15分 | 感想記入&HackMDに書かれた皆さんの感想・気づき・疑問をもっと掘り下げたいものを、 :+1: 付けていく | | | 20:25-21:55 | 90分 | 本の節ごとにディスカッション(ここでも適宜、HackMDに気づきとか書いていってもらっていいです) | | |21:55-22:00 | 5分 | 次回開催日と読む範囲決めてクローズ | | ## 下に感想などを書いていって下さい。どんな些細なことでもOKです。 「感想記入+リアクション付ける作業」を、輪読会開催前に早めると良さそうに思いました。 ・10分で上記を終えることができず、中途半端になることが多い(この時間で追記する人もいたり) ・この10分を討論に回すことができる(感想記入は事前に行えるが、討論は輪読会中にしかできないので、みんなで読んでる時間を勿体なく感じてしまう) --- # 第6章 序章 ## 目安の時間 - 20分 ## 感想・気づき - P.122「ライフサイクルを通じて整合性を維持すること」 - [discord](https://discordapp.com/channels/432531367427964929/740202765619429487/779670945014743062) - 複数のオブジェクトの関連が複雑になって整合性維持が困難→集約で対策? - P122に書いてありますが、集約「ライフサイクルの全て」、ファクトリ「ライフサイクルの始まり」、リポジトリ「ライフサイクルの中期と終わり」ですね - :memo: よく見ると、ライフサイクルの始まりだけが特別視されているんですね - :memo: 個人的には、特別視というより、単に関わる時間の範囲や役割で分けたものと解釈しました - :memo: ライフサイクルの始まりが特別視されてる理由はファクトリ読めばわかるかとー - P.122「ライフサイクルを管理するのが複雑でも、モデルが侵食されないようにすること」 - 永続化のためのロジックがドメインモデルを複雑にしていく→リポジトリで対策? - オブジェクト生成のための複雑なドメインロジックを複雑にしていく→ファクトリで対策? ## 疑問 - > ライフサイクルを管理するのが複雑でも、モデルが浸食されないようにすること。:+1: - [discord](https://discordapp.com/channels/432531367427964929/740202765619429487/779669279758483457) - モデルが侵食されるとは具体的にどういうことなんだろうか - 集約で境界を決めているのに集約外からオブジェクトを編集できる状態だと思ってました:+1: - :memo: ドメイン層のコードなのに、めっちゃ頑張ってデータかき集めてオブジェクト生成する手続きがあちこちに、というケースもありそう - :memo: 「ドメイン層」を名乗っているのにWeb API叩いたり DBから引っ張ってきたレコードガチャガチャしてたり、 あまつさえそれらに引数や戻り値や継承や(略)で依存してたり…… - :memo: 「ドメインモデルはドメイン課題の解決に有益な解決能力を持つ概念であるはずなのに、モデル生成のロジックだけに圧倒されて、本質的な解決能力がどこにあるのか分からない状態」を侵食されている状態と解釈。 - :memo: オブジェクトのネットワークが混沌としてもつれた状態 --- # 集約(AGGREGATES) ## 目安の時間 - 35分 - [discord](https://discordapp.com/channels/432531367427964929/740202765619429487/779672073853337651) ## 感想・気づき - p.127 「ルートエンティティは、内部のエンティティへの参照を他のオブジェクトに渡せるが、受け取ったオブジェクトは参照を一時的に使用することができるだけで、その参照は保持してはならない」という内容に、ちょっと怖さを感じる - 参照は保持するしないの話より、内部のエンティティの参照を渡して、そのエンティティの内容を参照するだけならいいが、何かしら変更などの操作が起きてしまわないかどうかが心配になる:+1: - ここでいう参照は、参照渡しの参照ではなく、閲覧という意味での参照だと勝手に解釈してました。 - 例6.1 の購入注文の整合性のところで、集約の境界の決め方に関しては「削除されたときに、こいつも一緒に削除されてほしいのか」「変更された内容が他のオブジェクトにも即座に反映されなければいけないのか」といった考え方も1つありそう。 - 購入注文と購入注文品目は同時に削除されてほしいが、商品に関しては削除される必要はないので、そこはただの値を参照するか、コピーして渡せばいいということで、集約の境界の外側にいるということが判断できそう:+1: - あとは、独立するかどうか。購入注文品目は、それ単体では独立しなそうだが、商品は集約のルートとして独立していそう。:+1: - p.126 「不変条件とは、データが変更される時は常に維持されなければいけない一貫性のルールで、集約のメンバ間の関係も含んでいる」 - p.124 「この問題は、データベーストランザクションにおける技術的な難問であるように見えるが、その根源はモデルにある。つまり、境界が定義されていないことが問題なのだ - [discord](https://discordapp.com/channels/432531367427964929/740202765619429487/779678806033301524) - はっとさせられる。何かしら追加、変更、削除をする時に、データベース側に注目しがちだけど、いろんな参照を持った巨大なモデルがあることによって、複雑にしているのだと思った。:+1::+1::+1: - 第5章の関連の話でも、関連を単方向にしたり、本質的ではない関連を除去したりしないと実装と保守が複雑になるって話があった - IDDD本10.2のところでは、トランザクション内での不変条件の整合性を完全に維持できるのが、適切に設計された集約と言っているので、トランザクション整合性のところで何かしら問題が起きている場合には、集約の境界が適切になっていないということだと思った。:+1::+1: - > 各集約にはルートと境界がある。境界は集約の内部に何があるかを定義するものだ。ルートは集約に含まれている特定の1エンティティである。 - p.125 :+1: - なぜ、ルートは値オブジェクトではなくエンティティでなくてはならないのか?読んでいてさらっと流しそうになったので、あえて深堀りしてみる - そもそもとして、「オブジェクトに対する変更の一貫性を保証するのは難しい(p.124)」上に、そのためのロックも無闇矢鱈に行うと「相互に干渉し合い、システムが使いものにならなくなる(p.同)」という問題意識がある - そこで、「データを変更するトランザクションのためのスコープと、データの一貫性を維持する(つまり、不変条件を維持する)方法(p.124)」として、集約という概念が導入された - > つまり、境界が定義されていないことが問題なのだ - p.124 - トランザクションだと実装の段階のため、モデルを考えるデータの一貫性の方が優先されるのだろうと認識しました。 - では、ルートがエンティティではなく値オブジェクトになるとどうなるか.値オブジェクトは同一性をもたず、それ故に時間・空間を超えた連続性も持たない - 一箇所でも値が変われば、そこで「別のモノ」になってしまう = 連続性が成立しえない - 連続するためには始点と終点の二箇所が必要だが、「別のモノ」になってしまうので、それぞれのオブジェクトは常に、始点でも終点でもない唯の「点」 - ルートこそが集約の同一性を担保する役割だが、そのルートが値オブジェクトであるということは、その集約は一切の同一性を提供しない - 集約に何か変更を加えた場合、変更する前の集約Aと変更後の集約A'は別の集約、ということになる - これでは、「A → A'」という変化を表現できないし、捉えることもできない - なので、ルートはエンティティでなくてはならない、ということか - 集約にとって、ルートエンティティは有る種の識別子(Identity)として機能している - > 削除の操作は、集約境界の内部に存在するあらゆるものを一度に削除しなければならない(ガベージコレクションがあれば簡単である。ルート以外に対しては、外部から参照されていないので、ルートを削除した際に、他もすべてガベージコレクションすればよい)。 - p.127 - ここの部分の「削除」は、メモリからオブジェクトが消滅することしか書いていないように見える - ということは、「集約を削除」という時、それは集約に含まれる各エンティティ・値オブジェクトをメモリ上から消失させる(「ライフサイクル」を消失させれば良い)のであって、DBレコードとして永続化したものをどうするかはまた別の話になりそう:+1::+1::+1::+1::+1::+1::+1: - 自動車の例で言えば、ある車を廃棄したからといって、その車に付いてたパーツも全て必ず廃棄するのかというと - [タイヤとかは結構中古の需要がありそう](https://www.goo-net.com/parts/shop/s4201303/p4201303D30200321001.html&gr721_001) - 中には再利用するケースもありそう(シートとか?)で、実際には関連を削除するにとどまる(値オブジェクトやエンティティに対応するレコードそのものは残る)ケースも多そう - 中には、オンメモリでしか存在しない(永続化されない)状態も有るだろうし - リポジトリの向こう、あるいは、ドメインイベントの受け取り手で、適切になんとかしてくれるはず - > 技術的なフレームワークを用いて集約が宣言でき、さらにロック機構などが自動的に実行されると、非常に便利だ。そういう助けがなければ、集約に関して合意し、それと矛盾しないようにコーディングする自己規律がチームに必要となる。 - p.128 - [Discord](https://discordapp.com/channels/432531367427964929/740202765619429487/779679640343150612) - Akkaのアクターで集約作るアレだ - 集約もドメイン層の住人のはずだけど、その宣言にフレームワークを使う(ドメイン層に、直接ドメインと関係ないものが入り込む)のは良いのかしら:+1::+1: - 集約を実装するためのフレームワークを使うことはドメインをアプリケーションから分離することと矛盾しないのでは? - :memo: それを実現する為の仕組みを導入できれば良いって事で、特定のアプリケーションフレームワークに依存しようという話しではないかと。 - :memo: 依存しよう --> できると便利だ - :memo: 「集約フレームワーク」なので、アプリケーションの枠組みみたいな全体構造とか外部インタラクションとかの枠組みではなく、あくまでも集約を実装するときに繰り返し書かれるような部分を自動化してその中にはめ込むドメイン知識を書くだけ、ってしてくれるようなフレームワーク。を想定していると読めます。 - :memo: 依存しないことが大切なのではなく、依存するとどういうデメリットがあるかって観点が大事なのかなと。 - 特定のフレームワークと距離を取るのはIFが常套手段だけど、集約でIFを使ってしまうと、集約内部のオブジェクト間の関連が定義できない - 集約IFの実装クラス依存になってしまう - Evansが想像している「技術的なフレームワークを用いて集約が宣言でき」、かつドメイン層の独立性を維持する(あるいは諦める)形は、どんなものなのだろ - 「宣言」であって「実装」ではない、というのがミソだったりするんだろうか - 集約の「実装」はフレームワーク非依存にしつつ、その外形ないし「集約です」という宣言をフレームワークで?イメージがつかない - あんまり具体的なことは考えてなかったのかもしれない(できたら嬉しいよね、ぐらいの温度感?) - 集約ルートはPOJO的なやつで宣言して、フレームワークがその実際の操作の実装をやる感じでしょうか? - 実装方法として、集約とルートエンティティは同じクラス?それとも、集約クラスの中にルートエンティティクラスを内包? - 集約の境界の引き方に関しては、そもそもこの集約はどんな不変条件があるのかとか、振る舞いを特定することが大事そうです。 ## 疑問 - 全体を通して出てくる「不変条件」という言葉、分かりにくいなあという気持ちが強い。何かもっと伝わりやすく理解しやすい言葉はないだろうか?(欲を言えば一言で):+1: - [discord](https://discordapp.com/channels/432531367427964929/740202765619429487/779674714171244556) - 一貫性のルール - ~~ビジネスルール~~(ではなさそうですね、用語解説を見ると) - 常に成り立っていなければいけない条件。 - [集約と設計の実装 p.22](https://speakerdeck.com/j5ik2o/ji-yue-falseshe-ji-toshi-zhuang?slide=22) - 集約のルートでデータの整合性を確認すると理解した。この場合、集約のルートでのみ、入力の事前チェックや事後条件のチェックを行うということだろうか? - DDDを実践している人は、図6.3のように集約をドキュメントなどに残しているのだろうか?それとも、HogeRootArrgegatesのようにコード上で表現しているのか気になった:+1::+1::+1::+1::+1: - [discord](https://discordapp.com/channels/432531367427964929/740202765619429487/779672073853337651) - :memo: 図式化結果、「これトランザクション整合性の必要性なくね?」みたいな判断をしたことはままある。普通か。 - :memo: コードだと集約の範囲を明示する以上のコードがノイズになって読み取りにくいと感じるので、そういう意図で資料にしておきたい派 - :memo: ホワイトボードで整理して、写真撮っておく、が、蒸留には向かない気がしている - :memo: モデル図15票、コード3票、その他1票 - p.127 「購入注文と品目の生成と削除が自然と結びつけられるのに対し、商品の生成と削除は独立しているのだ。」とあるが、存在しない商品が購入注文品目にあるのは問題ではないだろうか? - 削除に関して`ガベージコレクションがあれば簡単である`との表記があるが、これは言語機能におけるガベージコレクション(JVMのような)のことを指しているものではない...? :+1: - [discord](https://discordapp.com/channels/432531367427964929/740202765619429487/779676481830912010) - たとえばRDBMSのカスケーディングのようなものも指すのだろうか - :memo: 質問者です。エンティティのライフサイクルは、メモリ上の存在期間を超えることが普通にありそうだなと思っていて - :memo: 集約の実装上の表現は、あるイベントにおけるメモリ上の状態、と解釈してもよいんでしょうか? - :memo: 実際、このガベコレがどうこうの部分はちょっと浮いているというか、かなり詳細寄りの話が急に混ざったような感はある --- # ファクトリ(FACTORIES) ## 目安の時間 - 35分 ## 感想・気づき - 読んだ全体的な感想として、生成や再構成の知識は複雑ではあるが、生成物自体がどう振る舞うかどうかとは全く別物であるため、それを一緒に書いてしまうと、モデルの表現が不明瞭になるというのがあるから、分けたいよねというお話だと感じ取りました(自動車工場と自動車の関係を例として示してる) - では生成の知識はどこに書くのかと言えば、とある集約ルートにファクトリメソッドかもしれないし、まったく別のサービスとして、ファクトリを作っておくかもしれない。:+1: - p.135 「さらに、部品を組み立てる仕事は、シャフトを回転させる仕事とは全く関係ない」、「同じように、複雑な複合オブジェクトの組み立ても、そのオブジェクトが完成した時に行うことになる仕事からは、分離しておくのが一番である」 - [discord](https://discordapp.com/channels/432531367427964929/740202765619429487/779690416680468491) - ファクトリを別オブジェクトで定義する動機が、このあたりを読んでいるとわかりやすい - この例はとてもわかりやすいですよね。:+1::+1::+1::+1: - ドメインオブジェクトそのものに構築させると、本来の責務が不明確になりやすく、アプリケーション層に置いていても、アプリケーション層がやる仕事が不明確になってくる。 - 適切な置き場としてファクトリというパターンを使おうね、という話 - `Hoge`というオブジェクトに対して`HogeFactory`を用意するのではなく、`Hoge`を境界内に持つ集約ルートに、ファクトリオブジェクトを所属させる、という気づき - > コンストラクタの呼び出しでさえ、構築しているオブジェクトの具象クラスにクライアントを結びつけてしまう。クライアントを変更しないと、ドメインオブジェクトの実装を変更することができず、リファクタリングは困難になる。 - p.135 - [discord](https://discordapp.com/channels/432531367427964929/740202765619429487/779690673188110336) - Effective Javaで、「コンストラクタよりもstaticファクトリメソッドを使うべき」という理由の一つとして、これと同じ様な理由を挙げていたのを思い出した - > staticファクトリーメソッドの3つ目の長所は、コンストラクタと異なり、メソッドの戻り値型の任意のサブタイプのオブジェクトでも返すことができることです。 (...) クライアントは、ファクトリーメソッドから得たオブジェクトのクラスが何であるかも知りませんし関心もありません。単に、EnumSetのサブクラスということだけに関心があります。 - Effective Java 2nd p.6-7 - 他の理由として、メソッド名が付けられるので、生成されるオブジェクトの特徴や意図を表現できたり - Effective Javaではファクトリメソッドをだいぶ推してる印象を受ける一方で、Kent Beckの「実装パターン」では、「必要な時にだけ使え」と書いてあったりして(Kent Beck 実装パターン p.111)、表現の差が面白い:+1::+1::+1::+1::+1: - [Discord](https://discordapp.com/channels/432531367427964929/740202765619429487/779690673188110336) - :memo: Factoryって使わない理由がない、っていう感じで書いている人も確かにいますね。 - :memo: 基本的に OOP を実践していくと new を隠す傾向になっていく方が多いと思う new がコードを硬くする - :memo: https://twitter.com/t_wada/status/1278836435047440384 - > 複雑なオブジェクトの生成はドメイン層の責務であるが、その仕事はモデルを表現するオブジェクトのものではない。 (...) すなわち、これから設計に追加する要素は、モデル中のどれにも対応していないが、それでもドメイン層の責務の一部なのである。 - p.136:+1: - モデルには存在しないものが設計において追加されうる。ということは、モデルと設計はそれぞれ異なるものである = 区別される - 全く等しいものである = 区別されないなら、こんなことが許されることも、そもそも可能であるはずもない - DDDにおいてモデルと設計は開発者・設計者が「一致させる」「結びつける」関係なのであって、明確に区別されるというお話、ここで腹落ちした気がする :+1: - > [DDD本の第3章を読めば、エヴァンスが「モデル」と「設計」を注意深く区別していることがわかる。設計とはコードの設計だ。彼が言うモデル駆動設計とは、ユーザーに理解できる「モデル」とコードの「設計」を結び付けることだ。](https://twitter.com/sugimoto_kei/status/1314843283135184897) - このあたり、あくまでソフトウェア開発のための方法論ということもあり、実践を踏まえた記述。単に「アカデミック( is 何)」なだけの空論ではない。 - (「アカデミック」≠アカデミックという意図のカッコです) - p.139 「ファクトリを所属させるのは、その生成物に対して密接で自然な関係を持つオブジェクトだけにするべきである」 - p.141 「最も安全なパラメータは、下位の設計層に由来するものである。1つのレイヤの内部でも、基本的なオブジェクトがより上位のオブジェクトに使用されるというかたちで、自然な階層ができる傾向がある」 - 16章にある責務のレイヤの話だろうか? - p.142「不変条件のロジックはどこへ置くべきか?」で明快な指針が得られそうだと思ったけど、ますますわからなくなった - と思ったけど、次のエンティティにファクトリ対オブジェクトファクトリのところで、「プログラムが識別子を割り当てる場合はファクトリで制御するのがよい」と書いてあるので、採番知識もエンティティの生成に対する知識になるけど、エンティティの中でやるのはぎこちないから、ファクトリに収めようってことなんだろうと理解。 ## 疑問 - ここで言っている、クライアントとは、なんだろう - アプリケーション層とは区別されているっぽいので、他の何かだとは思うんだけど、アプリケーションにあるメソッドを使用するクライアントコードのことを言っている? - ドメインオブジェクトを使う側のオブジェクトのことですね - p.137 「あるオブジェクトの持つデータや、場合によってはルールが、別のオブジェクトを生成する上で非常に支配的である場合〜」の説明が、図6.14に掛かっていると思われるが、「取引注文」オブジェクトを生成する為に、「証券取引口座」オブジェクトの口座番号を知らないといけない(=他のオブジェクトが支配している属性)から、「証券取引口座」内で、「取引注文」を作ってしまうファクトリメソッドを用意しよう、と解釈しましたが、皆さんどうでしょうか。 - p.138 図6.13の 購入注文が 購入品目を作るのはわかるけど、購入品目だけを返してしまうのがなんとなく違和感を覚える - [discore](https://discordapp.com/channels/432531367427964929/740202765619429487/779685030083035177) - 変更メソッドとして戻り値なしのものを用意するでも良さそうだし、イミュータブルを守りたいなら、新しい購入品目を追加した購入注文のルートを返して、そこから新しく追加された購入品目を参照するのではダメなのだろうか?:+1: - p.142 「不変条件のロジックはどこへ置くべきか?」のところで、ファクトリに置いたほうがシンプルになる、という例で、エンティティにおいての同一性を表す属性を割り当てるルールがあるかもしれない、と書いてあるが、これはIDの採番ルールみたいなものだろうか - [discord](https://discordapp.com/channels/432531367427964929/740202765619429487/779682449516003359) - 大抵の不変条件は集約ルートで任せられるのかと思っていたけど、みなさんがファクトリのオブジェクトに用意する指針があれば、お聞きしてみたい。:+1::+1::+1::+1::+1::+1::+1: - :memo: ただ引数を受け取ってフィールドに格納する以上のことをする場合かな あ、ファクトリ作るか頭にちらつくタイミングね 必ずしも作るわけじゃないです - :memo: 集約にファクトリを持たせた方がわかりやすいというかユビキタス言語に沿う時は作りますね。 - :memo: 匂いマジ大事。設計の言語化の第一歩。これを見逃してはいけない。 - :memo: 自分がファクトリがほしくなる典型例。 コンストラクタがやたらと複雑になるとか、 コンストラクタの引数並びがいろいろなパターンができちゃうとか、 ビルダパターンで書きたいけれど利用側オブジェクトにはそれをやらせたくないときとか。 - :memo: 今手に入れられる情報では、必要な知識が本当に十分か怪しい(しかし進めないわけには行かない)、という時にも、 Factoryなどで一枚噛ませておくと、変更を吸収する余地が残せたり -:memo: ファクトリー挟むことで、オブジェクト生成の実装判断を遅らせられる - :memo: 改めて思い返すと、コンストラクタで受け取る or 内部フィールドのための単純なインスタンス化以上のことをする際は作るかな? - 値オブジェクトのファクトリも言及されているが、値オブジェクトは大体小さい単位でまとまるので、それをファクトリとして別に切り出すモチベーションはあるだろうか - あっても、デフォルトコンストラクタを隠して、createとかそんなメソッドを値オブジェクト内に作るとかになりそう - 値オブジェクトのファクトリメソッドの名前、何にしていますか?(create, from...etc) - [Discord](https://discordapp.com/channels/432531367427964929/740202765619429487/779688198275072021) - :memo: モデリング的なところから出た操作がファクトリメソッドの役目になるだけなので特定の名前って決めてないかな - :memo: 俺は factory は xxxFactory ってつけるのが好き。 Factory って言葉がなんかカッコいいから - 値オブジェクトをenumとして定義することがあると思いますが、定義外のものが来た時はNULLと例外、どちらにしていますか?:+1: - [discord](https://discordapp.com/channels/432531367427964929/740202765619429487/779689432595955723) - 定義外のenum値が来るということですか? (私が知っている言語でこれが簡単にできるのはC言語しか知らないです) - intやStringと紐づく値と紐づけたenumがあり、そのファクトリの引数に定義外のものが来た場合です(正常時:値が合致したenumを生成) - :memo: 例外派が多い - 外部から取得した値をenumの値オブジェクトとして扱いたい+外部のアップデートにより、予期せぬタイミングで新しい値が来ることが予想される場合、以下のどれで対応しますか?①新規の値が来たら例外を吐かせる②外部に依存するのでそもそもenumで定義しない③その他:+1: - 具体的にどんな実装を想定しているか教えてほしいです(静的型付け言語ならコンパイルエラーになるかと) - 例えば外部のエラーコードを自ソフトのenumで管理するとして、新しいエラーコードが来た際に、値オブジェクトのファクトリメソッドで例外となるイメージです。 --- # 放課後 * [はじまり](https://discordapp.com/channels/432531367427964929/740202765619429487/779693237803679744) * [集約の見つけ方](https://discord.com/channels/432531367427964929/740202765619429487/779694402188345374) * [ITエンジニア本大賞2021](https://discord.com/channels/432531367427964929/740202765619429487/779701218028814338) * [データベース設計?](https://discord.com/channels/432531367427964929/740202765619429487/779710558433443861) * [リポジトリと削除処理](https://discord.com/channels/432531367427964929/740202765619429487/779714884312367126) * [啓蒙と、べき論との付き合い方](https://discordapp.com/channels/432531367427964929/740202765619429487/779719943108952074) * [現実世界とドメインモデル](https://discordapp.com/channels/432531367427964929/740202765619429487/779722955306696724) * [エヴァンス本のわかりにくさとは?](https://discordapp.com/channels/432531367427964929/740202765619429487/779724861400416277) --- # ======後半はここから======== # リポジトリ(REPOSITORIES) ## 目安の時間 - 45分 ## 感想・気づき - > すでに知っているオブジェクトから始め、関連しているオブジェクトを要求する。 (...) そうしたリンクこそが、オブジェクトモデルに表現力の多くを与えている。 - p.146 - [discord](https://discord.com/channels/432531367427964929/740202765619429487/784745691595145237) - この「リンク」にこそ、あるモデル、ある実装が「表現力豊か」「ドメインの知識を多く伝えている」という状態の基礎が有るように感じる - さまざまな知識をバラバラに提示しているのではなく、知識と知識の接続(関わっている)、あるいは切断(関わっていない)を、観測者に対して示すことができているか - 図画的には、図と図の間の線によって、こうしたリンク(=接続)が表現される - 実装的には、メソッドの引数やフィールドへのオブジェクト保持で、こうしたリンク(=接続)が表現される - そうしたリンクが一切発生しないようなオブジェクトとはつまり、メソッドの引数にもフィールドにも一切、他のオブジェクトが存在しないようなもの - 例えば引数やフィールドが全部プリミティブとか - ドメインモデル貧血症、getter/setterの話が専らフォーカスされるが、重要なのはむしろ、こうしたリンクの多寡という観点(ドメイン内部の関連が、リンクによって表現されているか)の方に感じる:+1::+1::+1::+1::+1: - getter/setterは単なる具体例であって、中心じゃないのでは - :memo:getter/setterと、リンクの多さ少なさは別軸っぽい? - p.147 「終わりのない関係性のもつれを作り出す〜」 - この辿るべき関連を抑えることが集約のモチベーションなんだと気づいた - フレーム問題を連想した - p.147 「検索と関連を正しく組み合わせることで、設計はわかりやすくなる」 - この考え、なかった気がする:+1: - p.147 「格納したオブジェクトを取り出すことは、実は生成することのサブセットである」は、言われてみれば確かにそうだよね、という。 - p.148 「さらにまずいことに、クライアントコードがデータベースを直接使用していると、開発者は、集約のようなモデルの機能や、オブジェクトのカプセル化さえも迂回し、その代わりに必要なデータを直接取り出して操作したくなってしまう」:+1::+1::+1::+1::+1::+1: - [discord](https://discord.com/channels/432531367427964929/740202765619429487/784742560945799199) - わかる!! Djangoのモデルつかっていると、View、Controllerとかで平気でモデル使ってデータベースアクセスしようとする - p.149 「永続された値オブジェクトを見つけるには、それをカプセル化する集約のルートとして機能するエンティティから関連を辿るのが普通である」 - たぶんそうだと思っていたけど、集約ルートはエンティティだ、ということをここでハッキリ明示しているな、と思った:+1: - 値オブジェクトが集約ルートの場合があると書いてあるのが、意外だった。 - 少なくとも↑の引用付近では「値オブジェクトが集約ルートの場合がある」とは書いてない気がするのですが、どのあたりを見ての感想か気になりました - 「いくつか例外もある」というのは、「プロパティを用いて値オブジェクトを検索するのは〜」にかかっていそうな - p.148 最初の6行くらいはシステムがDB中心に設計させていることへの問題提起なんだなと思った - [discord](https://discord.com/channels/432531367427964929/740202765619429487/784743830510632980) - `どうやってDBからデータを取り出して` と考えたらインフラに依存している状態だなと:+1: - PoEAAのパターンを引用しているな勉強し直さないと:+1::+1: - 中盤の太字の「ドメインロジックがクエリとクライアントコードに移され、…」自分のモヤモヤが表現されていて嬉しい - P.151 リポジトリの利点「アプリケーションとドメインの設計を、永続化技術や複数のデータベース戦略、さらには複数のデータソースからも分離する」:+1::+1::+1::+1::+1::+1: - そう!これ!アプリケーションとドメインの設計とDBは分離すべきということが書かれていてスッキリ - p.148 「オブジェクト」と「ドメインオブジェクト」が注意深く区別されていそうな点が意味深い:+1: - DDDにおいて注目したい、注力したいのは、単なるオブジェクトではなく「ドメインオブジェクト」だよね、という - 「私達が日々作っているのは”オブジェクト”か、それとも”ドメインオブジェクト”か?」という問い、良い自戒というか、チェック項目的になりそう ## 疑問 - p.147でデータベースから取り出した顧客オブジェクトは、エンティティのライフサイクルの中期と言われて、ここで言うライフサイクルは、「ドメインオブジェクトとしてのライフサイクル」なのか、「システム内にあるデータとしてのライフサイクル」なのか、わからなくなってきた:+1::+1: - [discord](https://discord.com/channels/432531367427964929/740202765619429487/784751316957921280) - たしかに既存の顧客の情報を取り出すからエンティティのライフサイクルで言えば中期に見える - では顧客オブジェクトのライフサイクルの終わりとは、顧客データをシステム上(データベース上)から削除したときなのか? - ドメイン上の終わり(退会フラグが立った時など)がそれにあたると思ったが、それは下にある図でいうとアーカイブに当たりそうなので、やはりシステム上(メモリやDBなど)からの削除なのかなぁと思った。「退会した顧客」として再構成できるからなのだろうか。 - システム内のメモリにあるライフサイクルと、ドメインオブジェクトのライフサイクルがいろいろ混ざってきた。 - 第6章のライフサイクルの図を見ていると、これって「システム内にあるデータのライフサイクル」のことを指している気がする:+1: - ![](https://i.imgur.com/HWMSeHF.png) - :memo: これは、システムの (ランタイムのオブジェクト機構の) ライフサイクルですね。 ドメインのセマンティクスでのライフサイクルではない。 上に引用された図がです。 - :memo: 自分もスルーしてましたが、表でのアクティブの状態が中期ってことかもって気がしてきました - :memo: アクティブもデータベース表現も、ドメインオブジェクトとしては概念的には存在していて、メモリ上にあるのがアクティブ、メモリ上にないのがデータベース表現、かな。 - 「データとしてのライフサイクル」という言葉を定義して、「始まり〜中期〜終わり」を考えてもややこしくなるだけなので、ドメインオブジェクトのライフサイクルにだけ意識を尖らせておくだけで良い気はしました。 上の図を見ながら、ドメインオブジェクトのライフサイクルそれぞれが、図のどこにマッピングしてるかだけ意識すれば良いかなと。:+1::+1: - :memo: 「ドメインオブジェクトのライフサイクル」と「データのライフサイクル」は何が違うのでしょう? - :memo: 「オブジェクトがnewされる」≠「ドメインにおいて新しいモノが生まれる」なのだと思います `new Hoge` というのは、既になんらかの形でアーカイブされたものをなんらかの方法で再構成するケースと、本当に新しいまだ存在しないものを生み出すケースと、実は二つの用途で使われている、それを区別しよう分けよう、というのがEvansの言いたいことなのかな、という印象 - コレクション指向型のリポジトリを用意しておくと、DBを意識しなくて済むのは良いが、Controllerとかで呼んだり、知らずのうちにデータベースアクセスを行って密結合していた、みたいなことはないだろうか - [discord](https://discord.com/channels/432531367427964929/740202765619429487/784764420437442561) - DjangoやRuby on RailsのモデルみたいにDBアクセスを隠蔽することによって、ControllerやViewで平気に呼ばれちゃったりするみたいに。 - でもRepositoryのインターフェースをハッキリしておけば、そこは避けられるのかな? - こういう、「知ってないと間違える」みたいなことは、この例に限らず多くありそうですよね。PRで止めれたら良いな。。理解してないもの同士で勝手に混ぜてしまうことがないよう、小さいチームで共通認識持てれば幸せ。 - 言語によってはビルドレベルで依存の方向を制約できることがあるので、こういう仕組みが使えればある程度は避けることができるかなーと思いました:+1: - p.148「一方で、クエリを使用してデータベースから必要なデータだけを引き出したり、集約ルートから進む代わりに数個の特定のオブジェクトを取り出したりすることもあるかもしれない。」とあるが、SQLクエリの検索条件として許されるのは、集約のルートエンティティを取得するための条件のみということだろうか。 - p.149の値オブジェクトに対してグローバルな検索はエンティティより稀である。という内容に、これって本当に? と思っているけど、みなさんどう思うだろうか - p.159 「通常、新しいオブジェクトと既存のオブジェクトを区別することは、ドメインにおいて重要であり、〜」 - [discord](https://discord.com/channels/432531367427964929/740202765619429487/784749095260127232) - これ、どう重要なんだろう?:+1::+1::+1::+1: - ドメインにおいて新規にオブジェクトが生成される時、その状況を導き出したドメインのロジック/文脈があるので、なかったら勝手に作って以前からあったかのように振舞われるのはその意味を失わせたりしてしまうのでは。 - :memo: 無ければデフォルト値 (暗黙の状態) を返すような場合であれば、アクセスした時点で自動生成する、というのはありますね。 オブジェクトのセマンティクスによるので、やっていいか悪いかは状況次第だと思う。 - :memo: 冪等性は応答の性質であって、ドメインでどう区別するかと、応答(プレゼンテーション層の実装)とは関係ないというか、どう実装するかだけの話かなーという気はします - :memo: 「無ければデフォルトを〜」は、個人的にはそれ自体何らかの仕様として記述したい(ので、リポジトリからは別にしたい)派 - :memo: DDD的に言えばリポジトリの責務ではないですね。 # 関係データベースに合わせて設計する ## 目安の時間 - 45分 ## 感想・気づき - 全体的に難しい感じが…。 - p.160 「時にはデータベース設計の方で歩みよらなければならない(選択的な非正規化など)。そうしなければ、モデルと実装の緊密な結びつきが失われてしまう危険があるのだ」 - ここでも結びつきを結構意識をするのだなぁと。 - P.160 「オブジェクトシステムの外部にあるプロセスから…」は集約ルート以外から集約にあるオブジェクトに触るなってことだと理解した - 個人的にはエヴァンスさんのDBに対する考え方がポエミーだけどはっきり書かれていていい章だった:+1::+1: ## 疑問 - p.160 「データベースをオブジェクトの格納先として見る場合には、マッピングツールの能力に関わらず、データモデルとオブジェクトモデルをかけ離れたものにしてはならない。関係モデルと近づけておくため、オブジェクトの関係性が持つ豊かさを若干犠牲にすること」 - [discord](https://discord.com/channels/432531367427964929/740202765619429487/784755490575220747) - これ、妥協しちゃってにしていいの?:+1: - これは「オブジェクトデータベースを使う場合は」って話と理解した - 関係モデル(データベース)とドメインオブジェクトを乖離させるよりは、多少関係モデルの作法に寄せてでもズレをなくしたほうが良いであろう、という事ではないでしょうか。:+1::+1::+1: - :memo: evans本ってヘキサゴナルアーキテクチャが提唱される前で、レイヤーもドメイン層→インフラ層で紹介されているので、ヘキサゴナルとかクリーンアーキテクチャみたいにドメイン層がインフラ層のこと全く知らないで良い、というアーキテクチャで考えればオブジェクトとデータベースの関係を考えなくてもよくなる気がします - :memo: ドメイン層がインフラ層を知らなくてはいけないって話しとかではなくて、DB使う際は、オブジェクトの構造をDB側でそのまま表現しなくても良いよ、正規化とかもしようね。 - p.160 「一方で、データが、オブジェクトの格納先として全く想定されていなかった、レガシーや外部のシステムから来ているという場合も多い。こういう状況では、実は、同一のシステムに2つのドメインモデルが共存しているのだ」 - 2つというのは、自システム内のドメインモデルと、外部システムからのドメインモデルという意味? - そうだと思います。「自システムのドメインモデルとレガシーのドメインモデル」、または「自システムのドメインモデルと外部システムのドメインモデル」:+1: - 外部システムから出てくる貰ってくるデータを取得して、自分のシステム内のドメインモデルに変換しようとするのは、Repositoryの責務なんだろうか - [discord](https://discord.com/channels/432531367427964929/740202765619429487/784758764267372574) - 変換をすると考えると、Repositoryなの? という疑問がある - 変換の責務を担う他のモデルを用意して委譲する(再構成をファクトリに任せるように)というのはアリかもしれません。:+1::+1: - :memo: Repository使わない。 - :memo: そこらへん4部にあった気がする Adapter とかそこら辺の話 - :memo: 外部コンテクストとの変換などのやりとらはadapterとtranslatorの責務な気がする - :memo: 外部連携システムと内部サブシステムは、自分たちで制御できるかいなかが関わるからアーキテクチャ的には区別したいので、同じくリポジトリにしないかなー - :memo: Repositoryは自分で制御できるもの? - :memo: 自分は腐敗防止はシステム的に分かれてるときに言ってるイメージですね - :memo: 外部システムから連携してもらってくるってのは、現時点での状態を聞くだけで、ライフサイクルには関わらなさそうだなと思いました - DBとのマッピングには何を使ってますか?:+1: - [discord](https://discord.com/channels/432531367427964929/740202765619429487/784760320287178812) - 「DBから取り出したものをDTOに詰め、集約に変換」をリポジトリの実装側でしていますが、みなさんどんな感じで行っていますか? - コンテキストが多くなるとドメインモデルの数が増えるので、DBもその分増やす必要がありそうですよね。AWSでDBを立てている際、コンテキストの数だけクラスターを作成していますか?スキーマを増やして対応してる等。 - みなさん、リポジトリのインターフェースってどこに置きますか?:+1::+1: - よかったらその理由も教えてほしいです - :memo: P.157 フレームワーク内で作業する 『フレームワークとは争わないこと』 - :memo: 今までドメインモデルをあれだけ強調してたのに、フレームワークとDBには弱腰 - :memo: フレームワークの流儀にそぐわないことをやると、アーキテクチャレベルの歪みが生じてしまうので避けましょう。なので、ドメインはフレームワーク独立に保ちましょう。 - :memo: 俺の記憶によるとMVC2の時代だったので実質ろくなMVC派生のフレームワークはありませんでした - :memo: cakePHP ですら 2006 年

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully