[解決済み】iOSネットワークアプリケーション(RESTクライアント)構築のための最適なアーキテクチャアプローチ【クローズド
質問
私は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つの条件をすべて満たす場合にのみ検討する必要があります。
- 単一インスタンスの所有権を合理的に割り当てることができない。
- 遅延初期化が望ましい。
- グローバルアクセスが他に用意されていない。
私たちの場合、単一インスタンスの所有権は問題ではなく、また、god managerをサービスに分割した後は、グローバルアクセスは必要ありません。なぜなら、現在、1つまたはいくつかの専用コントローラのみが特定のサービスを必要とするからです(例
UserProfile
コントローラーのニーズ
UserServices
といった具合に)。
を常に尊重すべきです。
S
の原則に従います。
ソリッド
を使用し
懸念事項の分離
特に大規模なエンタープライズ・アプリケーションを開発する場合は、1つのクラスにすべてのサービス・メソッドとネットワーク・コールを入れてはいけないのです。特に大規模なエンタープライズ・アプリケーションを開発する場合はそうです。だからこそ、依存性注入とサービス・アプローチを考慮する必要があります。私はこのアプローチを現代的で
ポストOO
. この場合、アプリケーションを制御ロジック(コントローラやイベント)とパラメータの2つのパートに分割しています。
パラメータの一種は、通常の「データ」パラメータでしょう。関数に渡すもの、操作するもの、変更するもの、永続化するものなどだ。エンティティ、アグリゲート、コレクション、ケースクラスなどがこれにあたります。もうひとつは、「サービス」パラメータだ。ビジネスロジックをカプセル化し、外部システムとの通信を可能にし、データアクセスを提供するクラスである。
ここで、私のアーキテクチャの一般的なワークフローを例に挙げて説明します。例えば
FriendsViewController
には、ユーザーの友人のリストが表示され、友人から削除するオプションがあります。私は
FriendsServices
というクラスがあります。
- (RACSignal *)removeFriend:(Friend * const)friend
ここで
Friend
は、モデル/ドメインオブジェクト(または、単なる
User
オブジェクトと同じような属性を持っている場合)。このメソッドは
Friend
を
NSDictionary
JSON パラメータの
friend_id
,
name
,
surname
,
friend_request_id
といった具合です。私はいつも
マントル
ライブラリは、この種のボイラープレートと、私のモデル層(前後方向のパース、JSONのネストされたオブジェクト階層の管理など)に使用されます。パースした後に
APIClient
DELETE
メソッドを使用して実際の REST リクエストを行い、その結果を
Response
で
RACSignal
を呼び出し側(
FriendsViewController
を使用して、ユーザーに対して適切なメッセージを表示したりすることができます。
もし私たちのアプリケーションが非常に大きなものであるなら、私たちはロジックをより明確に分ける必要があります。例えば、「リポジトリ」や「モデル」のロジックと「サービス」のロジックを混在させることは、常に良いことではありません。 私のアプローチを説明したとき、 `removeFriend` メソッドは `Service` レイヤーにあるべきと言いましたが、もっと衒学的に考えれば、それは `Repository` にあるべきということに気づくでしょう。ここで、リポジトリとは何かを思い出してみましょう。Eric Evansは彼の本[DDD]の中で正確な説明を与えています。
<ブロッククオートリポジトリは、特定のタイプのすべてのオブジェクトを概念的なセットとして表します。より精巧なクエリ機能を除いて、コレクションのように機能する。
そのため
Repository
は本質的に、データ/オブジェクトへのアクセスを提供するために、Collectionスタイルのセマンティクス(Add、Update、Remove)を使用するファサードです。そのため、次のようなものがある場合
getFriendsList
,
getUserGroups
,
removeFriend
に配置することができます。
Repository
というのも、ここではコレクション的なセマンティクスがかなり明確になっているからです。そして、こんなコード。
- (RACSignal *)approveFriendRequest:(FriendRequest * const)request;
は間違いなくビジネスロジックであり、基本的な
CRUD
の操作で、2つのドメイン・オブジェクト (
Friend
と
Request
) に配置する必要があるのはそのためです。
Service
レイヤーを作成します。あと、気がつきたいのは
不必要な抽象化をしない
. これらのアプローチはすべて賢く使ってください。なぜなら、もしあなたが抽象化でアプリケーションを圧倒してしまうなら、それは
増加
偶発的な複雑さ、そして複雑さ
より多くの問題を引き起こす
ソフトウェアシステムにおいて、他の何よりも
私は、古いObjective-Cの例を説明しましたが、このアプローチは、より多くの便利な機能と機能的な砂糖を持っているので、より多くの改善でSwift言語に非常に簡単に適応させることができます。私はこのライブラリを使うことを強くお勧めします。
モヤモヤ
. これを使うと、よりエレガントな
APIClient
層(覚えているように、私たちの主力商品です)。さて、私たちの
APIClient
プロバイダは、プロトコルに準拠した拡張機能を持つ値型(enum)となり、デストラクチャリング・パターンマッチングを活用する。Swift の列挙型とパターンマッチングによって
代数的データ型
は、古典的な関数型プログラミングのように 私たちのマイクロサービスでは、この改良された
APIClient
プロバイダーは、通常のObjective-Cのアプローチと同様です。モデル層では
Mantle
を使用することができます。
ObjectMapper ライブラリ
または、よりエレガントで機能的な
アルゴ
ライブラリです。
というわけで、私の一般的なアーキテクチャのアプローチを説明しましたが、これはどんなアプリケーションにも適用できるものだと思います。もちろん、もっと多くの改良が可能です。関数型プログラミングを学ぶことをお勧めします。なぜなら、関数型プログラミングから多くの恩恵を受けることができるからです。過剰な、共有された、グローバルなミュータブルステートを排除すること。
不変のドメインモデル
を作成したり、外部副作用のない純粋な関数を作成することは、一般的に良い習慣であり、新しい
Swift
はこれを推奨しています。しかし、純粋な関数パターンやカテゴリ理論的なアプローチでコードをオーバーロードすることは、常に念頭に置いてください。
悪い
というのも
その他
開発者はあなたのコードを読み、サポートし、その不満や恐ろしさを
prismatic profunctors
などが、イミュータブルモデルに含まれています。同じことが
ReactiveCocoa
を使わないでください。
RACify
あなたのコード
あまりに
特に初心者は、すぐに読めなくなってしまうからです。目標やロジックを本当に単純化できるときに使ってください。
だから、たくさん読んで、混ぜて、実験して、さまざまなアーキテクチャのアプローチからベストなものをピックアップするようにしましょう。それが、私ができる最善のアドバイスです。
関連
-
[解決済み] Xcode 10でコマンドCompileSwiftが0以外の終了コードで失敗する [重複] 。
-
[解決済み] 警告: 'characters' は非推奨です。String または Substring を直接使用してください。
-
[解決済み] Xcode 9の問題を修正しました。"iPhoneはビジー状態です。iPhoneのデバッガーサポートを準備中"
-
[解決済み] 基本的なUIButtonをプログラムで作成するには?
-
[解決済み] なぜSwiftでUIViewControllerのデフォルトのsuper.init()を呼び出すことができないのでしょうか?
-
[解決済み] CFBundleVersionとCFBundleShortVersionStringには、どのような値を使用すればよいですか?
-
[解決済み] Xcode 6: iOS 8.1 The Developer Disk Imageをマウントできませんでした。
-
[解決済み] Objective C - 値で渡す、参照で渡す
-
[解決済み] Xcode 12、iOS Simulator用にビルドしても、iOS用にビルドされたオブジェクトファイルでは、アーキテクチャ「arm64」用にリンクされます。
-
[解決済み] セキュリティで保護されたWebサービスにもアクセスするiOSアプリで、Facebook認証を行うためのデザイン
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】iOS 9.0より前のUIStackView
-
[解決済み] Xcodeエラー "Could not find Developer Disk Image" が発生する。
-
[解決済み] "CFNetwork SSLHandshake failed (-9806)" の解決方法について
-
[解決済み] iPhoneが使用できません。デバイスを再接続してください
-
[解決済み] MacOSとXcodeをアップデートすると、Xcode 8.1の「No matching provisioning profiles found」が表示される。
-
[解決済み] Swiftを使って音を鳴らすには?
-
[解決済み] このアクションは完了できませんでした。再試行 (-22421)
-
[解決済み] ERROR ITMS-9000: "Redundant Binary Upload. 列車 '1.0' のビルドバージョン '1.0' のバイナリアップロードが既に存在します" と表示されました。
-
[解決済み] 配列型 'int [16]' は代入不可能です。
-
[解決済み] なぜSwiftでUIViewControllerのデフォルトのsuper.init()を呼び出すことができないのでしょうか?