💨

【Flutter】機能ファーストなディレクトリ構成

2024/09/23に公開

はじめに

Flutterでの開発が進むにつれ、コードが増えてくるとディレクトリ構成が乱雑になりがちです。特に大規模なアプリケーションでは、スケーラビリティとメンテナンス性を考慮したディレクトリ構成が重要になります。

今回は、「機能ファースト」なディレクトリ構成を紹介します。機能ごとに整理された構成により、アプリケーションの拡張や管理が容易になります。

機能ファーストのディレクトリ構成とは?

「機能ファースト」のディレクトリ構成では、ファイルやクラスが機能ごとに整理され、関連するロジックやUIコンポーネントがその機能内で一貫して管理されます。この構成により、次の利点が得られます。

  • 可読性の向上: 各機能に関連するコードが1つのディレクトリに集約され、探しやすくなります。
  • スケーラビリティ: 機能単位で拡張することで、プロジェクトが成長しても構成が乱れにくくなります。
  • メンテナンスの効率化: 機能ごとにコードがまとまっているため、修正や追加が簡単になります。

また、機能ファーストと比較されるものとして、レイヤードファーストというものがありますが、以下は両者の比較になります。

項目 機能ファースト レイヤードファースト
コードの可読性 各機能に関連するコードが1つのディレクトリに集約され、わかりやすい 各レイヤーに分かれており、関連するコードを探すのに時間がかかる
スケーラビリティ 機能が増えても個別に管理できるので、拡張しやすい 機能の追加時、すべてのレイヤーにコードを追加する必要がある
再利用性 機能ごとに分かれているため、再利用が難しい場合がある レイヤーごとに共通ロジックを管理でき、再利用がしやすい
管理の複雑さ 機能単位でわかりやすいが、コードが重複しやすい 共通化されるため、管理は効率的だが、依存関係が複雑化しやすい
推奨される用途 機能ごとの独立性が高く、チームごとの分業や拡張性が重要なプロジェクト 一貫性と共通ロジックの再利用が重要なプロジェクト

ディレクトリ構成の概要

.
├── core
│   ├── components
│   ├── datasources
│   ├── extensions
│   ├── routes
│   └── util
├── features
│   └── todo
│       ├── components
│       │   └── todo_list.dart
│       ├── entities
│       │   └── todo.dart
│       └── repositories
│           └── todo_repository.dart
├── main.dart
└── pages
    └── home_page.dart
  • core/: アプリケーション全体で再利用される汎用的な機能(コンポーネント、データソース、ユーティリティなど)を格納します。
  • features/: 機能ごとにディレクトリを分け、それぞれの機能に関連するUI、ビジネスロジック、データモデルを格納します。
  • pages/: 主要な画面ごとのページを管理します。ページ自体のUIを定義するファイルがここに含まれます。

ディレクトリ構成の詳細

core

coreディレクトリは、アプリ全体で共有する共通機能を管理します。これは、アプリケーション全体で一貫して使われるロジックやコンポーネントを格納する場所です。
本稿では、このディレクトリに関しては詳細には説明しません。

  • components/: カスタムボタンや入力フィールドなど、再利用可能なUIコンポーネント。
  • datasources/: APIやデータベースとのやりとりに関連するデータ取得ロジック。
  • extensions/: DartやFlutterの標準クラスを拡張するためのExtensionメソッド。
  • routes/: アプリケーション内のルートやナビゲーションを管理します。
  • util/: アプリ全体で使用される汎用的なヘルパー関数やユーティリティクラス。

features

featuresディレクトリには、各機能ごとにディレクトリが存在し、UI、ビジネスロジック、データモデルを一箇所にまとめて管理します。例えば、todo機能が以下のように構成されます。

features/
└── todo/
    ├── components/
    │   └── todo_list.dart
    ├── entities/
    │   └── todo.dart
    └── repositories/
        └── todo_repository.dart

components

todo_list.dartなど、この機能に関連するUIコンポーネントが含まれます。ToDoリストの描画ロジックなどがここに書かれます。

todo_list.dart
class TodoList extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return ListView(
      children: [
        // TODO: Todoアイテムをリスト表示
      ],
    );
  }
}

entities

todo.dartなど、アプリ内で使用するToDoのデータ構造を定義するファイルが格納されます。ビジネスロジックやデータの整合性を確保するためのモデルが含まれます。

todo.dart
class Todo {
  final String id;
  final String title;
  final bool isCompleted;

  Todo({
    required this.id,
    required this.title,
    this.isCompleted = false,
  });
}

repositories

todo_repository.dartなど、データの取得や操作に関連するロジックを管理するクラスを格納します。APIやデータベースからToDoを取得する処理などがここに集約されます。

todo_repository.dart
class TodoRepository {
  List<Todo> fetchTodos() {
    // TODO: データソースからToDoを取得
    return [];
  }
}

pages

pagesディレクトリには、アプリケーションの主要な画面が含まれ、各ページは複数のfeatures//components/.dartのコンポーネントを組み合わせて構成されます。これにより、ページが独立して機能ごとのコンポーネントを統合し、再利用性と保守性が高まります。

home_page.dartでは、features/todo/components/todo_list.dartのようなコンポーネントを呼び出して、ToDoリストを表示する部分を構築します。他にも、異なる機能のコンポーネントを組み合わせて1つのページを完成させます。

home_page.dart
import 'package:flutter/material.dart';
import '../features/todo/components/todo_list.dart'; // Todo機能のコンポーネントをインポート
import '../features/user/components/user_profile.dart'; // ユーザー機能のコンポーネントをインポート

class HomePage extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Home'),
      ),
      body: Column(
        children: [
          UserProfile(), // ユーザー情報を表示するコンポーネント
          Expanded(child: TodoList()), // ToDoリストを表示するコンポーネント
        ],
      ),
    );
  }
}

まとめ

機能ファーストなディレクトリ構成は、アプリケーションを機能ごとに整理し、それぞれの機能に関連するコードを集約することで、可読性やメンテナンス性を向上させることができます。Flutterプロジェクトが大規模になるにつれて、管理が難しくなることがありますが、この構成により、スケーラブルで効率的な開発が可能になります。

この構成を取り入れることで、アプリケーションの成長に応じて柔軟に対応でき、開発チーム全体の効率が向上します。ぜひ一度、自分のプロジェクトに適用してみてください。

Discussion