社内でドメイン駆動設計入門の読書会 #12

2020/05/18 2020/05/18 #PHP #勉強会 #ドメイン駆動設計 #読書会

社内でドメイン駆動設計入門の読書会 #12

こんにちは。Nonです。

今回も会社で読書会をしている話をしようと思います。

内容は控えめに、ディスカッションの内容重視で書いていきたいと思います。

より具体的なコードや内容がみたい!という方は購入しましょう!

前回:

読んでいる本

読んでいる本はこちらのドメイン駆動設計です。

ogp
https://www.amazon.co.jp/%E3%83%89%E3%83%A1%E3%82%A4%E3%83%B3%E9%A7%86%E5%8B%95%E8%A8%AD%E8%A8%88%E5%85%A5%E9%96%80-%E3%83%9C%E3%83%88%E3%83%A0%E3%82%A2%E3%83%83%E3%83%97%E3%81%A7%E3%82%8F%E3%81%8B%E3%82%8B-%E3%83%89%E3%83%A1%E3%82%A4%E3%83%B3%E9%A7%86%E5%8B%95%E8%A8%AD%E8%A8%88%E3%81%AE%E5%9F%BA%E6%9C%AC-%E6%88%90%E7%80%AC-%E5%85%81%E5%AE%A3/dp/479815072X/ref=sr_1_3?adgrpid=80422044687&gclid=Cj0KCQiA4sjyBRC5ARIsAEHsELH1_WgS89EWpNAswUpFQO3uQOdoReiYJIMYrbeAsqBIxpk-bGG0c6saAnI4EALw_wcB&hvadid=374679990844&hvdev=c&hvlocphy=1009547&hvnetw=g&hvqmt=e&hvrand=11783283473722260575&hvtargid=aud-759242200046%3Akwd-857025096386&hydadcr=1789_10144705&jp-ad-ap=0&keywords=%E3%83%89%E3%83%A1%E3%82%A4%E3%83%B3%E9%A7%86%E5%8B%95%E8%A8%AD%E8%A8%88%E5%85%A5%E9%96%80&qid=1582467293&sr=8-3

https://www.amazon.co.jp/%E3%83%89%E3%83%A1%E3%82%A4%E3%83%B3%E9%A7%86%E5%8B%95%E8%A8%AD%E8%A8%88%E5%85%A5%E9%96%80-%E3%83%9C%E3%83%88%E3%83%A0%E3%82%A2%E3%83%83%E3%83%97%E3%81%A7%E3%82%8F%E3%81%8B%E3%82%8B-%E3%83%89%E3%83%A1%E3%82%A4%E3%83%B3%E9%A7%86%E5%8B%95%E8%A8%AD%E8%A8%88%E3%81%AE%E5%9F%BA%E6%9C%AC-%E6%88%90%E7%80%AC-%E5%85%81%E5%AE%A3/dp/479815072X/ref=sr_1_3?adgrpid=80422044687&gclid=Cj0KCQiA4sjyBRC5ARIsAEHsELH1_WgS89EWpNAswUpFQO3uQOdoReiYJIMYrbeAsqBIxpk-bGG0c6saAnI4EALw_wcB&hvadid=374679990844&hvdev=c&hvlocphy=1009547&hvnetw=g&hvqmt=e&hvrand=11783283473722260575&hvtargid=aud-759242200046%3Akwd-857025096386&hydadcr=1789_10144705&jp-ad-ap=0&keywords=%E3%83%89%E3%83%A1%E3%82%A4%E3%83%B3%E9%A7%86%E5%8B%95%E8%A8%AD%E8%A8%88%E5%85%A5%E9%96%80&qid=1582467293&sr=8-3を見る

