参画したプロジェクト仕様がわからない。
仕様書が十分にないし、どうすれば??
という悩みを抱えた経験はございませんか?
これらを解決するために、軽量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構成を採用されているリポジトリがあったので、こちらを共有します。
コメント