🚀 ニフティ’s Notion

【モバイルアプリ2024 #6】モバイルアプリのアーキテクチャについて

💡
この章で話すこと
  • 設計を行う上での考え方について
  • レイヤードアーキテクチャについて
  • モバイルアプリにおけるプレゼンテーション層
  • モバイルアプリにおけるドメイン層
  • モバイルアプリにおけるデータ層


良い設計をするためには何を気にすればいい?

慎重に良い設計をするべき、ということに否定する開発者はいないと思います。

JetpackComposeを使う時に気をつけなければならないところはデータの扱いです。

JetpackComposeは生まれた背景とその性質上、Viewのコードも全部Kotlinで作ることになります。その故に、状態(State)を使ったデータの操作の設計がうまくできていないと、Viewのコードと、データ操作のコードが互いに依存してしまい完成した後の維持保守が難しくなる可能性があります。

そのために各レイヤー(データ、ドメイン階層など)の役割、データを扱う方法(MVVMパターン、データバインディングの仕組みなど)とその必要性を理解し設計をする必要があります。

設計を考える上で大事なこと

設計とは

アプリケーションを設計する、とは以下のような作業を指します。

  • アプリケーションを層(≒クラス)として分割する
  • 各層は明確な責任を持ち、責任外の処理は他の層に移譲する
  • 層の入れ替え・拡張により、起こりうる変化に対応できるような単位で分割する

ここで問題になるのは、仕様変更による システムに起こりうる変化 とは何かということです。ある程度まではプロジェクト開始時に洗い出しますが、予測できない変化はよく起こります。

オーバーエンジニアリングを防ぐ

ではどんな変更にも対応できるように極めて詳細な設計をしておくのが正解でしょうか?いいえ。 You Are not Going to Need It.(YAGNI: どうせ誰も使わないよ) です。使わない機能を作り込むのは時間の無駄であるばかりか、可読性を下げ、後の運用も辛くします。

例: FizzBuzzを作り込んだ例 https://github.com/EnterpriseQualityCoding/FizzBuzzEnterpriseEdition

大切なのは リファクタリング です。変化に応じて、規模に応じて、仕様を変えずに設計を変えていくことです。そして仕様を変えないことを保証するには テスト が必要です。

つまり、 テストをしやすいコードを維持することが最も重要 です。

既存のパターンに乗る

設計は自分で1から考えなければならないかというと、そんなことはありません。大抵の問題は先人が通った道であり、すでにパターン化されています。

既存パターンを学びつつ、その中から適切なパターンを選んでいくのが最初のステップです。

設計を行う上での一般的な考え方について学びました。

次に、アーキテクチャについてお話します。

レイヤードアーキテクチャとは

モバイルアプリの構成に触れる前に、一般的なシステムの構成についてお話します。

レイヤードアーキテクチャって?

UIのあるシステムは基本的に次のような処理をします。

  1. UIが入力を受ける
  2. 入力イベントをシステムが解釈し、処理
  3. 処理結果をUIに描画

UIとシステムには明確な関心の違いがあります。

関心が違うならレイヤーとして切り分けるべきだと考えられます。

一般的にアプリケーションを設計するときは、全体をいくつかの役割を持ったレイヤー(層)に分けて考える、ということがよく行われます。このような設計手法を レイヤードアーキテクチャ と言います。

3つのレイヤーについて

レイヤーの分け方は複数パターンありますが、シンプルなケースでは以下の様になります。

image block
1. プレゼンテーション層
  • UIの表示、ユーザ入力の受け付けに責任を持つ
  • ビジネスロジックを持たず、画面にのみ責任を持つ
2. ドメイン層
  • ドメインロジックを持つ(UI以外の処理)
  • 単純なアプリでは存在しないこともある
3. データ層
Webアプリ回では「インフラストラクチャー層」と説明しております
  • データの取得・更新に責任を持つ
  • DBやHTTPでのデータ取得・更新など

