俺流フロントエンドのディレクトリ構成と設計の考え方

アマノのプロフィール画像
エンジニアアマノ

導入

お疲れ様です、ベイジでエンジニアをやっている天野です。

ベイジではWebアプリのビジュアルデザインやフロントエンド実装の案件が増えてきており、エンジニアは日夜設計に頭を悩ませています。今回は、その中で個人的に良いと感じたフロントエンドの設計について共有します。

注意点として、ベイジではモックアップ実装の案件が多く、サーバーサイドとの通信の部分に関しては実装しないことが多いです。よって本記事では、UIなどWebアプリにおけるViewの話が中心となります。

ディレクトリ構成

最初にディレクトリ構成を示すことで、本設計の概要をつかんでもらいます。

src // ルートディレクトリ
├─infrastructure // インフラ層。バックとの通信やデータ管理を担う
│ ├─domain // APIのインターフェースの定義など
│ ├─interface // データの通信処理
│ └─repository // データ保持に関連。状況によってinterfaceと統合
│   └─mockdata
├─ui // UI層。UIパーツなど主に見た目に関する処理を入れる
│ └─component // アトミック・デザインを取り入れてコンポーネントを管理
│   ├─elements // アトミック・デザインにおけるatoms, molecules
│   ├─layouts // アトミック・デザインにおけるtemplatesに近い使い方
│   ├─modules // アトミック・デザインにおけるorganisms
│   └─pages
│     └─sample // サンプルページ 
│       ├─presentation //プレゼンテーション層
│       └─container //コンテナ層
├─libs
├─utils
└─etc...

3つのポイント

本設計のポイントは3つあります。

①大きくインフラ層、UI層に処理を分ける

フロントエンド実装における関心というのは大きく2つです。

1つが、画面の見た目やユーザの操作に関する処理。2つ目が、サーバーサイドとのやり取りまでの一連のデータの処理です。前者の関心に関わる処理を/uiというディレクトリ(以下UI層)にまとめ、後者のデータのやりとりについて責任を持つ処理を/infrastructure(以下インフラ層)というディレクトリにまとめます。

インフラ層はざっくりの説明となりますが、ビジネスロジックを含む処理(ないことが望ましいですが)や、APIの型定義を/domainに格納します。APIとの通信処理を/interfaceに入れ、/repositoryの中ではサーバーサイドから取得したデータをフロントエンドで永続化しておくような処理を格納します。ベイジではモック用のデータが必要になることが多いため、/repositoryの中に/mockdataというディレクトリを作り、そこで管理をします。

ベイジの業務上、このインフラ層をいじることはあまりないため、詳しい説明は省きます。

②UI層ではアトミック・デザインを踏襲する

次にUI層についてですがですが、ここではアトミック・デザインを取り入れたディレクトリ分けとなっています。アトミック・デザインについてはこちらをご覧ください。

アトミック・デザインでは本来、”原子”、”分子”、”有機体”、”テンプレート”、”ページ”の5つのレイヤーでコンポーネントを分けますが、著者であるブラッド・フロスト氏も組織の性質やコミュニケーションの相手によって、レイヤーの数とその呼び方を変えることを推奨しています。

“Atomic design” as a buzzword encapsulates the concepts of modular design and development, which becomes a useful shorthand for convincing stakeholders and talking with colleagues. But atomic design is not rigid dogma. Ultimately, whatever taxonomy you choose to work with should help you and your organization communicate more effectively in order to craft an amazing UI design system.

流行語としての「アトミック デザイン」は、モジュラー設計と開発の概念を要約したものであり、利害関係者を説得し、同僚と話をするための便利な略語になります。しかし、アトミック デザインは厳格な教義ではありません。最終的には、どのような分類法を選択しても、あなたとあなたの組織がより効果的にコミュニケーションを取り、素晴らしい UI デザイン システムを作成するのに役立つはずです。

https://atomicdesign.bradfrost.com/chapter-2/

あくまでも、関係者間での共通言語として働くわかりやすい呼び方や分類をしよう、と言っているに過ぎません。ベイジでは先にディレクトリ構成で示した4つのレイヤーで管理しています。そのままアトミック・デザインに倣ったディレクトリ名でも問題ないでしょう。

