Azit Inc.

to Developer

DeliveryXのWebフロントエンドでのFeatureアーキテクチャ導入の背景と実践

  • #Webフロントエンド
  • #開発

2024.12.05

Sogame Masato

Azit CTOの十亀(@Pocket7878)です。
DeliveryXでは、2023年中盤からWebフロントエンドへの新しいアーキテクチャの導入を進めており、
本記事では、新しいアーキテクチャの導入にあたってどのような観点から判断を行ったか、
DeliveryXのWebフロントエンドで今後行いたいと思っている技術的な改善項目などをご紹介いたします。

2024年現在のDeliveryXのWebフロントエンド技術スタック

2024年11月現在、DeliveryXのWebフロントエンドの技術スタックは以下のようになっています。

  • Next.js 14
  • React 18
  • TypeScript

そして、プロジェクトの構成はモノレポ構成になっており

  • クライアント企業が利用する管理画面
  • 弊社オペレーターが利用する管理画面
  • 共通化されたUIコンポーネント
  • UIコンポーネントではない共通化されたロジック

という構造になっています。

また、クライアント向け管理画面は画面数27画面、社内管理画面は26画面となっており、それぞれの画面にいくつか機能が存在しますので機能数としてみるともう少し多くなっております。

2023年中盤ごろのアーキテクチャと課題

2023年中盤、DeliveryXではNext.js 13を使用し、Page Routerを採用していました。この時期、次期バージョンであるNext.js 14やApp Routerへの移行を検討していました。
当時のプロジェクト構成では、以下の特徴がありました:
• 各ページを*.page.tsxファイルとして定義。
• ページ内で使用するコンポーネントを同じディレクトリに配置する形式。

当時の課題

この構成で初期段階の開発は順調に進んでいましたが、ページ数や機能数が増えるにつれ、以下の課題が明らかになりました。

コード管理の煩雑化
ページ内機能が複雑化するにつれて、データ取得用のReact hooks、UIコンポーネント、API通信(POSTやDELETEなど)が混在し、管理が難しくなりました。

プロジェクト構造の視認性の低下
ページとその関連コンポーネント、ロジックが一箇所にまとめられているため、どのページが存在しているか一目で把握しにくい状況に。

新規メンバーへの負担
プロジェクト内に明確な規律や設計パターンが存在せず、新しいメンバーが加入した際に開発に慣れるまで時間がかかる状態でした。

これらの課題は、アプリケーション規模の拡大に伴う問題を浮き彫りにし、設計や構成の見直しを検討するきっかけとなりました。

新しいアーキテクチャの導入検討

そのため、これらの課題の解決のためにプロジェクトのアーキテクチャの見直しを検討しはじめました。
いくつかのアーキテクチャ設計のパターンを見ていく中で、たとえば以下のような事を検討しました。

Atomic Design

UIデザインの観点から整理するという方向で Atomic Designの思想にもとづいて整理することも検討を進め
実験用のブランチを作り試験的に既存の機能をリファクタしてみたりしました。
またAtomic Designを先に導入をすすめた企業の知見なども取り入れたいと思い、記事を探す中では以下の食べログさんの記事が非常に参考になりました。

https://note.com/tabelog_frontend/n/n07b4077f5cf3

食べログさんの記事では、

「Atoms」と「Molecules」を分けることで得られたメリットが特にありませんでした。

と触れられており、

  • Atomsは他のコンポーネントに依存していない最小粒度のコンポーネント
  • MoleculesはAtomsや同じ階層のMoleculesなどを組み合わせたコンポーネント

としてAtomic Designでは定義されていますが、

我々のチームの開発のなかでも

  • ボタン
  • 入力フィールド
  • 入力フィールドとボタンが一列にならんだインラインフォーム

などAtomsとMoleculesにそれぞれ分類できそうな単位のコンポーネントは存在しますが、
一方でそれらを区別して管理することで特に開発上まだメリットを感じられる状況では無いように思えました。

事業フェーズやチーム編成を考えると、場当たり的に開発を進めるのは当然避けるべきだと考えています。
一方で、設計思想に準拠しているかを検討することに過度な時間を割いてしまうリスクも、同様に避けるべきだと判断しました。

bulletproof-react

そのなかで、以下の記事と出会い、bulletproof-reactというアーキテクチャの存在を知りました。

https://zenn.dev/hrbrain/articles/437d0b7492ac47

bulletproof-reactではfeaturesというディレクト以下に機能のグループごとにディレクトリを作り

  • hooks
  • api
  • components
  • types

という構成で分類し、index.tsを通して外部に必要な部分だけを公開するという方針をとっています。

このような機能 (feature) ごとにディレクトリを分割して管理する方式はfeature moduleパターンとしてモバイルアプリなどでも採用事例が多く

https://github.com/DroidKaigi/conference-app-2024

