私は経験のある iOS 開発者ですが、この質問は私にとって非常に興味深いものです。このトピックに関するさまざまなリソースや資料を見ましたが、それでもまだ混乱しています。iOS ネットワーク アプリケーションに最適なアーキテクチャとはどのようなものでしょうか。サーバー要求が少数の小さなアプリでも、複雑な REST クライアントでも、あらゆるネットワーク アプリケーションに適合する基本的な抽象フレームワーク、パターンのことです。Apple は、すべてMVC
の iOS アプリケーションに基本的なアーキテクチャ アプローチとして使用することを推奨していますが、MVC
最新のMVVM
パターンでも、ネットワーク ロジック コードをどこに配置するか、また、一般的にどのように整理するかについては説明されていません。
MVCS
( S
for Service
) のようなものを開発し、このService
レイヤーにすべてのリクエストとその他のネットワーク ロジックを配置する必要がありますかAPI
? 全体的に見ると非常に複雑になる可能性があります。調査を行った結果、これに対する 2 つの基本的なアプローチが見つかりました。ここWeb サービスへのネットワーク要求ごとに個別のクラスAPI
(LoginRequest
クラスやPostCommentRequest
クラスなど) を作成し、これらはすべて基本要求抽象クラスから継承しAbstractBaseRequest
、さらに共通のネットワーク コードやその他の設定をカプセル化するグローバル ネットワーク マネージャーを作成することが推奨されました (複雑なオブジェクト マッピングと永続性がある場合や、標準 API を使用した独自のネットワーク通信実装がある場合、AFNetworking
カスタマイズやチューニングが必要になることがありますRestKit
)。しかし、このアプローチは私にとってオーバーヘッドのように思えます。もう 1 つのアプローチは、API
最初のアプローチのようにシングルトン ディスパッチャーまたはマネージャー クラスを用意することですが、要求ごとにクラスを作成するのではなく、すべての要求をこのマネージャー クラスのインスタンスのパブリック メソッド ( fetchContacts
、loginUser
メソッドなど) としてカプセル化します。では、最良かつ正しい方法は何でしょうか。私がまだ知らない興味深いアプローチはありますか。
そして、アーキテクチャの上にService
、 、などのネットワーク関連のレイヤーすべてに対して別のレイヤーを作成する必要がありますか、それともこのレイヤーを既存のレイヤーに統合 (挿入) する必要がありますか?NetworkProvider
MVC
MVC
Model
美しいアプローチが存在することは知っていますが、では Facebook クライアントや LinkedIn クライアントのようなモバイル モンスターは、ネットワーク ロジックの指数関数的に増大する複雑さにどのように対処するのでしょうか?
この問題に対する正確で正式な答えがないことはわかっています。この質問の目的は、経験豊富な iOS 開発者から最も興味深いアプローチを集めることです。提案された最も優れたアプローチは承認済みとしてマークされ、評判の報奨金が授与され、その他のアプローチは賛成されます。これは主に理論と研究に関する質問です。iOS のネットワーク アプリケーションの基本的、抽象的、正しいアーキテクチャ アプローチを理解したいです。経験豊富な開発者からの詳細な説明を期待しています。
ベストアンサー1
iOSのネットワークアプリケーションの基本的かつ抽象的で正しいアーキテクチャアプローチを理解したい
アプリケーション アーキテクチャを構築するには、「最良」または「最も正しい」アプローチはありません。これは非常に創造的な仕事です。常に、プロジェクトに取り組み始める開発者やチーム内の他の開発者にとって明らかな、最も単純で拡張可能なアーキテクチャを選択する必要がありますが、アーキテクチャには「良い」ものと「悪い」ものがあることには同意します。
あなたが言った:
経験豊富なiOS開発者からの最も興味深いアプローチを集める
私のアプローチが最も興味深い、または正しいとは思いませんが、いくつかのプロジェクトで使用し、満足しています。これは、あなたが上で述べたアプローチと、私自身の研究努力による改良を組み合わせたハイブリッドアプローチです。私は、いくつかのよく知られたパターンとイディオムを組み合わせたアプローチを構築する問題に興味があります。ファウラーの企業パターンモバイル アプリケーションにうまく適用できます。以下は、iOS アプリケーション アーキテクチャの作成に適用できる最も興味深いものの一覧です (私の意見では)。サービス層、作業単位、リモートファサード、データ転送オブジェクト、ゲートウェイ、レイヤースーパータイプ、特別なケース、ドメインモデル常にモデル層を正しく設計し、永続性を忘れないようにしてください(これにより、アプリのパフォーマンスが大幅に向上します)。Core Data
を使用できます。ただし、これはORMやデータベースではなく、永続性を備えたオブジェクトグラフマネージャーであることを忘れないでください。そのため、多くの場合、ニーズに対して重すぎる可能性があり、次のような新しいソリューションを検討できます。Core Data
Core Data
レルムそしてカウチベース ライト、または生のSQLiteまたはレベルDBまた、以下の点にも注意することをお勧めします。ドメイン駆動設計そしてCQRS。
まず、ネットワーク用に別のレイヤーを作成する必要があると思います。なぜなら、コントローラが肥大化したり、モデルが重くなったり、負荷がかかったりするのは望ましくないからですfat model, skinny controller
。私はそういったことは信じていません。しかし、クラスが肥大化することは絶対にあってはならないので、このアプローチは信じていますskinny everything
。すべてのネットワークは一般にビジネス ロジックとして抽象化できるため、ネットワークを配置できる別のレイヤーが必要です。サービス層私たちに必要なのは:
アプリケーションのビジネス ロジックをカプセル化し、トランザクションを制御し、操作の実装における応答を調整します。
私たちのMVC
領域にはService Layer
、ドメインモデルとコントローラの間の仲介者のようなものがあります。このアプローチに似たバリエーションとして、MVCSここで、aはStore
実際にはService
レイヤーです。モデルインスタンスを販売し、ネットワーク、キャッシュなどを処理します。ネットワークとビジネスロジックをすべてサービスレイヤーに記述しないStore
でください。これも悪い設計と見なされます。詳細については、貧血そしてリッチドメイン モデル。一部のサービス メソッドとビジネス ロジックをモデル内で処理できるため、これは「リッチな」(動作を備えた) モデルになります。
私は常に 2 つのライブラリを頻繁に使用します。AFネットワーキング 2.0そしてリアクティブココアネットワークや Web サービスとやり取りしたり、複雑な UI ロジックを含む最新のアプリケーションには必須だと思います。
建築
まず、APIClient
サブクラスである一般クラスを作成します。AFHTTPセッションマネージャーこれは、アプリケーション内のすべてのネットワークの主力です。すべてのサービス クラスは、実際の REST 要求をこれに委任します。これには、特定のアプリケーションで必要な HTTP クライアントのすべてのカスタマイズが含まれています。SSL ピン留め、エラー処理、NSError
すべての詳細な失敗理由と説明を含む簡単なオブジェクトの作成API
(このような場合、コントローラーはユーザーに正しいメッセージを表示できます)、要求と応答のシリアライザーの設定、HTTP ヘッダー、その他のネットワーク関連のもの。次に、すべての API 要求を論理的にサブサービスに分割します。より正確には、マイクロサービス: UserSerivces
、、CommonServices
など、実装するビジネス ロジックに応じて、マイクロサービスSecurityServices
がFriendsServices
存在します。これらのマイクロサービスはそれぞれ別のクラスです。これらが一緒になって を形成しますService Layer
。これらのクラスには、各 API 要求のメソッドが含まれており、ドメイン モデルを処理し、常にRACSignal
解析された応答モデルまたはNSError
呼び出し元とともに を返します。
複雑なモデルシリアル化ロジックがある場合は、別のレイヤーを作成することをお勧めします。たとえば、データマッパーしかし、より一般的な例、JSON/XML -> モデルマッパー。キャッシュがある場合は、それを別のレイヤー/サービスとして作成します(ビジネスロジックとキャッシュを混在させないでください)。なぜでしょうか?正しいキャッシュレイヤーは、独自の落とし穴があり、非常に複雑になる可能性があるためです。人々は、プロファンクタに基づく射影によるモノイドキャッシュなど、有効で予測可能なキャッシュを得るために複雑なロジックを実装します。この美しいライブラリについては、以下を参照してください。カルロス理解を深めるために、Core Data がキャッシュの問題の解決に非常に役立ち、記述するロジックを少なくできることも忘れないでください。また、NSManagedObjectContext
サーバーリクエストモデルとモデルの間にロジックがある場合は、リポジトリパターンは、データを取得してエンティティ モデルにマップするロジックを、モデルで動作するビジネス ロジックから分離します。そのため、Core Data ベースのアーキテクチャを使用している場合でも、リポジトリ パターンを使用することをお勧めします。リポジトリは、、などをNSFetchRequest
、NSEntityDescription
またはなどNSPredicate
の単純なメソッドに抽象化できます。get
put
サービス層でこれらすべてのアクションを実行した後、呼び出し元(ビューコントローラ)は、プリミティブの助けを借りて、シグナル操作、チェーン、マッピングなどの複雑な非同期処理を応答に対して実行したりReactiveCocoa
、単にサブスクライブしてビューに結果を表示したりできます。依存性注入これらすべてのサービス クラスでAPIClient
、特定のサービス呼び出しを対応するGET
、POST
、PUT
などのリクエストに変換してDELETE
REST エンドポイントに送信します。 この場合、APIClient
はすべてのコントローラーに暗黙的に渡されますが、APIClient
サービス クラス全体にパラメーター化された を使用してこれを明示的にすることができます。 これは、特定のサービス クラスに対して の異なるカスタマイズを使用する場合には意味がありますAPIClient
が、何らかの理由で余分なコピーが必要ない場合、または の特定のインスタンスを常に (カスタマイズなしで) 使用する場合は、それをAPIClient
シングルトンにします。ただし、サービス クラスをシングルトンにしないでください。
そして、各ビューコントローラはDIを使用して必要なサービスクラスを注入し、適切なサービスメソッドを呼び出し、その結果をUIロジックと組み合わせます。依存性注入には、ブラッドマジックあるいはより強力なフレームワーク台風私はシングルトンやGodAPIManagerWhatever
クラスなどの間違ったものは決して使用しません。なぜなら、クラスを呼び出すとWhateverManager
、その目的を知らないことになり、悪いデザイン選択シングルトンもアンチパターンであり、ほとんどの場合 (まれな場合を除く)間違った解決策です。シングルトンは、次の 3 つの条件がすべて満たされている場合にのみ検討する必要があります。
- 単一インスタンスの所有権を合理的に割り当てることができない。
- 遅延初期化が望ましいです。
- それ以外の場合、グローバル アクセスは提供されません。
私たちの場合、単一インスタンスの所有権は問題ではなく、また、god マネージャーをサービスに分割した後は、グローバル アクセスも必要ありません。これは、特定のサービス (UserProfile
コントローラーのニーズUserServices
など) を必要とするのは 1 つまたは複数の専用コントローラーのみであるためです。
私たちは常にS
原則を尊重すべきです固体使用して関心事の分離なので、サービスメソッドとネットワーク呼び出しをすべて1つのクラスにまとめないでください。特に大規模なエンタープライズアプリケーションを開発する場合は、それはおかしいです。そのため、依存性注入とサービスアプローチを検討する必要があります。私はこのアプローチを現代的でポストOOこの場合、アプリケーションを制御ロジック (コントローラーとイベント) とパラメーターの 2 つの部分に分割します。
パラメータの 1 つは、通常の「データ」パラメータです。これは、関数を渡したり、操作、変更、保持などを行うものです。これらは、エンティティ、集約、コレクション、ケース クラスです。もう 1 つの種類は「サービス」パラメータです。これらは、ビジネス ロジックをカプセル化し、外部システムとの通信を可能にし、データ アクセスを提供するクラスです。
FriendsViewController
これは、例として私のアーキテクチャの一般的なワークフローです。ユーザーの友達リストを表示し、友達から削除するオプションがある があるとします。FriendsServices
クラスに次のメソッドを作成します。
- (RACSignal *)removeFriend:(Friend * const)friend
ここで、はモデル/ドメインオブジェクトです(または、同様の属性を持つ場合はFriend
単なるオブジェクトでもかまいません)。このメソッドは、内部的にJSONパラメータ、、などを解析します。私は常にUser
Friend
NSDictionary
friend_id
name
surname
friend_request_id
マントルこの種の定型文とモデル レイヤー用のライブラリ (前後の解析、JSON 内のネストされたオブジェクト階層の管理など)。解析後、メソッドを呼び出してAPIClient
DELETE
実際の REST 要求を行い、呼び出し元 (このケース) にResponse
戻って、ユーザーに適切なメッセージを表示するなどします。RACSignal
FriendsViewController
アプリケーションが非常に大きい場合は、ロジックをさらに明確に分離する必要があります。たとえば、`Repository` またはモデル ロジックを `Service` ロジックと混在させることは、必ずしも良いことではありません。私のアプローチについて説明したときに、`removeFriend` メソッドは `Service` レイヤーにあるべきだと言いましたが、もっと細かく見てみると、`Repository` に属する方がよいことがわかります。Repository とは何かを思い出してみましょう。Eric Evans は著書 [DDD] で、これについて正確な説明をしています。
リポジトリは、特定のタイプのすべてのオブジェクトを概念的なセットとして表します。より複雑なクエリ機能があることを除けば、コレクションのように動作します。
つまり、 はRepository
本質的には、データ/オブジェクトへのアクセスを提供するためにコレクション スタイルのセマンティクス (追加、更新、削除) を使用するファサードです。 、 のようなものがある場合、コレクションのようなセマンティクスがここではかなり明確であるため、getFriendsList
に配置できるのはそのためです。 そして、次のようなコード:getUserGroups
removeFriend
Repository
- (RACSignal *)approveFriendRequest:(FriendRequest * const)request;
CRUD
は、基本的な操作を超えて2つのドメインオブジェクト(Friend
および)を接続するため、間違いなくビジネスロジックです。Request
そのため、レイヤーに配置する必要があります。また、不必要な抽象化を作成しないService
ことにも注意してください。これらのアプローチはすべて賢く使用してください。抽象化でアプリケーションを圧倒すると、偶発的な複雑さが増し、複雑さが増すためです。さらなる問題を引き起こすソフトウェアシステムでは他の何よりも
ここでは「古い」Objective-C の例を取り上げましたが、このアプローチは、より便利な機能と関数型糖衣を備えているため、多くの改良を加えて Swift 言語に簡単に適応できます。このライブラリの使用を強くお勧めします。モヤAPIClient
よりエレガントなレイヤー(覚えている通り、私たちの主力)を作成できます。これで、APIClient
プロバイダーはプロトコルに準拠した拡張とデストラクチャリングパターンマッチングを活用した値型(列挙型)になります。Swiftの列挙型とパターンマッチングにより、次のようなものを作成できます。代数データ型古典的な関数型プログラミングのように。マイクロサービスでは、APIClient
通常のObjective-Cアプローチと同様に、この改良されたプロバイダを使用します。モデル層では、代わりMantle
にObjectMapper ライブラリまたは、よりエレガントで機能的なものを使いたいアルゴ図書館。
ということで、私は一般的なアーキテクチャアプローチを説明しましたが、これはどんなアプリケーションにも適応できると思います。もちろん、もっと多くの改善点があります。関数型プログラミングを学ぶことをお勧めします。多くのメリットが得られるからです。ただし、やりすぎには注意してください。過剰な共有グローバル可変状態を排除し、不変ドメインモデルまたは、外部副作用のない純粋な関数を作成することは、一般的に良い習慣であり、新しいSwift
言語はこれを奨励しています。しかし、常に覚えておいてください。コードを純粋な関数パターンやカテゴリ理論的アプローチでオーバーロードすることは悪い考えです。他の開発者があなたのコードを読んでサポートし、不変モデル内の などの種類のものにイライラしたり恐れたりする可能性があるためですprismatic profunctors
。 についても同じことが言えますReactiveCocoa
。RACify
コードを過度になぜなら、特に初心者にとっては、すぐに読みにくくなる可能性があるからです。目標とロジックを本当に簡素化できる場合にのみ使用してください。
ですから、たくさん読んで、組み合わせて、実験して、さまざまなアーキテクチャのアプローチから最良のものを選ぶようにしてください。それが私があなたに与えられる最良のアドバイスです。