アトミック・デザインのコンポーネント設計の本質の1つは、コンポーネントの適切な粒度と依存関係の管理にあると考えます。ここはサーバーサイドの処理を主な対象とした、レイヤード・アーキテクチャと呼ばれる設計論とも通ずるところがありますね。レイヤード・アーキテクチャについては後で概要を説明しますが、詳しく知りたい方はこちらの書籍をご覧ください。

③Pages層でコンテナ・プレゼンテーションパターンを使う

UI層の/pagesディレクトリ配下にSampleというページがあるとして、その中に/container/presentationというディレクトリが切られています。

ここでは、デザインパターンの1つである「コンテナ・プレゼンテーションパターン」の考え方を取り入れています。コンテナ・プレゼンテーションパターンは画面の表示と、内部の処理を分ける目的で使われます。

Container層では、View側は最低限のレイアウトのみを担当し、インフラ層とデータをやりとりするためのインターフェースとして主に働きます。そして受けとったデータをPresentation層に流す、というのがContainer層のコンポーネントの役割です。

Presentation層では下のレイヤーのコンポーネント群に対し、Container層から受け取ったデータを渡してページに即した実態を与える役割を担います。というわけでPresentation層の中では、粒度の大きい(アトミック・デザインでいうと有機体レベル以上)複数のコンポーネントを格納することになります。

本設計の目的

上記であげた3つのポイントは、どれも一貫して「関心の分離」を目的にしています。というのも、フロントエンドの実装は先ほどあげたように「画面の見た目」と「データの処理」という2つの関心が絡み合い、下手をするとカオスなコードになりがちです。

3つのポイントを念頭に置き、ここまで触れてこなかった設計の根底にある考えについて見ていきます。それが依存の方向性です。

依存の方向性

インフラ層とUI層、/pages配下のレイヤーの異なる各コンポーネント、そしてPresentationとContainerなど、それぞれで処理やコンポーネントの呼び出す先、つまり依存の方向性を定義します。

依存の方向性については、クリーンアーキテクチャで用いられている図が分かりやすいです。

Robert C. Martin(2018)『Clean Architecture 達人に学ぶソフトウェアの構造と設計』 角征典 高木正弘 訳 ドワンゴ.

クリーンアーキテクチャに限らないレイヤードアーキテクチャ全般の考え方になりますが、ソフトウェアの部品をその責務(関心)ごとにグループ化し、グループ同士のやり取りのルールを定義しようというものです。

層の外側から内側に向って矢印が伸びており、これは責務で分けたグループ同士の依存の方向性を表します。内側に向かうほど、ソフトウェアが対象とするビジネスそのものを表現するコードとなり、外側にあるほどビジネスに直接依存しないコードを指すようになります。

例えば一番外側に「UI」という層がありますが、これが変更したからといってシステムが対象とするビジネスの内容そのものが変わることはありません。逆に業務そのものが変われば、それに応じて内側のビジネスを表現したコードが変わると同時にシステムのUIが変わることはあるでしょう。

よって依存の方向性として「UIはビジネスロジック側に依存する」と言うことができ、他のグループに関しても同じような依存の方向性があることをこの図は表しています。

これを踏まえて、本記事の設計を図に起こしてコンポーネントや処理の依存の方向性を可視化すると以下のようになりました。クリーンアーキテクチャのような同心円にはなりませんでしたが、矢印の先と元で依存の方向性を示しているのは同じですね。

※諸々の事情があり、この図ではComponentの中身を一般的なアトミック・デザインのレイヤー名に対応させています。

基本的には、この図の矢印の先が元の処理やコンポーネントを呼び出します。

AtomsやMoleculesなどは、自身より上位のレイヤーのコンポーネントに関する知識を持たず、ただ呼び出されるがままに自身の持つ機能を提供します。UI層とインフラ層を繋ぐ矢印に関してのみ、インフラ層は基本的に自分たちが扱っているデータをUI層がどのように表示するかについて関心を持たず、UI側がインフラ層の処理を呼び出す場合が基本となるはずです。よって、UI層はインフラ層に依存していると言えます。

この例については、よりシンプルな設計にできることもあるでしょうし、見方や実装の仕方によっては依存の方向性が変わることもあります。

システムの規模次第で要素を増減させたり、名前や矢印の向き先などは変える必要がありますが、依存関係のつながりさえきちんと整理されていれば、フロントエンド実装が複雑化することをある程度防げると考えます。

