Laravel +DoctrineでDDD開発の雛形的なものを作成
まず初めに
↑成果物です。
上記のREADMEに従っていくとセットアップが完了し、開発を行う準備が整います。
どんな記事ですか?
基本的には上記リポジトリ内容の紹介でございます。
この記事ではどのようにDDD開発を行うために工夫した点にはあまり多くは触れません。
ここに関しては別途の記事を書いていこうと思います。
開発を始める際の雛形のご紹介でありまして、DDD設計仕様なlaravelは雛形の一部でございます。
※リポジトリ内のREADMEをたどっていくと、設計思想なんかが書いてあったりなかったりします。
経緯
弊社ではPHP言語を扱う案件が比較的多いのですが、新規でアプリケーションを作成する際に
その都度一からフルスクラッチにて開発をすることがほとんどでした。
また設計に関しても特に指標があるわけではなく、各プロジェクトにアサインされているアーキテクトの裁量により決定されていました。
このような状況だと案件を重ねるごとに積み重なるはずの技術知見がまばらになりがちで、その後ロンチしたサービスの保守等をする際にはその都度キャッチアップに時間をかけてきていました。
この状況を打破するために社内で標準的となりうる雛形を作ろうということで今回雛形の作成へと踏み切りました。
あとdomain,service,infrastructure層に関しては、PHPらしさを排除できるように工夫してみました。
これは多言語でも同様な雛形を作ろうと思っていて、なるべく言語違いによるキャッチアップコストを下げたいという思いからこのような設計になっています。
主要な技術
開発環境
- Vagrant (必須ではないですがwindowsの場合は仮想環境上で動かす前提です)
- Docker
フレームワーク/ライブラリ
- Laravel (sail)
- Doctrine
- Flyway (DBバージョン管理)
- Vue(2系)
なぜLaravelなのか
この理由としては、やはりPHPのFWとしてのシェアが高いことが第一に上げられます。
開発も活発であり、なるべく幅広く、長く使えるものをと考え選定しています。
また、筆者はLaravelのDIコンテナが結構好きでありますのでそこも理由の一つに上げられます。
なぜDoctrineなのか
通常Laravelを使用したアプリ開発ではEloquentというORMを使うと思います。
一言でいうとこのORMを使用した開発が、今回の設計思想に合わなかった。というのが理由になります。
ここに関しては賛否両論あることと思いますが、やはりドメイン層とインフラストラクチャ層の関心事をわけたく
今回はDoctrineを採用しています。
※まぁしかしDoctineも学習コストは高めではありますのでもっと良い感じのMyBatisに近いSQLマッパー的なものがあれば教えていただきたいです。
Flyway?マイグレーションどうするの?
artisanにflywayを実行するマイグレーション用のコマンドを登録しています。
標準のマイグレーションは捨てています。
※賛否あると思いますが、筆者としてはこの組み合わせが一番しっくり来たのです。
クライアントのsampleがVueの2系なのだが?
現在3系にあげようか、React(Next)にしようか迷い中ですが、今後更新していくつもりです
Laravelソースの中にクライアントソースがないですが
はい、SAMPLEでは完全にソースを分離させたSPAな仕様となっています。
- service1/api
サーバサイドソースが詰め込まれています。つまりAPIにまつわるソースですね。 - service1/cli
クライアントサイドソースが詰め込まれています。
この構成に関しては特に縛りを設けているわけでもなく、各PJにて適宜扱いやすいようにしていくとよいかと思ってます。
API側のざっくりとした設計デザイン
工夫した点
sail
開発環境の大元になっているのはlaravelSailです。そのsailプロジェクトを複数扱えるように
(例えば管理画面用webサービス、公開用webサービス等)
bundle-sail というコマンドを作成し、仮想環境下で複数のsailプロジェクトを扱えるようにしています。
flyway
DBのバージョン管理としてこちらを採用し、laravelプロジェクト依存を抜け出すことで、複数のwebサービスで共有で使うようなDBリソースも
そこそこ安全に扱えるようになっているんじゃないかなと思っています。
doctrineのXMLマッピング
ドメイン層とインフラストラクチャ層を完全に分離させるために
xmlマッピングを採用しています。
また、mybatisでいうCollection的な機能を持つ関数を作成し、
DoctrineRepository
というDoctrineを使用したインフラ層が必ず継承するクラスを作成しています。
そして、クエリは基本的に「SQL構文を書く」という方針にしています。
またアプリケーション層にてトランザクションを扱うため
DoctrineTransactionManagerというトランザクション制御のクラスを作成
アプリケーション層では、TransactionManagerInterfaceというものを参照することでインフラ層と分離しています。
※上記のようにdoctrine依存な部分をインフラストラクチャ層以外から排除することでDoctrineからはいつでも卒業できる準備を整えています。
presentation層の半自動化
Swaggerを使ってIFの設計をした場合、そのjsonファイルをinputにrequest,resource,controller,routesをある程度は自動で作成してくれるようなコマンドを作成しています。
Laravel側にBuildツール導入
Phingというツールを導入し、その中で
- phpunit
- phpcs
- phpmd
を実行する設定にしています。
それぞれの設定値は各個別にxmlにて定義しています。
※コードをキレイにするコマンドとして、prettierという名前でcomposerに登録していたりします
CI/CDについて
あくまで参考程度ですが、下記のような環境を用意(できればですが)し運用していくといいのではないかと思っています。
今後の改善点
Doctrineのマッピング
現状Doctrineにて、xml形式のマッピングを採用しています。
これはmyBatisっぽくていいなぁっと思って採用したのですが、まぁマッピングするのがかなりメンドクサイ。。。
学習コストも結構かかっちゃうような感じになってるのでここをどうにかして簡素にしたいと思ってます。
※SQLは基本NativeQueryを使用してSQL文を書く。という手法に倒していますがここは思想上変えるつもりは今のところありません。ORMとしてどうなの?って話があると思いますが、これに言及すると荒れる原因になる気がするので言及は控えます。
テストコードの充実
テストコードのsampleがいまいち過ぎて参考にならないので
ここをもう少し充足させる必要がある。
クライアント側を再整理
現状vue2系というだけではなく、sampleのコードはAtomicDesignを採用してるっぽいのに、別にそうでもないという非常に中途半端なコードになっていて、標準的なものと旨を貼って言えません。。。
ここはVue3系にする、あるいはReactにする際に力を入れていきたいなと考えています。
テストコード然り
TIPS
郵便番号更新機能
郵政から提供されているken_all.csvという郵便番号データがあるのですが、そのデータを正規化して
インサート用SQLの作成、投入してくれるような機能が実装済です
ken_all.csvでお悩みの方向け。
最後に
この雛形はどのようなプロジェクトでも超有能に、超万能に機能するとは思っていません
そもそもドメイン駆動開発には結構な開発コストがかかりますし、超性能を求められた場合の最適解とは言い難いつくりとなっているためです。
しかしながら極端な時間的、性能的要求さえなければ本雛形の対応の幅は広いんじゃないかと思っています。
PHPでドメイン駆動な開発してみたい。
とか
PHPでレイアードアーキテクトやってみたい。
とか
LaravelでDoctrine使うといいって聞くけどsampleなさすぎて困ってる
とか
そんなビギナーたちの一助になるといいなと思っております。
この記事に関してはちょくちょく更新していこうと思っています。
また先に触れましたが、LaravelでDDDのもう少し詳しい内容なんかも別の記事を起こそうと思います。
指摘事項やアドバイスお待ちしております。
Discussion