ogp
https://www.amazon.co.jp/%E3%83%89%E3%83%A1%E3%82%A4%E3%83%B3%E9%A7%86%E5%8B%95%E8%A8%AD%E8%A8%88%E5%85%A5%E9%96%80-%E3%83%9C%E3%83%88%E3%83%A0%E3%82%A2%E3%83%83%E3%83%97%E3%81%A7%E3%82%8F%E3%81%8B%E3%82%8B-%E3%83%89%E3%83%A1%E3%82%A4%E3%83%B3%E9%A7%86%E5%8B%95%E8%A8%AD%E8%A8%88%E3%81%AE%E5%9F%BA%E6%9C%AC-%E6%88%90%E7%80%AC-%E5%85%81%E5%AE%A3/dp/479815072X/ref=sr_1_3?adgrpid=80422044687&gclid=Cj0KCQiA4sjyBRC5ARIsAEHsELH1_WgS89EWpNAswUpFQO3uQOdoReiYJIMYrbeAsqBIxpk-bGG0c6saAnI4EALw_wcB&hvadid=374679990844&hvdev=c&hvlocphy=1009547&hvnetw=g&hvqmt=e&hvrand=11783283473722260575&hvtargid=aud-759242200046%3Akwd-857025096386&hydadcr=1789_10144705&jp-ad-ap=0&keywords=%E3%83%89%E3%83%A1%E3%82%A4%E3%83%B3%E9%A7%86%E5%8B%95%E8%A8%AD%E8%A8%88%E5%85%A5%E9%96%80&qid=1582467293&sr=8-3

https://www.amazon.co.jp/%E3%83%89%E3%83%A1%E3%82%A4%E3%83%B3%E9%A7%86%E5%8B%95%E8%A8%AD%E8%A8%88%E5%85%A5%E9%96%80-%E3%83%9C%E3%83%88%E3%83%A0%E3%82%A2%E3%83%83%E3%83%97%E3%81%A7%E3%82%8F%E3%81%8B%E3%82%8B-%E3%83%89%E3%83%A1%E3%82%A4%E3%83%B3%E9%A7%86%E5%8B%95%E8%A8%AD%E8%A8%88%E3%81%AE%E5%9F%BA%E6%9C%AC-%E6%88%90%E7%80%AC-%E5%85%81%E5%AE%A3/dp/479815072X/ref=sr_1_3?adgrpid=80422044687&gclid=Cj0KCQiA4sjyBRC5ARIsAEHsELH1_WgS89EWpNAswUpFQO3uQOdoReiYJIMYrbeAsqBIxpk-bGG0c6saAnI4EALw_wcB&hvadid=374679990844&hvdev=c&hvlocphy=1009547&hvnetw=g&hvqmt=e&hvrand=11783283473722260575&hvtargid=aud-759242200046%3Akwd-857025096386&hydadcr=1789_10144705&jp-ad-ap=0&keywords=%E3%83%89%E3%83%A1%E3%82%A4%E3%83%B3%E9%A7%86%E5%8B%95%E8%A8%AD%E8%A8%88%E5%85%A5%E9%96%80&qid=1582467293&sr=8-3を見る


でも言っていたように、業務でDDDを利用して開発することが多くなったのですが、DDDに精通している人が少ないという問題がありました。

そこで、その精通している人が読書会をしようかと誘ってくださいまして、是非にと参加させていただきました。

進行方法

読書会の進行方法は

  1. 今回読書の対象にする章を決める。
  2. 10〜20分間その章を読む
    1. 読み終わってしまった人は、もう一周読み直すか、次の章に進んでもらう
  3. その後40分間で、その章に対する疑問や考え方をディスカッションする
  4. 1〜3を毎週定期的に行う

という進行方向となっています。

社内で読書会をするのはこれが初めてなので、進行方法はもっといいのあったら教えて下さい。

今回読んだ内容

  • ドメインのルールを守る「集約」
  • 集約とは
    • 集約の基本的構造
    • オブジェクトの操作に関する基本的な原則
    • 内部データを隠蔽するために
  • 集約をどう区切るか
    • IDによるコンポジション
  • 集約の大きさと操作の単位
  • 言葉との齟齬を消す
  • まとめ

ディスカッション

今回はより現実的な話

今までに挙がった例はどれも単体での動作のみに注目してきましたが、現実のドメインは様々なEntityが複雑に関連しています。

