1. ホーム
  2. アイオス

[解決済み】iOSネットワークアプリケーション(RESTクライアント)構築のための最適なアーキテクチャアプローチ【クローズド

2022-04-16 22:43:24

質問

私はiOS開発者で、ある程度の経験があります。この質問は私にとって本当に興味深いものです。このトピックに関するさまざまなリソースや資料をたくさん見ましたが、それでも私はまだ混乱しています。iOSのネットワークアプリケーションに最適なアーキテクチャは何でしょうか?基本的な抽象的なフレームワークやパターンのことで、サーバーへのリクエストが数回しかない小さなアプリでも、複雑なRESTクライアントでも、あらゆるネットワークアプリケーションに適合するものです。Appleが推奨するのは MVC は、すべてのiOSアプリケーションの基本的なアーキテクチャアプローチとして使用できますが MVC と、よりモダンな MVVM パターンは、ネットワーク・ロジック・コードをどこに置くか、そして一般的にどのように整理するかを説明します。

のようなものを開発する必要があるのでしょうか? MVCS ( S について Service ) で、この Service レイヤーにすべての API リクエストやその他のネットワーク・ロジックは、観点からは非常に複雑かもしれませんね。調べてみると、これには2つの基本的なアプローチがあることがわかりました。 これ ウェブサービスへのネットワークリクエストごとに個別のクラスを作成することが推奨されています。 API (例えば LoginRequest クラスまたは PostCommentRequest クラスなど)を継承し、これらはすべてベースリクエスト抽象クラス AbstractBaseRequest さらに、共通のネットワークコードやその他の設定をカプセル化するグローバルなネットワークマネージャーを作成します(それは、例えば AFNetworking カスタマイズや RestKit 複雑なオブジェクトマッピングや永続化、あるいは標準APIを使用した独自のネットワーク通信実装がある場合、チューニングが必要です)。しかし、この方法は私にはオーバーヘッドに思える。もうひとつの方法は、シングルトン API ディスパッチャまたはマネージャクラスは、最初のアプローチと同様です。 ただし のように、すべてのリクエストに対してクラスを作成し、代わりにこのマネージャークラスのインスタンスパブリックメソッドとしてすべてのリクエストをカプセル化することができます。 fetchContacts , loginUser メソッドなどです。では、どのような方法が最善で正しいのでしょうか?私がまだ知らない他の面白いアプローチがあるのでしょうか?

また、このようなネットワークに関する事柄のために、別のレイヤーを作成する必要があります。 Service または NetworkProvider レイヤーなどの上に MVC アーキテクチャに統合するか、あるいは、このレイヤーを既存の MVC レイヤーの例 Model ?

FacebookクライアントやLinkedInクライアントのようなモバイルモンスターは、指数関数的に増大するネットワークロジックにどのように対処しているのでしょうか?

この問題に対する正確で正式な答えがないことは承知しています。 この質問の目的は、経験豊富なiOS開発者から最も興味深いアプローチを収集することです。 . 最も良い提案されたアプローチは、受け入れられたとマークされ、評判バウンティが授与されます。これは主に理論的、研究的な質問です。私はiOSのネットワークアプリケーションのための基本的、抽象的で正しいアーキテクチャのアプローチを理解したいです。経験豊富な開発者からの詳細な説明を希望します。

どのように解決するのですか?

<ブロッククオート

iOSのネットワークアプリケーションの基本的、抽象的で正しいアーキテクチャアプローチを理解したい。

があります。 いいえ アプリケーションアーキテクチャを構築するための最良の方法、あるいは最も正しい方法は、以下のとおりです。それは 非常に クリエイティブな仕事です。しかし、私は、良いアーキテクチャと悪いアーキテクチャがあることに同意します。

とおっしゃっていましたね。

<ブロッククオート

経験豊富なiOS開発者の最も興味深いアプローチを集めています。

私のアプローチが最も面白いとか正しいとは思いませんが、いくつかのプロジェクトで使い、満足しています。この方法は、あなたが上で挙げたものと、私自身の研究努力による改良を加えたハイブリッドなアプローチです。私は、いくつかのよく知られたパターンやイディオムを組み合わせたアプローチを構築する問題に関心を持っています。私は、多くの ファウラーのエンタープライズ・パターン は、モバイルアプリケーションにうまく適用することができます。ここでは、iOSアプリケーションのアーキテクチャを作成するために適用できる、最も興味深いもののリストを紹介します ( 私見 ): サービスレイヤー , 作業単位 , リモートファサード , データ転送オブジェクト , ゲートウェイ , レイヤースーパータイプ , 特殊なケース , ドメインモデル . モデル層は常に正しく設計する必要があり、永続化についても常に忘れないようにしましょう(アプリのパフォーマンスを大幅に向上させることができます)。そのためには Core Data を使用します。しかし、あなたは はいけません。 を忘れてはいけない。 Core Data はORMでもデータベースでもなく、オブジェクトグラフマネージャであり、その良いオプションとしてパーシステンスがあります。ですから、しばしば Core Data のような新しいソリューションに目を向けることができます。 領域 Couchbase Lite または、生の SQLite または レベルDB . また、私はあなたがあなた自身に精通していることをお勧めします ドメイン駆動設計 CQRS .

最初は、私たちが すべき なぜなら、太ったコントローラや、重くて負荷のかかるモデルはいらないからです。私は、それらの fat model, skinny controller のようなものです。しかし、私は 信じる skinny everything というのも、どんなクラスも決して太ることはないからです。すべてのネットワークは一般にビジネスロジックとして抽象化することができ、その結果、それを置くことができる別のレイヤーを持つべきです。 サービス層 が必要なのです。

アプリケーションのビジネスロジックをカプセル化し、トランザクションを制御し、操作の実装においてレスポンスを調整するものです。

私たちの MVC レルム Service Layer は、ドメインモデルとコントローラの間の仲介役のようなものである。このアプローチにかなり似たバリエーションとして MVCS ここで Store は、実は私たちの Service レイヤーになります。 Store はモデルインスタンスを販売し、ネットワーキング、キャッシュなどを処理します。私が言及したいのは、あなたが はいけません。 は、ネットワークとビジネスロジックをすべてサービスレイヤーに書き込みます。これもまた、悪い設計と言えるでしょう。詳しくは 貧血 リッチ ドメインモデルです。一部のサービスメソッドやビジネスロジックはモデル内で処理できるため、"rich"(振る舞いを伴う)モデルとなります。

私はいつも2つのライブラリを広く使っています。 AFNetworking 2.0 リアクティブココア . であると思います。 必須 ネットワークやウェブサービスとやりとりする、あるいは複雑なUIロジックを含む最新のアプリケーションのために。

アーキテクチャー

まず、一般的な APIClient のサブクラスである AFHTTPSessionManager . これはアプリケーション内のすべてのネットワークの主力です。すべてのサービスクラスは、実際のRESTリクエストをこれに委譲します。これは、私が特定のアプリケーションで必要とする、HTTP クライアントのすべてのカスタマイズを含んでいます。SSL の固定化、エラー処理、そしてわかりやすい NSError オブジェクトを作成し、失敗の詳細な理由とすべての API と接続エラー (この場合、コントローラはユーザに正しいメッセージを表示することができます)、 リクエストとレスポンスのシリアライザー、http ヘッダー、その他のネットワーク関連のものを設定します。それから、すべてのAPIリクエストを論理的にサブサービスに分割します。 マイクロサービス : UserSerivces , CommonServices , SecurityServices , FriendsServices といった具合に、実装するビジネスロジックに応じて変化します。これらのマイクロサービスのそれぞれは、独立したクラスです。これらは一緒になって Service Layer . これらのクラスは、各 API リクエスト用のメソッドを含み、ドメインモデルを処理し、常に RACSignal パースされたレスポンスモデルまたは NSError を呼び出し元へ送る。

複雑なモデルシリアライゼーションロジックがある場合、そのために別のレイヤーを作成することを言及したいと思います。 データマッパー しかし、より一般的な例:JSON/XML -> Model mapper。キャッシュがある場合:それも別のレイヤー/サービスとして作成します(ビジネスロジックとキャッシュを混同してはいけません)。なぜか?正しいキャッシュ層は、それ自身のゴチャゴチャでかなり複雑になる可能性があるからです。例えば、プロファンクタに基づく投影を用いたモノイダルキャッシングのように、有効で予測可能なキャッシュを実現するために、複雑なロジックを実装する人がいます。という美しいライブラリについて読むことができます。 カルロス を使うと、より理解が深まります。また、Core Dataはキャッシュの問題を解決し、より少ないロジックを書くことを可能にすることを忘れないでください。また、もしあなたが NSManagedObjectContext とサーバーリクエストのモデルで リポジトリ これは、データを取得してエンティティモデルにマッピングするロジックと、モデル上で動作するビジネスロジックを分離するパターンです。そのため、Core DataベースのアーキテクチャであってもRepositoryパターンを使用することをお勧めします。Repositoryは次のようなものを抽象化することができる。 NSFetchRequest , NSEntityDescription , NSPredicate などのプレーンなメソッドから get または put .

サービス層でのこれらのアクションの後、呼び出し側 (ビューコントローラ) は、レスポンスに対して複雑な非同期処理を行うことができます。 ReactiveCocoa プリミティブ、または単にサブスクライブして結果をビューに表示するだけです。私は 依存性インジェクション これらのサービスクラスには、私の APIClient これは、特定のサービスコールを、対応する GET , POST , PUT , DELETE などをRESTエンドポイントにリクエストします。この場合 APIClient はすべてのコントローラに暗黙的に渡されます。 APIClient サービスクラスです。の異なるカスタマイズを使用したい場合、これは理にかなっています。 APIClient のインスタンスを使用することが確定している場合です。 APIClient - しかし、サービスクラスをシングルトンにするのはやめてください。

そして、各ビューコントローラは、再びDIを使って必要なサービスクラスをインジェクトし、適切なサービスメソッドを呼び出し、その結果をUIロジックと合成します。依存性注入のために、私は ブラッドマジック またはもっと強力なフレームワーク タイフーン . シングルトンは使わない、神 APIManagerWhatever クラスなど、間違ったものを使っています。なぜなら、もしあなたがクラスを WhateverManager これは、あなたがその目的を知らないということであり、また、それが デザイン上の悪い選択 . シングルトンもアンチパターンです。 最も のケースは(稀なものを除いて) の解決策となります。シングルトンは、以下の3つの条件をすべて満たす場合にのみ検討する必要があります。

  1. 単一インスタンスの所有権を合理的に割り当てることができない。
  2. 遅延初期化が望ましい。
  3. グローバルアクセスが他に用意されていない。

私たちの場合、単一インスタンスの所有権は問題ではなく、また、god managerをサービスに分割した後は、グローバルアクセスは必要ありません。なぜなら、現在、1つまたはいくつかの専用コントローラのみが特定のサービスを必要とするからです(例 UserProfile コントローラーのニーズ UserServices といった具合に)。

を常に尊重すべきです。 S の原則に従います。 ソリッド を使用し 懸念事項の分離 特に大規模なエンタープライズ・アプリケーションを開発する場合は、1つのクラスにすべてのサービス・メソッドとネットワーク・コールを入れてはいけないのです。特に大規模なエンタープライズ・アプリケーションを開発する場合はそうです。だからこそ、依存性注入とサービス・アプローチを考慮する必要があります。私はこのアプローチを現代的で ポストOO . この場合、アプリケーションを制御ロジック(コントローラやイベント)とパラメータの2つのパートに分割しています。

パラメータの一種は、通常の「データ」パラメータでしょう。関数に渡すもの、操作するもの、変更するもの、永続化するものなどだ。エンティティ、アグリゲート、コレクション、ケースクラスなどがこれにあたります。もうひとつは、「サービス」パラメータだ。ビジネスロジックをカプセル化し、外部システムとの通信を可能にし、データアクセスを提供するクラスである。

ここで、私のアーキテクチャの一般的なワークフローを例に挙げて説明します。例えば FriendsViewController には、ユーザーの友人のリストが表示され、友人から削除するオプションがあります。私は FriendsServices というクラスがあります。

- (RACSignal *)removeFriend:(Friend * const)friend

ここで Friend は、モデル/ドメインオブジェクト(または、単なる User オブジェクトと同じような属性を持っている場合)。このメソッドは FriendNSDictionary JSON パラメータの friend_id , name , surname , friend_request_id といった具合です。私はいつも マントル ライブラリは、この種のボイラープレートと、私のモデル層(前後方向のパース、JSONのネストされたオブジェクト階層の管理など)に使用されます。パースした後に APIClient DELETE メソッドを使用して実際の REST リクエストを行い、その結果を ResponseRACSignal を呼び出し側( FriendsViewController を使用して、ユーザーに対して適切なメッセージを表示したりすることができます。

もし私たちのアプリケーションが非常に大きなものであるなら、私たちはロジックをより明確に分ける必要があります。例えば、「リポジトリ」や「モデル」のロジックと「サービス」のロジックを混在させることは、常に良いことではありません。 私のアプローチを説明したとき、 `removeFriend` メソッドは `Service` レイヤーにあるべきと言いましたが、もっと衒学的に考えれば、それは `Repository` にあるべきということに気づくでしょう。ここで、リポジトリとは何かを思い出してみましょう。Eric Evansは彼の本[DDD]の中で正確な説明を与えています。

<ブロッククオート

リポジトリは、特定のタイプのすべてのオブジェクトを概念的なセットとして表します。より精巧なクエリ機能を除いて、コレクションのように機能する。

そのため Repository は本質的に、データ/オブジェクトへのアクセスを提供するために、Collectionスタイルのセマンティクス(Add、Update、Remove)を使用するファサードです。そのため、次のようなものがある場合 getFriendsList , getUserGroups , removeFriend に配置することができます。 Repository というのも、ここではコレクション的なセマンティクスがかなり明確になっているからです。そして、こんなコード。

- (RACSignal *)approveFriendRequest:(FriendRequest * const)request;

は間違いなくビジネスロジックであり、基本的な CRUD の操作で、2つのドメイン・オブジェクト ( FriendRequest ) に配置する必要があるのはそのためです。 Service レイヤーを作成します。あと、気がつきたいのは 不必要な抽象化をしない . これらのアプローチはすべて賢く使ってください。なぜなら、もしあなたが抽象化でアプリケーションを圧倒してしまうなら、それは 増加 偶発的な複雑さ、そして複雑さ より多くの問題を引き起こす ソフトウェアシステムにおいて、他の何よりも

私は、古いObjective-Cの例を説明しましたが、このアプローチは、より多くの便利な機能と機能的な砂糖を持っているので、より多くの改善でSwift言語に非常に簡単に適応させることができます。私はこのライブラリを使うことを強くお勧めします。 モヤモヤ . これを使うと、よりエレガントな APIClient 層(覚えているように、私たちの主力商品です)。さて、私たちの APIClient プロバイダは、プロトコルに準拠した拡張機能を持つ値型(enum)となり、デストラクチャリング・パターンマッチングを活用する。Swift の列挙型とパターンマッチングによって 代数的データ型 は、古典的な関数型プログラミングのように 私たちのマイクロサービスでは、この改良された APIClient プロバイダーは、通常のObjective-Cのアプローチと同様です。モデル層では Mantle を使用することができます。 ObjectMapper ライブラリ または、よりエレガントで機能的な アルゴ ライブラリです。

というわけで、私の一般的なアーキテクチャのアプローチを説明しましたが、これはどんなアプリケーションにも適用できるものだと思います。もちろん、もっと多くの改良が可能です。関数型プログラミングを学ぶことをお勧めします。なぜなら、関数型プログラミングから多くの恩恵を受けることができるからです。過剰な、共有された、グローバルなミュータブルステートを排除すること。 不変のドメインモデル を作成したり、外部副作用のない純粋な関数を作成することは、一般的に良い習慣であり、新しい Swift はこれを推奨しています。しかし、純粋な関数パターンやカテゴリ理論的なアプローチでコードをオーバーロードすることは、常に念頭に置いてください。 悪い というのも その他 開発者はあなたのコードを読み、サポートし、その不満や恐ろしさを prismatic profunctors などが、イミュータブルモデルに含まれています。同じことが ReactiveCocoa を使わないでください。 RACify あなたのコード あまりに 特に初心者は、すぐに読めなくなってしまうからです。目標やロジックを本当に単純化できるときに使ってください。

だから、たくさん読んで、混ぜて、実験して、さまざまなアーキテクチャのアプローチからベストなものをピックアップするようにしましょう。それが、私ができる最善のアドバイスです。