Laravel で軽量DDDを模索してみた

Laravelで軽量DDDを模索

参画したプロジェクト仕様がわからない
仕様書が十分にない、どうすれば??

という悩みを抱えた経験はございませんか?

これらを解決するために、軽量DDDを模索していきたいと思います

目次

コードを仕様書にする

まず最新状況の仕様書をつくるのは非常に困難です。

度重なる仕様変更で、過去に作った仕様書は参考にならなかったり、機能説明が不十分だったりします。

  • ディレクターもクライアントの仕様書は用意するが、エンジニアのために仕様書を作る余裕はない
  • 技術やコード構造まで把握している営業・ディレクターは少ない
  • エンジニアも日々の開発で手一杯なので、質の高い仕様書を作れません。
  • READMEを仕様書にしたり、Wikiを充実させるのも一つですが、これらの記事を書くことに協力的な人も少ない
  • わかりやすい文章を書ける人も少ない

これらの理由から、エンジニアの仕様書を作るのは労力的に不可です。
できても誰か一人が極端に労力を注ぐことになるでしょう。
ER図などは作成できますが、すべての機能に対して詳しい仕様書をつくるのはまず難しい。

なら、仕様理解できるコードを書こう


エンジニアらしく、日々向き合うコード自体を仕様書にしてしまえばいい

こういった発想に着地します。

そのために仕様がわかるような、さまざまなアーキテクチャが誕生しました。

そのひとつがDDDです。

機能の依存関係がわかるディレクトリ構造

DDDの魅力は機能ごとにディレクトリをまとめる特徴があります。

MVCの課題

通常フレームワークは、フレームワークの機能ごとにディレクトリが分かれています。

MVCはモデル、コントローラー、ミドルウェアといった、フレームワークの機能ごとにディレクトリがあり、
その種類ごとにまとめてファイルを管理します。

フレームワークのディレクトリ構造も便利ですが、開発している機能の仕様がわかりやすいか?というと話は別です。

最初はファイル数が少ないので困りませんが、例えばServese層

ModelやMiddleware、interfaceごとにフォルダ分けをし、種類ごとにファイルを管理すると、それぞれの依存関係までは見えない課題があります。

つまり、すべてのファイル開き、コードの詳細まで読み込む必要があるのです。

MVCは序盤早く開発できるが、仕様が複雑になるとコードが理解しづらく、後半開発が遅くなる

例えば、

  • 担当者が別のプロジェクトに配属されたり、会社を退職して、仕様を把握している人がいない。
  • コントローラーにメソッドが詰め込まれており、どのメソッドがどれと関係しているのかわかりづらい。
  • このメソッドを編集すると、どこまで影響がでるかわからない。変更するのは怖い。確認に時間がかかる。

DDDの課題

  • DDDの知識や理解のあるエンジニアばかりではない。
  • 未経験者や育成段階のエンジニアにいきなり参画させると対応できない。
  • 完璧にDDDを取り入れるとフレームワークの良さを削ってしまう。
  • DDDの複雑なディレクトリ構成で逆に仕様がわからない。

これらのことから、フレームワークの良さを残しつつ、一部DDDを採用する軽量DDDを取り入れることにした。

機能ごとにファイルを管理する

DDDはFrameworkと喧嘩してはいけない。

また、完璧なDDDを取り入れすぎるとチームによっては混乱が生まれる。
DDDのドメイン層の概念のみを取り入れることにした。

Laravelで行うDDDのディレクトリ構成

DDDの層ごとに分けるパターン

/app
├── /Presentation
|  ├── /Controller
|  |   ├── /Product
|  ├── /Request
|  |   ├── /Product
|  ├── /ViewModel
|  |   ├── /Product
|  ├── /Resource
|  |   ├── /Product
|  └── /Middleware
|  
├── /Application
|  └── /ApplicationService
|      └── /Product
|
├── /Domain
|  ├── /Product
|      ├── DomainService
|      ├── DomainModel
|      ├── ValueObject
|
├── /Infrastructure
|  ├── /Product
|      ├── /Repositories

機能ごとに分けるパターン

シンプルに機能ごとにディレクトリをまとめています。

/app
├── /Packages
|  ├── /Product
|  |   ├── /Presentation
|  |   |   ├── /Controller
|  |   |       └── ProductController.php
|  |   ├── /Application
|  |   |   ├── Product.php
|  |   |   └── Services // Serviceはパッケージ化
|  |   |
|  |   ├── /Domain
|  |   |   ├── Services
|  |   |   |   ├── Concrete // 実装クラス
|  |   |   |   |   └── ProductPublicDomainServiceImpl.php
|  |   |   |   └── ProductPublicDomainService.php
|  |   |   ├── Entity
|  |   |   |  └── Product.php
|  |   |   └── ValueObject // EnumやValueObject
|  |   |      ├── ProductStatus.php // 公開ステータスなど。Enumでも可
|  |   |      └── ProductPrice.php  // 手数料処理など
|  |   |
|  |   ├── /Infrastructure
|  |   |   └──Repositories
/app
├── /Packages
|  ├── /Product
|  |   |
|  |   ├── /Presentation
|  |   |   ├── /Controller
|  |   |   |   └── ProductController.php
|  |   |   ├── /Listeners
|  |   |   ├── /Middleware
|  |   |   └── /Jobs
|  |   |
|  |   |
|  |   ├── /Application
|  |   |   ├── Product.php
|  |   |   └── Services // Serviceはパッケージ化
|  |   |       ├── Concrete // 実装クラス
|  |   |       |   ├── ProductServiceImpl.php
|  |   |       |   ├── ProductCreateServiceImpl.php
|  |   |       |   └── ProductUpdateServiceImpl.php
|  |   |       ├── ProductService.php
|  |   |       ├── ProductCreateService.php
|  |   |       └── ProductUpdateService.php
|  |   |
|  |   |
|  |   ├── /Domain
|  |   |   ├── Services
|  |   |   |   ├── Concrete // 実装クラス
|  |   |   |   |   └── ProductPublicDomainServiceImpl.php
|  |   |   |   └── ProductPublicDomainService.php
|  |   |   ├── Entity
|  |   |   |  └── Product.php
|  |   |   └── ValueObject // EnumやValueObject
|  |   |      ├── ProductNumber.php // uuidの制御
|  |   |      ├── ProductStatus.php // 公開ステータスなど。Enumでも可
|  |   |      └── ProductPrice.php  // 手数料処理など
|  |   |
|  |   |
|  |   ├── /Infrastructure
|  |   |   └──Repositories
|  |   |       ├── Concrete // 実装クラス
|  |   |       |   └── ProductRepositoryImpl.php
|  |   |       └── ProductRepository.php

コントローラー生成時のコマンド

php artisan make:controller ../../Packages/Product/Presentation/Controller/ProductController

ミドルウェア生成時のコマンド

php artisan make:middleware ../../Packages/Product/Presentation/Middleware/ProductCheck 

モデル生成時

php artisan make:model ../Packages/Product/Application/Product

Jobの作成

php artisan make:job ../Packages/Notice/Infrastructure/Notifications/NoticeSendJob

参考文献

LaravelでDDD構成を採用されているリポジトリがあったので、こちらを共有します。

ぎゅう
WEBエンジニア
渋谷でWEBエンジニアとして働く。
LaravelとVue.jsをよく取り扱い、誰でも仕様が伝わるコードを書くことを得意とする。
先輩だろうがプルリクにコメントをして、リファクタしまくる仕様伝わるコード書くマン
よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

コメント

コメントする

目次
閉じる