そうなるとEntityは肥大化していきますし、振る舞いも相当なものになるでしょう。

そういうときに、どのようにまとめるべきなのか。ということを話題にしていました。

正直まだよくわからない。

最初から申し訳ないですが、集約難しいです。
現実の関心事を論理的に切り分けるのは一苦労です。
なので、この本にかかれている内容を一部抜粋して、私の考えを記していきたいと思います。

ユーザーとサークルの関係

ユーザーはサービスを利用するにあたって、必須な情報でしょう。

  • ログインするため
  • アプリを利用するため
  • ログを残すため

色々なことにユーザー情報が利用されることでしょう。

対して、サークルも同様です。

  • アプリをより楽しく利用するため
  • サークル内でやり取りすることがアプリの真髄

など、サークル作ることは必須ではありませんが、サークル作らないことはこのアプリにとって利用していないのとほとんど変わらない場合を考えます。

するとユーザー⇔サークルは密接関係を持つことがわかります。

サークルとユーザーのドメインは?

ユーザー作成にサークルが関係無いことはわかります。

サークル作成にはユーザーが必須に感じますね。

class UserService
{
    private $circleFactory;

    public function __construct(CircleFactoryInterface $circleFactory)
    {
        $this->circleFactory = $circleFactory;
    }

    public function createCircle(CircleName $circleName): Circle
    {
        return $this->circleFactory->create($circleName);
    }
}
class Circle
{
    private $id;
    private $circleName;
    private $members;

    public function __construct(int $id, CircleName $circleName, MemberCollection $members)
    {
        $this->id = $id;
        $this->circleName = $circleName;
        $this->members = $members;
    }

    // ...(略)
}

class CircleName
{
    private $circleName;

    public function __construct(string $circleName)
    {
        if (mb_strlen($circleName) > 50) {
            throw new OverflowException('サークル名は50文字以内です。');
        }
        $this->circleName = $circleName;
    }

    public function __toString(): string
    {
        return (string)$this->circleName;
    }
}

class MemberCollection
{
    private $members;

    public function __construct(array $members = [])
    {
        foreach ($members as $member) {
            if ($member instanceof User) {
                throw new RuntimeException('Member is must be User type.');
            }
        }
        $this->members = $members;
    }

    public function toArray(): array
    {
        $result = [];
        foreach ($this->members as $member) {
            $result[] = [
                'id' => $member->id(),
                'name' => $member->name(),
            ];
        }
        return $result;
    }
}

のような感じでしょうか。ユーザーサービスはサークルEntityを作成するふるまいを持ちます。

このとき、注目すべきEntityがユーザーEntityとサークルEntityで2つ出てきます。

ユーザーEntityはユーザーの名前を変更する振る舞いを持ちますが、今回の注目ごとはサークルの作成です。果たしてこの振る舞いがこのユースケースと関係あるでしょうか?

このふるまいがあるせいで、関心事が散漫とならないでしょうか

また、このコードを是とすると、changeName以外のふるまいがユーザーEntiryに集中することになるので、サークル作成ドメインに違う関心事が書かれたコードがどんどんと追加されることになっていまいます。

ドメインごとにUserEntityを作成するべき?

もしかしたら境界づけられたコンテキストの方の分野かもしれませんが、ドメインごとにEntityを分ける戦略が考えられます。

src┓
   ┣User┓
   ┃    ┣CreateUser
   ┃    ┣User
   ┃    ┣UserName
   ...
   ┣Circle┓
   ┃      ┣CreateCircle
   ┃      ┣Circle
   ┃      ┣CircleName
   ┃      ┣User
   ┃      ┣UserService
   ...
   ┣Domain
   ...

うーん。でもCircleに関心事があるドメインにUserServiceがあるのが少し違和感。本では容赦なく使用していましたが、Circleを作成するにはUserにも関心が向くという知識がドメインに与えられるのでいいことなのかもしれない

EntityはEntityを持つことがある?

class MemberCollection
{
    private $members;

