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

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

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

こんにちは。Nonです。

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

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

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

前回:会社でドメイン駆動設計入門の読書会 #4

読んでいる本

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

51E+hPktiQL._SX350_BO1,204,203,200_.jpg

ドメイン駆動設計入門 ボトムアップでわかる! ドメイン駆動設計の基本


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

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

進行方法

読書会の進行方法は

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

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

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

今回読んだ内容

第4章の不自然さを解決する「ドメインサービス」です。

  • データにまつわる処理を分離する「リポジトリ」
  • リポジトリとは
  • リポジトリの責務
  • リポジトリのインターフェース
  • SQLを利用したリポジトリを作成する
  • テストによる確認
    • テストに必要な作業を確認する
    • 祈り信者のテスト理論
    • 祈りを捨てよう
  • テスト用のリポジトリを作成する
  • オブジェクトリレーショナルマッパーを用いたリポジトリを作成する
  • リポジトリに定義されるふるまい
    • 永続化に関するふるまい
    • 再構築に関するふるまい
  • まとめ

私はWEBでの管理プログラムや、WindowsFormを用いてアプリ作成などしかプログラムを作成した経験がないので、MySQLなどしかデータストアとして使ってきませんでした。

この章を読んで色々なものがデータストアとして見ることができることに気づきました。

ディスカッション

リポジトリとは?

経験の長い方が、読書会に入ってくださっているので、簡単に説明してくれました。

DDDの読書会をしているときに申し訳がないが、そもそもドメイン駆動設計の論点とは、設計全体ではなく、例えばMVCのM(モデル)の設計手法について語っているように感じます。
なので、ここでいうリポジトリはこれまでと違って、 「 データ」 をメインに扱うことになるので、コレまでやってきた内容とは一線を画すように感じます。

前回までは、できるだけ「データ」に注目することなく進んできましたが、データストアを扱う章なので、今回ばかりはドメインではなくデータに注目することになりそうです。

第1章でもドメインオブジェクトは知識をプログラムで表現するとありました。

リポジトリとはドメインオブジェクトではなく、ドメインオブジェクトに侵食しそうな プログラムを受け止め防衛するものかもしれません。

COLUMN: リポジトリはドメインオブジェクトを際立たせるでも書かれていることですね

ユーザーみたいに簡単なエンティティの永続化の例はわかるけど、複雑なリレーションが組まれたものに対してはどのように実装すべき?

これは経験の浅い方からの質問でしたが、私も同じことを思いました。

その日の読書会メンバーの中には答えを持っている人はいませんでしたので、私達なりに一つの結論を出しました。

中間テーブルにはリポジトリを作らず、臨機応変に対応すること。

例:ブログ記事とタグの関係

例えば、記事のみを扱うリポジトリを考えてみましょう。

$articleEntity = new ArticleEntity();
$articleRepository = new ArticleRepository();

$articleRepository->save($articleEntity);

class ArticleRepository
{
    public function save(ArticleEntity $article)
    {
        // 記事の永続化処理
    }
}

他にもメソッドを持っているかと思いますが、とりあえず永続化のsaveを持ったArticleRepositoryを作成しました。

記事を作成したときに、このリポジトリのsaveを呼んで永続化します。

しかし、記事登録のユースケースはそれだけでしょうか?例えば、記事には1つ以上のタグが紐づくものとしましょう。

$articleEntity = new ArticleEntity();
$articleRepository = new ArticleRepository();

$articleRepository->save($articleEntity);

class ArticleRepository
{
    public function save(ArticleEntity $article)
    {
        // 記事の永続化処理
        if ($article->hasTags()) {
            // タグの永続化処理
        }
    }
}

これではなにか不自然に感じてしまいます。ArticleRepositoryがタグの永続化処理を持っているのです。

ということは、$articleRepository->save($articleEntity);を呼んだ後、この処理をすべきでは無いでしょうか?

$articleEntity = new ArticleEntity();
$tagEntity = new TagEntity();

$articleRepository = new ArticleRepository();
$tagRepository = new TagRepository();

$articleRepository->save($articleEntity);
if ($article->hasTags()) {
    $tagRepository->save($tagEntity);
}

class ArticleRepository
{
    public function save(ArticleEntity $article)
    {
        // 記事の永続化処理
    }
}

class TagRepository
{
    public function save(TagEntity $article)
    {
        // タグの永続化処理
    }
}

記事とタグの紐付けは?

これについても出てきましたが、前章エンティティでのディスカッションの通り、記事がタグを持っているかどうかをエンティティで表現すれば可能になるかもしれません。

class ArticleEntity
{
    public $tagIds;

    public function __construct()
    {
        // 省略
    }

    public function hasTags(): bool
    {
        return count($this->tagIds) > 0;
    }

    public function getTags(): array
    {
        return $this->tagIds;
    }
}

やっぱりふるまいは持つべきではない。

弊社はPOSレジを扱う会社ですので、色々な集計関数をSQLを用いて取得することでより高速にレジを打てるように努力しています。

しかし、集計関数を利用すると、SQLにロジックを書くことになりそうです。その時リポジトリはこのようになってしまうかもしれません。

class TransactionRepository
{
    public function sum(): int
    {
        // 集計関数などを用いたSQL
    }
}

この著書ではExistsが例に挙げられていましたが、このsumも同様な気がします。

よほど困った理由がなければ上記のようなコードをRepository書かずにEntityに書くべきでしょう。

class TransactionEntity
{
    public function sum(): int
    {
        // 集計処理
    }
}

最後に

リポジトリについては、データを扱うものからドメインオブジェクトを守るという考えが最初に理解できたので、簡単に理解できたと思います。

データストアに保存する処理や、データストアから取得する処理をできるだけ他のロジックから切り離して考えるというのは、今の知識でも理解できていますので。

リポジトリに何を書くかではなく、リポジトリをどのように扱う(呼び出す)かというのが大事になってくるのかもしれません。

次回は待ちに待ったアプリケーションサービスですね。

その時はよしなに。

.

Non

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

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

#non's Labo #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