DDDを意識したNext.jsのディレクトリ戦略
WEBアプリ開発においてNext.jsを採用するシーンというのはかなり多くなってきた印象があります。
そういった中で設計思想や開発方針、メンバーの構成、スキルセットによってどのようなディレクトリの構成にするか、というところが現れるところだと思います。
今回、私が担当した案件ではディレクトリ戦略としてDDDの文脈を表現できるような形にしたいと思いトライしてみました。
前提
説明するにあたって前提をいくつか揃えておきます。
設計思想
DDDを採用し、参考としてこの本を読みながら実践したつもりです。
ドメイン駆動設計入門 ボトムアップでわかる!ドメイン駆動設計の基本/成瀬 允宣
ライブラリ
ライブラリなどのバージョンはこちらです。
ライブラリ | バージョン |
---|---|
Next.js | 13.2.4 |
node | 16.10.0 |
バックエンドのshopifyのgraphql、microCMSのrest APIからデータを取得するシステム構成です。デプロイ先はAWSのAmplifyを使いました。
スキルセット、メンバー
私は普段はバックエンドのコードを書くことが多く、Next.jsは個人的に少し触ったことがある程度でした。
今回の開発ではNext.jsを使ったほうが工数をかけずにリリースできると判断し、Next.jsを採用するに至りました。
開発メンバーはエンジニアの私一人とデザイナー2人、QAが2人、ビジネスサイドのメンバーは2人という全員で7人での開発になりました。
ディレクトリ戦略については、もう少しきれいに整理することができたのかもしれないと思っているところはあります。同じ轍を踏まないでいいようにここに書きます。俺の屍を超えていけ。
ビジネスに柔軟に簡単に対応するために
開発していくコードでは次の3つを重要視しました。
- バックエンド/フロントエンドのコードを分ける
- コードを読めば何をしているかわかる
- ビジネス要件に柔軟に対応できる(DDDを取り入れる)
バックエンドとフロントエンド的な処理をできるだけコードを分けておくことで、これからシステムが大きくなっていくときにメリットがあると考えています。
また、できるだけ紳士協定的ではなく、コードで制約できるようにすることを意識し、ビジネス要件に柔軟に対応できるように、かつ、開発ができるだけわかりやすく簡単にできることを意識しました。
ディレクトリ構成全体像
結果を先に示すと、ディレクトリの構成の全体は以下のようにしました。
|- docker
|- src
|- layouts
|- models
|- pages
|- api
|- index(ページ)
|- reducers
|- repositories
|- interfaces
|- (repository)
|- services
|- states
|- stories
|- tests
|- repositories
|- services
|- types
|- utils
|- validators
以下、それぞれのディレクトリの説明をします。
docker
開発ではdockerを使っていました。src以下のディレクトリをdockerコンテナのボリュームとしてマウントします。
デプロイはAWSのAmplify
を使っていますが、この構成だと自動でコードを判別してNext.jsのアプリとしてデプロイが走るようになっています。
src/layouts
pageに読み込むlaytoutを定義しています。案件の都合として複数のlayoutを使うことがあったので、layouts
のディレクトリを作成しました。
src/models
DDDにおけるモデルを定義したクラス群を格納しています。
DDDの思想に沿って、repositoryクラスの中で利用されることを想定しています。
src/pages
src/pages/api
にはNext.jsのapi routesのディレクトリを作成しています。
そのほかは各ページのためのファイルが格納されます。
src/reducers
Reactのreducerを格納しています。DDDの文脈ではなくを横において、Reactの機能のディレクトリになっています。
src/repositories
src/repositories/interfaces
というディレクトリには、リポジトリのインターフェースを格納しています。
というのも、バックエンドのシステムとしてshopifyとgraphqlと、microCMSの REST APIを使っています。
この取得処理の違うことはバックエンドの関心事であり、フロントエンドの関心事ではないはずです。
取得するデータのバリエーションをフロントエンドが取得処理の書き方が違いを意識しないでいいように、複数のバックエンドシステムでもinterfaceを揃えておくことがベターと思います。
src/services
DDDにおけるapplication service
を定義しています。基本的にはrepositoryをラップしたり、ビジネスロジックが書かれることを想定しています。
DDDの文脈でのDomain service
は極力、定義しないように心がけています。
src/states
Recoil
を使っておりそれのディレクトリになります。ただRecoilを多用せず本当に必要な箇所、例えばページとページでstateを共有する必要がある場合にのみRecoilを使うようにしています。
src/stories
アトミックデザインを意識しており、この中にさらにatoms
, molecules
, organismas
, templates
のディレクトリを作成しています。
src/tests
src/repository
、src/service
のクラスのテストを定義します。それぞれsrc/tests/repositories
, src/tests/services
というディレクトリを作り、repository
, service
クラスをテストファイルを格納しています。
テストはjest
を採用しています。
src/types
基本的にはmodelクラスをはじめ、そのクラスを定義したファイル内でで全てを記述するようにしています。
しかし、型が複雑になっていたりするものをそのファイルで書いてしまうことで可読性を下げると考え、
このsrc/types
ディレクトリで別途、型定義するようにしています。
src/utils
このディレクトリではReactにおけるHook
、microCMS
、shopify
で使うユーティリティなコードをここに格納しています。
「util = なんでもあり」
という感じがあるのであまり使いたくありませんでしたが、この名前がしっくりきているというのも事実です。
ReactのHookはHooksというディレクトリに切り出してもいいと思っています。
src/validators
Reactで作ったフォームのバリデータを格納しています。バリデーションのライブラリはシンプルに書けるreact-hook-form
を使っています。
感想、余談
DDDという思想が流行り廃りの早い技術の波の中で、まだ生きているのかわかりません。
とはいえ、開発時点ではDDDの文脈に重きを置きたい、プログラミング言語の仕様に引っ張られるようなディレクトリは作りたくない、と考えていました。
しかし、Reactの機能のディレクトリをいくつか作ることになってしまったのは悔しさがあります。
zod, yayを使うと良いよ、という周りからの声もありましたが、あまり恩恵を感じれそうになかったのでつ使っていません。typescriptの型定義で十分という思っているのが個人的な感想です。
また最近のフロントエンドでは、プレゼンテーション/コンテナパターンというディレクトリ戦略が最近あることを知りました。
フロントエンド的にはこういうほうが開発がしやすかったりするのか?と思ってたりもします。
Discussion