補足

ここまで話に挙げませんでしたが、フロントエンドには「状態管理」という概念が登場してきました。詳しい説明は省きますが、これはここまでで分離しようと試みてきたUIの見た目とデータの両方に関心を持とうとするものだと解釈しています。

個人の考えですが、状態管理はフロントエンドにおける複雑さを生みやすい要因の1つだと思います。というのは、一口に「状態管理」と言っても個々のコンポーネントの中で完結するものと、アプリケーション全体で保持しておくべきものを判別する必要があるためです。

アプリケーション全体で保持しておくべき状態を管理したい場合にFluxアーキテクチャが採用されることは多いですが、その必要がないケースも当然あります。よって、Fluxアーキテクチャを含むアプリケーション全体の状態管理のために、それらの処理を格納するディレクトリをはじめから用意しておく必要はないと考えます。ディレクトリ構造上定義していないのは、「必要に応じて追加するもの」ということを意識するためです。

もし、アプリケーション全体の状態管理が必要になる場合、いくつか考え方はありますがUI層とインフラ層と並んで状態管理層を設けるか、インフラ層に配置してページのコンテナ層と繋ぐのが良いと考えます。その場合、インフラ層に新しくディレクトリを切るか、/repositoryの中で管理しても良いかもしれません。(UI層に入れても良い、という判断になる可能性もありますが、ここは検討中。)

その他にも、ルーティングやセキュリティ関連などの処理がどの層やディレクトリに入るか、といった問題も恐らく実際にこの設計を使うと出てくるはずです。明確な答えは出ませんが、ルーティングは画面の見た目の処理に該当するので、UI層に準ずる処理として分類するのが無難でしょうか。セキュリティはインフラ層に該当しそうですが、根本的に設計を見直して、UI層とインフラ層に並列する形で何らかの階層を新たに設けても良いかもしれません。フレームワークやライブラリそれぞれのベストプラクティスがある場合はそれに準じるのも良いでしょう。

おわりに

他にも現時点で考慮しきれていない部分はあると思いますが、今後はアプリ開発をする際に本記事で紹介した設計をベースに、フロントエンド実装の際の設計を行おうと思っています。

本記事を読んで「こういう考え方があるのかぁ」など新しい発見があったり、何かの参考になることがあれば幸いです。

最後に、この記事を執筆するにあたり非常にためになった、参考にさせていただいた資料や書籍を掲載します。

参考資料

フロントエンドでDDDLikeなアーキテクチャを導入したときに困ったこと・対策(検討中も含む)

【DDD】フロントエンド に「クリーンアーキテクチャ」を適用した場合 | プログラミングマガジン

フロントエンド(Angular)でクリーンアーキテクチャを適用できないかと格闘した記録 – Qiita

WEBフロントエンドにおけるソフトウェア設計の考察 – Speaker Deck

ぼくのかんがえたフロントエンドアーキテクチャ

Atomic Design by Brad Frost

フロントエンドのコンポーネント設計に立ち向かう

Reactで作る中規模SPAのレイヤードアーキテクチャ

書籍

Clean Architecture 達人に学ぶソフトウェアの構造と設計 | Robert C.Martin, 角 征典, 高木 正弘 |本

エリック・エヴァンスのドメイン駆動設計 (IT Architects’Archive ソフトウェア開発の実践) 大型本

こんな記事も読まれています

【2024年】ハンバーガーメニューの作り方決定版【コピペ可能】
【2024年】ハンバーガーメニューの作り方決定版【コピペ可能】
かんののプロフィール画像
かんの
ウェブアプリ開発にTailwind CSSを導入してみた
ウェブアプリ開発にTailwind CSSを導入してみた
金 伯冠のプロフィール画像
金 伯冠
ChatGPTの新機能GPTs!自分だけのオリジナルGPTを作ってみた!
ChatGPTの新機能GPTs!自分だけのオリジナルGPTを作ってみた!
金 伯冠のプロフィール画像
金 伯冠
WordPressの外観カスタマイズでノーコードのような機能を実装する方法
WordPressの外観カスタマイズでノーコードのような機能を実装する方法
さかっちょのプロフィール画像
さかっちょ
上に戻る