USEN Media 初 ドメイン駆動設計を導入してみた
こんにちは!エンジニアの重松です。
当社が運営する訪日外国人向けグルメメディア「SAVOR JAPAN」では、Web開発のフレームワークをLaravelへ一新するため、私含め計3名のリニューアル開発チームができました。そこで初の試みとして、開発設計手法にドメイン駆動設計(以下、DDD)を導入することになったので、導入までの背景や実際に取り入れてどうなのかお話できればと思います〜!
ドメイン駆動設計の導入に至るまで
初めに軽くDDDについて説明すると、DDDとは「Domain Driven Design」の略称でEric Evans氏が考案した設計手法です。DDDの定義は調べると様々で一言で説明するのが難しいですが、ドメイン駆動設計 モデリング/実践ガイドから引用すると「モデリングによってソフトウェアの価値を高めることを目指す開発手法の一つ」です。
まず、導入前のチームメンバーの状況ですが、DDDに取り組んだ経験がある人は誰一人いませんでした。
お恥ずかしながら、私はその当時ドメイン駆動設計とは?という状況でした。。。
そんな中、チームリーダーを筆頭に導入することが決定し、学びつつ実装を開始することになりました。
では、なぜ導入を決めたのかチームリーダーにお聞きしました。
重松:プロジェクトが始まった当初、チームリーダーがドメイン駆動設計の導入を提案したと思うのですが、導入を決めたきっかけは何だったのでしょう?
チームリーダー:SAVOR JAPANのコードを見たときに、サブドメインとロジックが混在していたり、DBのロジックとViewのロジックが混在しているなと思いました。ドメイン駆動設計を導入することで、それらの問題が解決されると感じたためですね。
重松:導入にあたりマネージャー陣とのMTGでは懸念点など指摘されることはなかったのでしょうか?
チームリーダー:クラスが多くなりすぎて処理が追いにくくなるのではないか?とは指摘されました。しかし、レイヤーごとの責務を明確にし、コーディングルールを徹底することで、ファイル構造が見通しやすくなり、この問題を回避できると考えています。また、分けるべきロジックが密結合してしまうよりは、チームメンバーに各レイヤーの役割をしっかり認識してもらい、コアドメインの価値を高めるためにもやったほうが良いと思い、押し通した感じはあります。
チームリーダーが上記で指摘していた問題はドメイン駆動設計は何を解決する手法なのかの記事でコアドメインを高められない妨害要因と説明されており、まさにDDDによって弊システムの機能修正のし辛さなどが改善されるのが期待できます。
メンバー認知のために取り組んだこと
取り組み1 : 勉強会
最初はチームメンバーで、READYFOR株式会社が投稿したREADYFORエンジニアセッション Vol.3 「DDD:ドメイン駆動設計 入門〜はじめの一歩」を視聴して、概念的な部分の理解に努めました。この動画では、そもそもドメインとは何かというところから噛み砕いて説明されているので、初心者に優しい内容です。
それ以降も週1で勉強会の日を設定しており、自主的に最近学んだことや共有したいことを発表することができます。また、月1でコード共有会も行っています。コード共有会を通じて、ドメインモデルの扱い方について議論したり、実装の際に漠然と進めていた箇所を具体的なコーディングルールに落とし込むことができています。その結果、よりDDDを取り入れた実装になっていると思います。
その他にも参考になりそうな記事やgithubのリポジトリをチーム内で共有するようにしており、特にlittle hands' lab の記事にはとても助かりました。
取り組み2 : レイヤーごとの責務を整理
私たちのチームではオニオンアーキテクチャを採用しています。
DDDでは基本的に永続化層との入出力はリポジトリを通じて行われますが、参照処理の場合、複数集約を組み合わせた結果を画面に返そうとすることが多いため、パフォーマンスの問題が起こってしまいます。その問題を解決するためにCQRSも導入しています。
そして以下の図のように、Controllerから呼ばれるクラスを明確化するなど、レイヤーごとの責務を整理しました。また、各レイヤーのフレームワークの依存関係も決めています。
取り組み3 : ドメインモデル図の作成
実装を開始する前に、更新処理があるモデルを対象にシステム設計図、ユースケース図、ドメインモデル図、オブジェクト図を作成しています。
最初の頃、この作業がとても大変だった記憶があります。Aggregateが膨大化してしまい、何度もチームリーダーと話しながら修正を繰り返してました、、、
参考にしたのは簡単にできるDDDのモデリング - ドメイン駆動設計の記事です。その中で、ドメインモデル図の作成に以下のルールが記述されています。
このルールを基に作成し、コードに落とし込んだことで抽象的なクラス名やオブジェクト名が無くなりました。また、同じようなロジックが複数ファイルに実装されなくなり、モデルが持つ役割が明確化して実装しやすくなったと思います。
実装での取り組みと効果
実装では何度も繰り返し修正しクラスに落とし込んでいくため、namespaceを勝手に直してくれる便利コマンドなどを用意しています。
また、ドメインサービスは1クラス1メソッドにするなど基本的なルールを決めています。これは以下のサイトを参考にしています。
それだけでなく、チームリーダーのコードレビューのお陰で一定のクオリティが保たれていると実感しております。そこで、チームリーダーにコードレビューで気をつけている点をお聞きしました。
重松:コードレビューする際に、DDD周りで気をつけてることがあれば教えてください。
チームリーダー:各レイヤーの責務外の処理が混じっていることがないかなどの確認はします。
あとは、実際のコードに落とし込んでからレビューするよりもモデル図を作成してレビューする方が修正スピードが短縮しているので、今後やっていきたいでです。
重松:レビューしていると複雑なパターンなど、どうDDDに落とし込むか悩むこともあると思うのですが、参考にしている資料などあれば教えてください!
チームリーダー:一番参考にしているのはドメイン駆動設計 モデリング/実践ガイドですね。その中で記載されているFetcherとかは実際に取り入れました。
私もドメイン駆動設計 モデリング/実践ガイド を活用しています。それ以外に、実装に困った時は他の方の実装例を探して積極的に採用しています。例えば、外部コンテキストの通信は DDD Beyond the Basics: Mastering Multi-Bounded Context Integration を参考に実装しました。
徐々に開発が進む中で、DDDの効果も実感しています!
ドメインロジックやユビキタス言語をモデル化したものをドメイン層に置いたことで、高擬集になり複数ファイルに同じロジックが点在する問題が改善されました。
また、技術的関心事とドメイン関心事が分けたことで、コアなロジックの整合性を保ったまま参照処理、更新処理の修正が容易になりました。
最後に
DDDを導入して約半年が経ちましたが、SAVOR JAPANのシステムは参照処理が多いので、ドメイン駆動設計を取り入れたことで大分シンプルになったと思います。
また、最初の頃はレイヤー違反になっていないか、この処理はどのファイルに置くのが最適かなど何度もチーム内で議論してきたことで、同じ認識のもと実装を進めることができています。
ファイル数が増えていることでディレクトリ構成を調整したりと、まだまだ改善することも多いですが、DDDを最大限に生かせるように努めていきたいと思います!
ご一読いただきありがとうございました!!!