ディレクトリの構成の話です
さてソフトウェア開発において、MVCやDDDは広く使われている設計パターンの1つです。しかしそういった設計パターンに引きづられたディレクトリ分けには問題点があり、概念別のディレクトリ構成がより良い選択肢であることがあります。本記事では、技術駆動なディレクトリ分けの問題点と概念別のディレクトリ構成のメリットについて解説します。
技術駆動なディレクトリ分け
設計パターンに引きづられたディレクトリ分けをここでは技術駆動なディレクトリ分けと呼びます。
MVCフレームワークにありがち
myapp/
├── controllers/
│ ├── user_controller.py
│ ├── product_controller.py
│ └── ...
├── models/
│ ├── user_model.py
│ ├── product_model.py
│ └── ...
├── views/
│ ├── user_view.py
│ ├── product_view.py
│ └── ...
├── main.py
├── requirements.txt
└── README.md
Rails, Laravel, DjangoのようなMVCアーキテクチャを採用したフレームワークではお馴染みのディレクトリ構成です。今回は例としてPythonになってますがModel, View, Controllerという各設計単位ごとにディレクトリが切られているという点はどのフレームワークも同じはずです。
DDDでもありがち
myapp/
├── domain/
│ ├── entities/
│ │ ├── product.py
│ │ ├── customer.py
│ │ └── ...
│ ├── value_objects/
│ │ ├── price.py
│ │ ├── email.py
│ │ └── ...
│ ├── repositories/
│ │ ├── product_repository.py
│ │ ├── customer_repository.py
│ │ └── ...
│ ├── services/
│ │ ├── order_service.py
│ │ ├── customer_service.py
│ │ └── ...
│ ├── use_cases/
│ │ ├── create_order.py
│ │ ├── update_customer.py
│ │ └── ...
EntityやValue ObjectといったDDD特有の設計単位がそのままディレクトリ分けや命名に反映されています。これも技術駆動なディレクトリ分けです。
技術駆動の何がダメなの
このディレクトリ構成では、さまざまな混乱が生じてしまいます。DDDの例で登場したファイルは、大きく種類分けすると商品(product)、顧客(customer)に大別されます。しかし、設計パターンでフォルダ分けしているために、どれがどの種類に関連があるのか一目では分かりません。
そしてこれらのファイルは、どれがどれに強く関係し合うでしょうか?たとえば商品は、product_repository.py
などが商品関連であることがわかります。では値オブジェクトの price.py
はどうでしょうか。一見商品の売値に関係がありそうですが、そうではなく実は買値でも用いられるものです。名前的にわかりにくく、何かの拍子にうっかり買値に売値が使われてしまう可能性があります。当然バグ化する可能性が高まります。また、email.py
はアカウント操作でしか用いられません。しかし、何かの仕様を満たすために、アカウント操作以外の用途で無理に email.py
が使われたりすると、本来の用途以外のものと結び付いてロジックが非常に混乱してしまいます。
本来強く関係し合うはずのファイルが技術駆動なディレクトリ分けではバラバラになってしまうのです。
(仙塲 大也. 良いコード/悪いコードで学ぶ設計入門保守しやすい 成長し続けるコードの書き方 (Japanese Edition) (pp.326-327). Kindle 版. を参考)
概念駆動なディレクトリ分け
root/
├── user/
│ ├── User.php
│ ├── UserController.php
│ ├── UserView.php
│ └── user.css
├── product/
│ ├── Product.php
│ ├── ProductController.php
│ ├── ProductView.php
│ └── product.js
├── cart/
│ ├── Cart.php
│ ├── CartController.php
│ └── CartView.php
├── common/
│ ├── Database.php
│ └── Utils.php
└── index.php
概念駆動なディレクトリ分けでは、機能や概念ごとにディレクトリを分け、そのディレクトリ内に必要なファイルをまとめます。例えば、"user"というディレクトリには、ユーザーに関連するModelやView、Controllerなどをまとめます。こうすることで、それぞれの機能や概念だけでしか使われないクラスを外部から隠蔽できるので、無関係な概念から参照される危険性がなくなります。また同じ分類どうしでまとまっているので、たとえばuser関係に仕様変更が生じた場合、userフォルダ内のファイルを読みに行けば良くなります。関連ファイルをあちこち探し回る手間が低減します。
(仙塲 大也. 良いコード/悪いコードで学ぶ設計入門保守しやすい 成長し続けるコードの書き方 (Japanese Edition) (p.328). Kindle 版. を参考)
※なお、この記事で挙げた例ではモデリングが不十分です。ある程度成熟したDDDにおいて user
などという名前でモデルを作成することは考えにくいですが、モデリングの話はここでは枝葉なので割愛しました。