🧼

[Nuxt3][Firebase v9] クリーンなアーキテクチャを意識したい

2023/11/20に公開

最近DDDを学んだので、よく使う Nuxt3 と Firebase での軽いWEBアプリを開発する時でもクリーンなアーキテクチャを意識していこうと決意したのでここにメモしていこうと思います。

ちゃんと学べていないので、クリーンアーキテクチャとレイヤードアーキテクチャ、DDDの概念がごちゃ混ぜになっていますがとりあえず4つの層で考えてみます。
また、Nuxt3の独自の機能と、デフォルトのディレクトリ構成のままで、かつ可能な限りクリーンな構成を目指していこうと思います。

  • インフラストラクチャ層
  • ドメイン層
  • アプリケーション層
  • UI層

インフラストラクチャ層

ドメインの永続化のために、データベースなどの外部リソースやサービスに対する依存関係を注入する層。ドメイン層やアプリケーション層が直接的に知る必要のない、技術的な詳細をカプセル化し管理します。

ドメイン層

アプリケーションのビジネスロジックとデータモデルをカプセル化する中心的な部分。ビジネスの問題領域(ドメイン)を反映し、その核となる概念、ルール、およびビジネスプロセスを表現します。

アプリケーション層

アプリケーションのワークフローとユーザーの操作を管理し、ドメイン層のビジネスロジックを調整します。外部インターフェース(例えば、ユーザーインターフェースやAPI)との通信を担い、トランザクションの管理やセキュリティチェックなどアプリケーション全体の関心事を処理します。

UI層

UI層(ユーザーインターフェイス層)は、ユーザーがアプリケーションと直接やり取りする部分です。この層は、データの表示、ユーザー入力の受け取り、およびユーザーのアクションに基づく適切な応答の提供を担当します。

Firebase と Nuxt3 のデフォルトの構成で当てはめてみる

インフラストラクチャ層

plugins(プラグイン)に書く。プラグインはNuxt3のライフサイクルで一番最初に実行されます。
ここで Firebase の Auth や Firestore の依存関係を注入(Firebaseアプリ初期化)します。

次に実行されるデータフェッチのuseAsyncData (useFetch) は、UI層に書いていきます。
(ちなみにuseAsyncDataはNextJSのuseSWRに似たようなものでもある)

ドメイン層

/lib/Domain/ のカスタムディレクトリに置く。
エンティティは/lib/Domain/Entity/にTypeScriptのinterfaceでサクッと定義。
ファクトリーは/lib/Domain/Factory/に、バリデーションはライブラリのZodを使用。

アプリケーション層

composables
軽いアプリなのでまずはcomposablesに書いていき、エンティティが増えてきたら/lib/Domain/Repository/に書こうかなと考えました。

ここでは、Firestoreの必要な関数をインポートし、CRUD操作の処理を書いていきます。Firebaseと通信するので、インフラストラクチャ層でもある気がします。

それと、composablesにはコンポーネントを跨ぐグローバルな状態管理についても書きたいので、その場合はDBへのCRUD操作と同じファイルにuseStateで書いていく感じで、DBへのCRUD操作がないものは、use〇〇の最後にStoreをつけるような命名規則にしようかなと思います。

データフェッチについて、コンポーネントごとに必要なデータをフェッチすることも考えましたが、そうではなく上位のpageコンポーネントでデータをフェッチしてuse〇〇use〇〇Storeに書いたuseSateに格納してコンポーネント間で共有した方がいいかなと考えています。

UI層

pages や components の<script setup>内に書く。

まとめ

ボタンの@clickで実行する関数を<script setup>内に書く、その関数の中では、composablesで定義した関数を呼び出して実行していて、主にDBまたはstore(アプリ上でのDBみたいな立ち位置)とのやり取りをしている、バリデーションやresponseデータを整えたい場合はドメイン層のファクトリなどを呼び出して間に挟む、と言う感じですかね。

気づいたこと

ディレクトリ構成としては、新しく/lib/Domain/Entity//lib/Domain/Factory/を作ったことくらいで他に目新しい工夫などはあまりない様な結果になりました・・。

しかしながら目立たない工夫として、composablesのuse〇〇use〇〇Storeの命名や、データフェッチとグローバルな状態管理の規則を決めることができたのと、その層の役割以外のロジックや道具をimportせず関連ごとにカプセル化することを意識することができたので、以前よりも結構綺麗になったかなと思います。

Discussion