実際にfeature moduleパターンを利用したプロジェクトの開発体験が良いことは弊社モバイルアプリに機能追加をした際に触れてみた場面でも感じていました。
ある機能がReact hooksであるか、componentsであるかなどは開発者の間で意見が分かれることは考えづらいと思い、
また、新しいアーキテクチャの導入目的であるプロジェクトへの規律という意味では十分であると判断しました。

そこで、実際に既存機能のいくつかを実験ブランチのなかで再整理し、迷いなく移行できることを確認したうえで導入を決定しました。
featuresディレクトリからはindex.tsを通して必要最小限の部分だけを外部公開しているため、内部のコンポーネントやロジックを再整理する際にも影響範囲が明確になるため、中長期でもメリットがあると感じています。

featureアーキテクチャとAppRouter移行

Next.js 13を長く使用してきたため、Next.js 14への早期移行を目指し、併せてApp Routerへの移行も進めることを検討していました。

しかし、まずは既存のコードベースの見通しが良くないと、新しい技術の導入は難しいため
まずは新機能開発時にはfeatureアーキテクチャで開発することとし、開発の合間で既存機能についても移行をすすめて全ページの移行を完了しました。

そこで、今度はPageRouterからAppRouterへの段階的な移行プロジェクトを開始することとしました。

AppRouterを利用するにあたっては、ページをRSCに対応する必要がありますし、速度の観点などからも当然メリットがあるため導入したいと思っていました。

一方で、DeliveryXでは物流のデータを扱っているため、ページは必要な情報鮮度の観点から以下のように区別できます:

  • クライアントの操作を伴わずに情報が更新され、鮮度の高い情報が随時反映される必要があるページ(業務の進捗確認ページや、車両の動態管理画面等)
  • 一般的なCRUD型の管理機能であり、クライアント側の能動的な操作によって情報が変更されるページ
  • クライアントの操作を伴わずに情報が更新されるが、鮮度の高さは求められていないページ

後者の2つについてはブラウザのリロードをされたタイミングで最新の情報が反映されればよく、データのフェッチはレンダリングごとで問題ありません。
(※ログインを伴ってユーザーごとの情報を表示する部分が多く、完全に静的に配信できるページは今現在はほとんど存在しません)

一方で前者についてはタブレットや作業場所でブラウザを開いたままにしていても勝手に画面内容が更新されてほしく、
そのようなページについてはすでにSWRを利用して実装されていたため、このようなページについてはほとんど丸々クライアントコンポーネントのままのほうが適切であると判断しました。

また、react-hook-formを利用してデータの入稿や更新系のフォームや実現していたため、Server Actionsには対応しておらず
将来的にServer Actionへの移行は別プロジェクトとして行いたいものの、今回の移行プロジェクトにおいてはこのような通信についても引き続きクライアントサイドから実施する方針としました。

このようにApp Router移行にともなって引き続きクライアントから利用される部分と、RSCでサーバーサイドから利用されるようになる部分が生まれるため
features内のAPI通信については、従来のapiディレクトリに一括してまとめるのは難しいと判断しました。
そこで、

  • server_api
  • client_api

という2つのディレクトリに分離し、それぞれに適切なAPIコールは配置することとしました。
また、featureディレクトリから外部に公開するのはserver_apiディレクトリのみに制限することでApp Router側のページからはRSCからの呼び出しを想定しているAPIのみとなるようにしました。

今後の課題とやりたいこと

ContainerとPresentationパターン化を検討しコンポーネントを整理する

RSC化をすすめるなかで、今回はserver apiclient apiを分離する形で分離をしていました。
現在はデータのフェッチはapiを直接呼び出すか、hooksで取得する形を利用していますが、RSCにともなってそのような構成を見直す動きもあります。
もしそのような設計のメリットが大きい場合は、featureディレクトリからserver_apiの公開を不要にできる可能性があり、その点も含めてリファクタリングを検討していきたいと思っています。

styled-componentsからRSCに適したCSSへ移行する

RSC化を進めていく中でいくつかのコンポーネントでは、単に情報を表示しているのみなので本来はサーバー側でレンダリング可能であるものの
styled-componentsしてスタイリングを行っているためクライアントコンポーネントのままになってしまっているパーツも存在します。
こちらについては、Tailwind CSSへの移行を進めており、そちらについても今後記事化したいと思っております。

Sever Actionへの段階的な移行

DeliveryXでは通信量を削減したりプログレッシブエンハンスメントを進めることで、
中長期的にプロダクトとして倉庫内など電波状況があまり良くない可能性がある環境など様々な方たちに導入していただきやすくなります。
そのような観点からRSCやServer Actionなどの仕組みを導入していくメリットがあると考えております。


AzitではWebフロントエンドエンジニアを募集中です。ご興味をお持ちいただけましたら以下の採用サイトからご応募下さい

この記事をシェアSHARE

この記事に関連する求人情報

  • Webフロントエンドエンジニア (Tech Lead)

この記事をシェアSHARE

MORE ARTICLES