Upgrade to Pro — share decks privately, control downloads, hide ads and more …

ドメイン駆動設計入門

uhzz
January 26, 2021

 ドメイン駆動設計入門

社内勉強会で使用したドメイン駆動設計のスライドです

uhzz

January 26, 2021
Tweet

More Decks by uhzz

Other Decks in Technology

Transcript

  1. Wikipedia contributors. "ダニング=クルーガー効果 ." Wikipedia. Wikipedia, 11 Oct. 2020. Web.

    12 Nov. 2020. 1年を通して、コードや書籍を通して 得た知見をお話しします
  2. type fullname struct { firstName string lastName string } type

    fullname struct { firstName name lastName name } どっちも一緒? firstName, lastNameはstring型だね! えーと、、name型です(そんな型なくね?)
  3. type fullname struct { firstName name lastName name } 値オブジェクト

    値オブジェクトではname型を定義します! 値オブジェクトとは、 「システム固有の値を表現するためのドメインオブジェクト」
  4. type name string //name型を定義してます func newName(value string) (name, error) {

    // 文字列が「hoge」であるか、初期化のタイミングでチェックできる! if value != "hoge" { return "", errors.New("value is not hoge") } return name(value), nil } 固有の型を定義するメリットは? 固有の型を定義することで、システムで不要な値を 初期化するタイミングで削ぎ落とせる!
  5. type name string //name型を定義してます func newName(value string) (name, error) {

    // 文字列が0文字以上か // 文字列が20字以下か // 文字列に英数字が含まれていないか return name(value), nil } つまり? ソースコードがドキュメントになる! →自己文書化を推進する
  6. そもそも「値」とは? - 1, 2, 3, …  → 数字 - A, B,

    C, … → 文字 - りんご, みかん, ぶどう →文字列 これらには、共通する性質が3つあります。 - 不変 - 交換が可能 - 等価性によって比較される 値オブジェクトは 「システム固有の”値”」なので、 この3つの性質が適用されます。
  7. 交換が可能って? 直感的に、普段以下のような「代入」を書いてることでしょう var hoge string = "こんにちは" hoge = "今何時?"

    何気なく使っている「代入」ですが、無意識にも 「変数に格納された値を変更(交換)」しているのです(驚) ※「こんにちは」という文字列を「今何時?」という文字列に 上書きしているのではなく、それぞれ格納されている文字列を 交換している ←この感覚をまんま値オブジェクトでも適用します
  8. つまり、、? 値オブジェクト、「不変」かつ「交換可能」を適用しないと、、 var hogeUser user = user{id: 0, name: "hoge"}

    hogeUser.name = "fuga" 確かにこのコードは正常に動作しますし、なんならよく見慣れた方もいるはず ※便宜上、idとnameをint/string型にしています ですが、残念ながらこのコードは、 「値は不変」というルールを適用してないので 「値オブジェクト」ではありません。 →やりがちなパターンです。。
  9. つまり、、? 値オブジェクト、「不変」かつ「交換可能」を適用すると、、 var hogeUser user = user{id: 0, name: "hoge"}

    hogeUser = user{id: 1, name: "fuga"} 新しいオブジェクトを代入していますね! 「値」と同じように、「不変」のオブジェクトを「交換」 しているので、これは値オブジェクトです!
  10. メリットある? var hogeUser user = user{id: 0, name: "hoge"} //

    途方もない処理(hogeUserの中身を変えたりもしてる) AllowUser(hogeUser.id, hogeUser.name) 仮に、userのパラメータにmailAddressなんかがあると、、 →ユーザの個人情報が別のユーザに送信されるといったバグになりかねない。。 つまり、状態を変更できないようにするというのは、 それだけでバグを起こしにくいようにすることができる!
  11. type user struct { id id fullname fullname } エンティティ

    値オブジェクトと同じように、 固有のid型、fullname型を定義するよ! エンティティとは、 「属性でなく同一性によって識別されるドメインオブジェクト」
  12. 値オブジェクトと何が違うの? type fullname struct { firstName name lastName name }

    type user struct { id id fullname fullname } 値オブジェクトは、firstName, lastNameが 同じであれば、まったく同じオブジェクト →属性によって識別される エンティティは、fullnameが同じでも、 まったく同じオブジェクトとは限らない →属性によって識別されない
  13. 不自然な振る舞い? var hogeUser user = user{id: 0, name: "hoge"} //

    ユーザーの重複チェック var isExist, _ := hogeUser.IsExists(hogeUser) hogeUserがhogeUserの存在チェックをする? ※便宜上、idとnameをint/string型にしています 一見すると普通のコードに見えますが、コードから読み取れるのは 「自身と同じオブジェクトが存在するかを自身に問い合わせて 確認している」 という情報のみで混乱を招く恐れがあります
  14. ドメインサービスを活用すると var userService := NewUserService() var hogeUser user = user{id:

    0, name: "hoge"} // ユーザーの重複チェック var isExist, _ := userService.IsExists(hogeUser) ここから分かることは、ドメインサービス(userService)というロジックを通して エンティティの存在チェックを行っているということです! つまり、ドメインサービスとは 「エンティティ(値オブジェクト)を利用して、 ビジネスルールを記述するオブジェクト」 と言うことができます。
  15. クリーンアーキテクチャでは ドメイン層 インフラ ストラクチャ層 ドメイン層はデータアクセスインターフェースに依存す るようになる ↓ こうする事でインフラストラクチャ層へ変更があったと してもドメイン層への影響はない 何が嬉しいかと言うと、ドメイン層がデータを

    保存する場所(RDSかDynamoDBかRedisか)といっ たインフラ部分を気にする事がなくなる これを「依存性逆転の原則」と呼びます。 データアクセス インターフェース アプリケーション層 ユーザー インターフェース層