    public function __construct(array $members = [])
    {
        foreach ($members as $member) {
            if ($member instanceof User) {
                throw new RuntimeException('Member is must be User type.');
            }
        }
        $this->members = $members;
    }

    public function toArray(): array
    {
        $result = [];
        foreach ($this->members as $member) {
            $result[] = [
                'id' => $member->id(),
                'name' => $member->name(),
            ];
        }
        return $result;
    }
}

に書いたように、CircleはMemberとしてUserを持ちます。

if ($member instanceof User) {
    throw new RuntimeException('Member is must be User type.');
}

の部分。

なので、CreateCircleというドメインにEntityが2つあるのは是とするのが、普通の考えなのかもしれません。

Circleに所属するのはUserなので、Userの知識を持つUserをCircleが持つというのは自然な考えだと思います。

この辺はもっとドメイン駆動設計に慣れたらまとめ直したい。

正直、腑に落ちていない点が少しあります。この状態でかくあるべきというのは危険ですので、これ以上の展開は避けておきますね。

最後に

ちょっと難しい話でした。それぞれのEntity同士の関係になると一気に難しくなった気がします。ここを理解(納得)できないと、実装も揺らぎそうで怖いです。

次回は複雑な嬢他県を表現する「仕様」です。
また記事にしますので、その時はよしなに。

.

のん

所属 : 株式会社スマレジ 開発部

YouTube : のんラボ

Twitter : @nonz250

Github : nonz250

Qiita : @nonz250

My Qiita posts My Qiita contributions My Qiita followers

主にPHPを使用し、サーバーサイドを担当。最近はフロントにも興味津々。

なにかを作ったりいじったりするのが好きで、個人開発なども行っている。

趣味はバイクアイコン画像は大抵愛車の「Z250」である。友達にアイコン描いてもらえて嬉しい。

PHP / Laravel / CakePHP2 / CakePHP3 / Vue / Nuxt / C# / etc...

Tags

#のんラボ #Laravel #Vue #個人開発 #ブログ #プログラミング #javascript #Html5 #WEBサービス #Twitter #今年の抱負メーカー #勉強方法 #PWA #モバイルアプリ #Android #ツーリング #バイクに乗るエンジニア #Z250 #秋吉台 #能登半島 #バイク #冒険 #東尋坊 #Squid #リバースプロキシ #hosts #axios #cropper #AdSense #Bootstrap #MySQL #高速化 #トドTask #Telescope #デバッグ #composer #テスト #セキュリティ #POSレジ #スマレジ #本部機能 #バリデーション #入力チェック #Mac #Chrome #テスト駆動開発 #開発手法 #UI #デザイン #WEBサイト #機能美 #PHP #Laravel 6 #コメント #バージョンアップ #vue-cli #localhost #BIツール #売上分析 #TANAX #MFK250 #ツアーシェルケース2 #RESTful #API #REST API #実務的 #PHP Tech Tutor #Smaregi Tech Talk #勉強会 # ブログ #CakePHP3 #CSRF #VSCode #開発環境 #CakePHP3.0 #さくらのレンタルサーバー #モジュールモード #シェル #メール #Gmail #relay #OGP #エラーページ #抱負 #家庭教師 #ドメイン駆動設計 #DDD #読書会 #那智の滝 #伊勢志摩 #伊勢志摩スカイライン #フロント #三方五湖 #レインボーライン #ボーイスカウト・ルール #プログラマが知るべき97のこと #リファクタリング #ユビキタス言語 #車輪の再発明 #マイクロサービス #デプロイ #QA #laravel-mix #Tips #storybook #@storybook/addon-actions #昇降デスク #コードレス #書斎 #オフィス #リモートワーク #働き方 #エラーハンドリング #スマレジ4 #pixel 5 #レビュー #スマレコ #TDD #RSS #404 #高山ダム #ラーツー #React #Nuxt #node_modules #エラー #インポート #設定方法 #環境構築 #Docker #フォレストパーク神野山 #学生向け #PR #採用 #Node.js #npm #しまなみ海道 #youtube #CSS #IE #SLA #Rust