【自社開発】保守性を武器にするFlutterエンジニアへ。ReITが実践するクリーンアーキテクチャ設計法
Photo by Mohammad Rahmani on Unsplash
こんにちは、ReIT合同会社の山田です。
今回は、現場のエンジニアリング実践にフォーカスした内容をお届けします!
現在弊社では、自社スマホアプリの開発をFlutterで進めており、将来の機能追加や保守運用に耐えうる設計を目指しています。
その中で「クリーンアーキテクチャに沿った構成」を意識し、保守性や拡張性の高いアプリをどう実現するかに取り組んでいます。
この記事では、その具体的な設計思想や工夫した点を、ディレクトリ構成や依存関係の整理方法を交えてご紹介します。
目次
「変更に強いアプリ」とは?
1. ディレクトリ構成で役割を明確に分離
2. 依存関係を明確にするクラス設計
依存性逆転の原則(DIP)を適用した例
3. UIと状態管理の責任を分離
各要素の役割
4. 最後に:設計の「意識」が未来の保守性を左右する
今後について
「変更に強いアプリ」とは?
アプリ開発において、仕様変更や新機能追加のしやすさは非常に重要なポイントです。
これを意識せず設計してしまうと、後々の保守・改修に膨大なコストがかかり、最悪の場合「プロダクトとして続けられない」なんてことにもなりかねません。
エンジニアにとっても、これは他人事ではありませんよね。
仕事を継続的に続けるためにも、「変化に強いアーキテクチャ」を意識することは重要です。
1. ディレクトリ構成で役割を明確に分離
まずは、全体のディレクトリ構成をご紹介します。
Flutterアプリにクリーンアーキテクチャの考え方を取り入れる場合、依存関係の方向を意識し、以下のような構成にしています。
lib/
├── app/ # アプリ全体設定(ルーティングなど)
├── core/ # 共通ユーティリティ・インフラ層
│ ├── util/ # 汎用関数、共通ロジック
│ │ ├── data/ # DBやFirebaseなどの実装
│ │ └── widget/ # 共通UI部品
├── feature/ # 機能ごとのディレクトリ
│ ├── function_A/
│ │ ├── data/ # データアクセス層
│ │ ├── domain/ # ドメイン層(モデル・ロジック)
│ │ └── presentation/ # UI層(Widgetなど)
│ └── function_B/
│ ├── data/
│ ├── domain/
│ └── presentation/
└── main.dart
特にポイントとなるのが core/util
のような共通層と、機能単位に feature/
を分割する考え方。
これにより、修正や追加をする際の影響範囲を限定できるようになります。
2. 依存関係を明確にするクラス設計
設計段階では、「どのクラスがどこに依存するか」を明確にすることが非常に大切です。
そのために、抽象(インターフェース)と具体(実装)を分けて設計するのがポイントです。
依存性逆転の原則(DIP)を適用した例
+---------------------+
| ItemRepository | ← 抽象クラス(インターフェース)
+----------+----------+
|
v
+----------------------------+
| ItemRepositoryImpl | ← 具体クラス(実装)
+----------------------------+
UIやビジネスロジック層では、ItemRepository
(抽象)だけに依存しているため、
例えばDBの実装を変更する場合も、ItemRepositoryImpl
を差し替えるだけで済みます。
+---------------------+ +------------------------------+
| ItemRepository |<------>| ItemRepositoryImpl(旧DB) |
+---------------------+ +------------------------------+
▲
|
▼
+------------------------------+
| ItemRepositoryImpl2(新DB) |
+------------------------------+
このように、抽象に依存し、実装は注入する構成にしておくことで、将来的な仕様変更や実装の差し替えも、最小限の影響で済みます。
3. UIと状態管理の責任を分離
Flutterでは、状態管理をどう設計するかも保守性に大きく関わります。
私たちは、以下のように「責務ごとにWidgetやProviderを分離」する設計を採用しています。
+-------------------------+
| MainScreen | ← 画面(Widget)
+-----------+-------------+
|
v
+-------------------------+
| ItemListWidget | ← UI部品(Widget)
+-----------+-------------+
|
v
+-------------------------+ +-------------------------+
| ItemViewModel (Provider)| <------ | ItemRepository |
+-------------------------+ +-------------------------+
各要素の役割
MainScreen
:画面単位のWidget。状態管理からデータを受け取る。ItemListWidget
:リスト表示のUI部品。再利用可能な形で作成。ItemViewModel
:状態や非同期処理を管理(Providerなどで実装)。ItemRepository
:データアクセスの抽象層。
このように責務を分離しておくと、
- UI修正時にロジックに影響しない
- 状態管理の切り替えが柔軟になる
- テストコードも書きやすくなる
といったメリットがあります。
4. 最後に:設計の「意識」が未来の保守性を左右する
アプリ開発において、「動けばよい」ではなく、「将来の変更にも耐えられる設計」を意識することが、結果として工数削減や開発スピードの維持につながります。
本記事では、
- クリーンアーキテクチャに基づいたレイヤー分離
- 依存性逆転の原則(DIP)を活用したクラス設計
- UIと状態管理の責務の分離
といった考え方を実際のプロジェクトでどう適用しているかをご紹介しました。
今後について
ReITでは、アーキテクチャや技術的な挑戦を通じて、「保守しやすく、継続可能なプロダクト開発」を一緒に追求できる仲間を求めています!
今後も、現場で得た知見や工夫を発信していきますので、アプリ開発を通じて「良い設計とは何か?」を考えている方は、ぜひ気軽にお話してみましょう!
ではでは!また次回!!