各層の内部の作り方はさらに細かくパターンがあります。以降では以下の内容について解説します。

  • プレゼンテーション層
  • ドメイン層
  • データ層


プレゼンテーション層(モバイルアプリの主流な設計モデルについて)

レイヤードアーキテクチャにおけるプレゼンテーション層を深堀ってみます。

構成要素

モバイルアプリは主に三つの要素で構成されています。

  • ビュー(View)
  • ビューモデル(View Model)
  • モデル(Model)

image block
https://learn.microsoft.com/ja-jp/dotnet/architecture/maui/mvvm
1. ビュー(View)

ビューの役割はUIに関わるものになります。ユーザーがスクリーンを通して見るものに対する情報、つまり構造・レイアウト・形態のことです。ビューはアニメーションみたいなUIロジックを含めていてビジネスロジックを含めてはいけません。

2. ビューモデル(View Model)

ビューモデルの役割はビューが使うメソッドとフィルド(メンバー変数)を実装し、ビューに状態(State)の変化を知らせることです。ここでビューはビューモデルの状態の変化を観察(Observing)します。つまり、ビューモデルで提供するメソッドとフィルドがUIで提供する機能ということになります。しかしこの機能と情報をどいう形で見せるのかはビューの役割になります。

3. モデル(Model)

モデルはビジネスロジックとデータが含まるアプリのドメインモデルのようなものです。モデル内でアプリで使用するデータと関連の動作を定めます。


🤔
レイヤードアーキテクチャとの関連性
プレゼンテーション層の中でこのような設計モデルが展開されることになります。
image block
💡
モデル、ビュー、ビューモデルの関係について
ビューはビューモデルの存在を知っているのですが、ビューモデルはビューを知らないです。ビューモデルはモデルを知っているのですが、モデルはビューもを知らないです。つまり依存関係は一方の方向を指しているということです。
このように依存関係の向きを揃えることで、わかりやすく変更容易なアーキテクチャを実現します。
image block
💡
ビューモデルとモデルの対応について
一般的にはビューモデルとモデルは1:N関係になります。ビューモデルはビューがわかりやすくするようにモデルのデータを加工してビューに提供します。例えば、ビューから違う二つのモデルを活用したデータが必要な場合、ビューでモデルの値を操作して使うのではなく、ビューモデルから二つのモデルのデータを加工してビューではUIだけ扱うようにします。
image block
ビューモデルとモデルの関係図
その他の構成との比較

🚫 Post not found

前章ではモバイルアプリの設計モデルをみていきました。

では、その他に使われている設計モデルはどういったものなのかについて見ていきます。

MVPパターン
image block

  • View
    • HTMLやXML、はたまたベタ書きで作られたUI部品の集合体
    • Modelから取得したデータを埋め込んで表示する
  • Model
    • ビジネスロジック
    • レイヤードアーキテクチャにおける ドメイン層+データ層
  • Presenter
    • Viewから呼び出される
    • Modelのメソッドを呼び出し、その返り値でViewを更新する
    • ModelのデータからViewのデータに変換する(色とか)のもここで行う
MVCパターン
image block
  • View
    • 省略
  • Model
    • 省略
  • Controller
    • ユーザ入力を受け、Modelのメソッドを呼び出してデータを更新する

MVVM, MVC, MVP, の比較表
特性 MVVM MVP MVC
構成要素 Model, View, ViewModel Model, View, Presenter Model, View, Controller
役割分担 ViewModelがViewとデータ同期し、Modelの更新用メソッドを呼ぶ Presenterがデータの取得・変換・Viewへの反映を行う Controllerがユーザ入力を受け取り、Modelを更新、Viewを再描画
メリット - Viewのテストがしやすい
 - Viewの状態はViewModelで完全に表現されている ※1
- Modelに存在しないデータでもPresenterが変換してViewに表示できる
 - MVCより複雑なUIを作れる
- 単純な構成である
デメリット - Databindingに対応したフレームワークが必要
- ViewModelが大きくなる
- Presenterが機能を持ちすぎてメンテ不能になることが多い
- 関数呼び出しベースなので、Viewの再現・テストが難しい
- Modelのデータ更新に追従してViewも更新する必要がある
- Modelとは異なるデータを画面に反映したいときに困る
ユースケース 大規模で複雑なアプリケーションに適しています。特に、UIの状態管理が重要であり、 ※1データバインディング を活用できる場合に有効。

【使用例】リアルタイムデータの表示や、ユーザーインタラクションが多いアプリで使用されます。
中規模から大規模のアプリケーションに適しており、特に複雑なUIロジックを持つ場合に有効です。
プレゼンテーションロジックを分離することで、テストが容易になります。

【使用例】ダッシュボードやデータ表示が多く、ユーザー操作が頻繁なアプリで使用されます。
小規模から中規模のアプリケーションに適しています。
ユーザー入力を受け取って、シンプルなデータ変換と表示を行う場合に有効です。

【使用例】フォーム入力や基本的なCRUD操作を行うアプリで使用されます。

※1  データバインディング とは
   ViewとView Modelのどちらかで値が書き変われば、値が変化するたびにViewとView Model、両方の値が変更されるという性質を指します。

以上がプレゼンテーション層におけるモバイルアプリの設計モデルについてでした。

開発の際は適切な設計モデルを選択することを心がけましょう。

ドメイン層

ドメイン層は、複雑なビジネスロジックだったり、複数のViewModelで再利用されるビジネス ロジックがある際に利用します。
一方、すべてのアプリにこのような要件があるわけではないため、この層は省略される場合もあります。

複雑さに対処する場合や再利用性を優先する場合など、必要な場合にのみ使用してください。

image block

一般的に、このレイヤのクラスを「ユースケース」または「インタラクタ」と呼びます。各ユースケースは 1 つの機能を担うようにします。

たとえば、複数の ViewModel がタイムゾーンに基づいて適切なメッセージを画面に表示する場合、アプリに  GetTimeZoneUseCase  クラスを持たせることができます。

データ層について

次に、モバイルアプリにおけるデータ層についてお話します。

アプリのデータ層には、ビジネスロジックが含まれています。ビジネスロジックはアプリに価値をもたらすものであり、アプリがデータを作成、保存、変更する方法を決定するルールで構成されています。

🔆
レイヤードアーキテクチャとの関連性
データ層の中でこのような設計モデルが展開されています。
image block
リポジトリ

リポジトリクラスは、主に次のタスクを行います。

  • アプリの他の部分にデータを公開する。
  • アプリの他の部分からデータソースを抽象化する。

次点でこのようなタスクも行います。

  • データの変更を一元管理する。
  • 複数のデータソース間の競合を解決する。
  • ビジネス ロジックを格納する。
データソース

データソースクラスは、以下のうち1 つのデータソースのみを処理する役割を担う必要があります。

  • 処理対象
    • ファイル
    • ネットワーク ソース
    • ローカル データベース
    • などなど…

データソースクラスは、データオペレーションのためにアプリとシステムの橋渡しをします。

モバイルアプリにおけるデータ層

データ層は、それぞれが 0 から多数のデータソースを含むことができるリポジトリで構成されています。アプリで処理するデータの種類ごとにリポジトリ クラスを作成する必要があります。

たとえば、映画に関するデータであれば MoviesRepository クラス、支払いに関するデータであれば PaymentsRepository クラスを作成します。

参考

このレイヤについて詳しくは、 データレイヤのページ をご覧ください。

💡
まとめ
  • 設計を行う上では 手入れしやすいこと が大事で、 既存のパターン を利用してみよう
  • モバイルアプリのプレゼンテーション層では、 モデル / ビュー / ビューモデル (MVVM) や、 MVPMVC といったアプリ設計が存在するよ
  • 構成によって特徴があるので、ケースバイケースで構成を検討しよう