モバイル アプリ開発者であれば、おそらく、レイアウトやロジックをその場で変更できるオンライン開発の柔軟性や、仮説テストを数秒で実行して結果をさらに速く処理できることを夢見たことがあるでしょう。
モバイル開発者は、アプリケーションがリリースおよび更新されるスピードが、ユーザーに届く速さに直接関係していると教えられるものです。App Store のモデレーション時間は、イライラするほど長くなることがあります。ソフトウェア開発キット (SDK) の構築は、他の誰かの製品開発およびリリース サイクルに自分のニーズを合わせなければならないため、さらに時間がかかります。
このブログの記事では、このトピックについて説明し、Backend-Driven Developmentのステップを通して、特定の問題を解決するためにどのように使用され、どのような利益をもたらしたかについて説明します。この投稿の資料は、MovilePayの例に関するソースから引用したものです。原文の著者はRodrigo Maximoです。
バックエンド駆動開発とは
バックエンド駆動開発(またはバックエンド駆動開発、またはバックエンド駆動UI、またはサーバー駆動UI)とは、サーバー応答に基づいてフロントエンド アプリケーションを開発するという概念です。
モバイル アプリケーションを作成する作業の大部分は、通常、ユーザー インターフェイスの構築と関連しており、ユーザーが 1 つまたは別のアクションをすばやく実行できるように、ユーザーが操作する要素を画面上に配置します。
画面を実装する従来のアプローチには、アプリケーション側に多くのビジネス ロジックがあり、コードの保守がより困難になるという欠点があります:
- 多くのプラットフォーム (Android、iOS、Web など) で同じコードが必要になります。このアプローチでは、クロスプラットフォームの互換性を維持することが難しく、バグや非互換性の可能性が高まります。
- モバイルアプリケーションの各アップデートまたは修正は、コードの変更の必要性を意味し、これは、App Storeでのアプリケーションのリリースが延長されることにつながります。コンセプトをテストし、重要な製品情報を理解するためにユーザーからデータを収集することはより困難です。
- ほとんどのビジネス ロジックがアプリケーション側にあるため、コードの保守と維持がより困難です。
バックエンド指向の開発はこれらの問題を解決するために登場しました。
次のシナリオ(サンプル iOS アプリに基づいていますが、他のフロントエンド プロジェクトに容易に翻訳できます)を考えてみてください:
バックエンド指向の開発は、バックエンドの開発から派生したものです。apms.io/api/_files/MzJ6ixGg2KXjvtwxPaUpZQ/download/" title="Backend driven development scenario" width="268" height="535" alt="Scenario for backend driven development" data-mce-src="https://ws.apms.io/api/_files/MzJ6ixGg2KXjvtwxPaUpZQ/download/">
{" width="280">{" width="280">"pageTitle": "Demonstrative Title", "pageTitle":「ボックス」: [] 「ボックス」。 { です。 "type":"ビッグブルー",。 「タイトル": "ナイスボックス",. "サブタイトル": "サブタイトル of box" },,? { です。 "type":"smallRed", : "type: "". "タイトル": "グレートボックス" },,? { です。 "type":"rectangleGreen",. "title": "Incredible box",. 「サブタイトル": "サブタイトル of box",. "number": 10. }。 ] のようになります。 }フィールドの表示やテキストおよび視覚情報を表示するためのすべてのビジネス ロジックは、サーバー側で統合および抽象化されており、この API を処理するための複数のインターフェースは不要になります。
次にサーバーはビジネスロジックを適用し、その結果を使用してJSON形式のAPIレスポンスを生成します。
この例では、画面上にブロック(「bigBlue」、「smallRed」、「rectangleGreen」)を表示するのに、各フィールドからの追加の情報を使用しています。興味深いことに、「box」変数により、バックエンドはサーバーが与えるだけのブロックを処理できます。
おそらく、この状況でA / Bテストを実施する方法をすでに推測していますか?サーバーは、特定のユーザーを選択して「bigBlue」ブロックのみを表示し、他のユーザーは3種類すべてのブロックを表示することができます。また、画面上のブロックの表示順序を変更する A / B テストを実施できます。
サーバー駆動型開発のもう 1 つの使用例は、アプリケーションのインターフェイスに変更を加えることです。たとえば、アプリケーションのヘッダーを変更する必要がある場合、サーバーの応答を単純に変更するだけで十分です。また、サブタイトルやブロックの表示順も簡単に変更することができます。この場合、新しいアプリケーションのリリースをAppStoreで公開する必要はありません。
{ "pageTitle": "Demonstrative Title", "pageTitle":「ボックス」: [] 「ボックス」。 { です。 "type":"rectangleGreen", . 「タイトル": "別のタイトル", 。 「サブタイトル": "別のサブタイトル", 。 "number": 100。 },,? { です。 "type":"smallRed", : "type: "". "タイトル": "異なるタイトル" }。 {. "type":"bigBlue",. "title": "Backend Driven", : "バックエンド駆動"。 「サブタイトル": "開発" }] のようになります。 }Think twice
Backend Driven Development のアプローチを使用する際には、いくつか注意すべきことがあります。
経験と研究に基づき、私たちは中程度の柔軟性が最適であると考えています。
極端から極端に急ぐべきではありません。大きな自由度は、開発の投資回収にマイナスの影響を与える可能性があります。特に、デバイスの仕様や画面サイズはコントロールできないので、すべてを予見することはできません。最終的には、アプリケーション側のプレゼンテーションロジックが非常に混乱し、過負荷になります。また、デザインチームが将来やりたいと思うことをすべて予測することはできません。
したがって、結論として、ほとんどの場合、HTML が持っている柔軟性は必要ありません。したがって、Backend-Driven Development のアイデアを使用した画面やサーバー ソリューションを開発する前に、すべての可能な選択肢を考え抜くことをお勧めします。
スーパー アプリの作成、Movile Tech の事例
みなさんはおそらくスーパー アプリという概念についてよく知っており、WeChat などのアプリについて聞いたことがあると思います。WeChat は中国で開発されたモバイル メッセージング プラットフォームおよびソーシャル ネットワークで、世界中で非常に人気があります。
このようなアプリケーションは、いくつかの定期的なサービスを 1 か所に集め、日常生活のほとんどのオンライン照会へのアクセスのシングル ポイントをユーザーに提供することを目的としています。それはソフトウェア開発の聖杯であり、多くの機能をまとめて1つのように見せ、複雑な質問に対する簡単な答えや大きな問題に対する解決策をユーザーに提供します。
過去に、MovilePayはスーパーアプリの開発に携わりましたが、その改良版の開発プロセスはこの記事で説明されています。それは、チームが新しいサービスをテストし、その使用における共通点を見つけることができるテストおよび検証センターとなるはずでした。
MovilePay には、コードにすばやく変更を加えることができるアプリケーションを作成するという問題がありました。それは、多くのサービスとサービスをテストし、表示された情報をもとにユーザーの行動に関する調査を行い、仮説を検証する必要がありました。MovilePayは、機能のオン・オフを迅速に行えるようにしたいと考えていました。このような条件下では、変更のたびにリリースを行う従来のやり方では無理があったのです。
MovilePay はこの問題を解決するために、カテゴリベースのホーム画面を作成しました。各セクションには、ウィジェットと呼ばれる独自のアイテムのセットがあります。
セクションは、すでに実装されているサービスのエントリーポイントを任意の順番で表示し、個々のウィジェットを強調表示しました。
あるときは、1つのサービスだけが表示され、あるときは、現在テスト中のものによって、3つのサービスが表示されました。MovilePayは、アプリケーションにルールをハードコーディングするのではなく、サーバーに選択を委ねました。その結果、アプリケーションは、サービスのエントリポイントの定義と、各特定のスタイルをレンダリングする方法のみを知っています。そのサーバーは、どのサービスがどのような順序で生成されるべきかを伝えます。以下は、MovilePayアプリケーションが表示できるいくつかのウィジェットです。
そこで、適応性が高いホーム画面を作るために、バックエンドから、それぞれがウィジェット一覧を含むセクション一覧のレスポンスを取得しなければなりませんでした。以下は、MovilePayアプリケーションが解析し、以下の例のようなホーム画面を作成すべきJSONレスポンスの例です。
[ { (トークン句読点)。 "title": "Section 1", "セクション1",:: "widgets": [] ウィジェット。 { (トークン句読点)。 "identifier": "COLLECTION_WIDGET", "identifier":"content": [) { (トークン句読点)。 "title": "Title A", "title A", "title B" "image": "A", "画像": "color": "yellow" "color" : },,? { (英語)。 "title": "Title B", "タイトル" : "image": "B", "B",,。 "color": "blue" "color" : },,? { です。 "title": "Title C", "タイトル", : "image": "C", "image"。 "color": "red" "color" : },,? { (英語)。 "title": "Title D", 。 "image": "D", "D": "image" "color": "purple" "color" : },,? { (英語)。 "title": "Title E", "title" : "title E" "image": "E", "画像": "画像" "color": "green" "color" : }。 ] のようになります。 },,? {... "identifier": "IMAGES_WIDGET", : "unknown""content": [) { (トークン句読点)。 "image": "Image", "Image": "image" "color": "green" "color" : },,? { (英語)。 "image": "Image", "image" "color": "blue" "color" : },,? { です。 "image": "Image", "image" "color": "orange" "color" }。 ] のようになります。 },,? {... "identifier": "COLLECTION_WIDGET",は、"識別子 "です。 "content": [) { (トークン句読点)。 "title": "Title E", "タイトル": "タイトルE" "image": "E", "画像": "画像" "color": "green" "color" : },,? { (英語)。 "title": "Title F", "タイトル" "image": "F", "image"。 "color": "purple" "color" : },,? { (英語)。 "title": "Title G", "タイトル", : "image": "G", "G": "G" "color": "red" "color" : },,? { (英語)。 "title": "Title H", "タイトル" : "image": "H", 画像。 "color": "blue" 。 },,? {. "title": "Title H", "タイトル" : "image": "H", 画像。 "color": "yellow" "color" : }。 ] のようになります。 } となります。 ] となります。 },,? {... "title": "Section 2", "タイトル" : "widgets": [] ウィジェット。 { (トークン句読点)。 "identifier": "CELLS_WIDGET", "identifier": "identifier""content": [) { (トークン句読点)。 "title": "Cell 1", "Cell 2"は、"title"。 "color": "red" "color" : },,? { (英語)。 "title": "Cell 2",は、"タイトル "です。 "color": "purple" "color" "color" },,? { (英語)。 "title": "Cell 3", は、"Cell 2 "を意味します。 "color": "yellow"を指定することができます。 },,? { (英語)。 "title": "Cell 4", は、"セル "です。 "color": "blue" "color" : },,? {. "title": "Cell 5", "Cell 5":。 "color": "dark green" "color" : }。 ] のようになります。 } となります。 ] となります。 } のようなものです。 ]バックエンドドリブン開発で構築する可能性のある画面も紹介します。
Flexible navigation
MovilePayが開発したスーパーアプリは、革新的であるという意図した目的を達成しました。MovilePayは仮説検証から多くのことを学びましたが、特に得意なのは決済処理、つまりさまざまなサービスや製品の決済処理です。彼らは、自分たちが提供するあらゆるサービスの決済を処理できる決済アプリを持っていました。
MovilePay は、Google Pay、Apple Pay、または VisaCheckout モデルに基づいて、他の任意のアプリケーションと統合できる決済 SDK を開発することにしました。
しかし、SDK で作業すると、通常の開発パターンでテストする能力が非常に低くなるので、自動化が必要です。
MovilePay では決済処理を行うため、フローの変換は重要でした。MovilePay は、コンバージョンファネルのどの段階においても、ユーザーを失うわけにはいきませんでした。したがって、ユーザー登録からカードの追加、支払いまで、ビジネスプロセス全体を最適化する必要がありました。ここで、Backend-Driven Development が再び役に立ち、サーバーをアプリケーションの「ナビゲーター」に変えました。
MovilePay アプリケーション画面のどれもが次にどの画面か知り得なかったのです。前の画面がそのタスクを完了した後、サーバーは次に表示されるべき画面を返す責任がありました。
ルーターは、ルーターとして知られている構造を通して、アプリケーションが認識し応答することができるアクションでした。ログイン機構には、2 つの個別のアクション分岐がありました。1 つはページ タイトル用、もう 1 つはアクション ツリーで遭遇したその他の要素 (ユーザー名の変更または新しいパスワードなど) 用です。ルーターはそれらをサーバーに送信することで処理し、サーバーはそれらのアクションを解釈し、どの解釈が次の画面にあるべきかを決定しました。
この戦略における控えめなシフトは、合理化を可能にしました。MovilePayはサインアップフォームを表示するために多くの異なる方法を試しました。カードを追加する前と後のどちらにチェックアウト画面を表示するのが望ましいかをテストすることが可能でした。たとえば、これは、MovilePayが他の支払いオプションと比較してコンバージョン率を30%増加させることができた理由の1つです。
もう1つの例は、MovilePayがバックエンド駆動開発を使って問題を解決した方法です。このアプローチを実際にどのように適用するのか、疑問に思っていると思います。
同じ目標を達成するために、多くの代替方法があります。ここでは、MovilePay が iOS 向けにどのようにそれを行ったかを紹介します。必要であれば、このコンセプトは他のどのフロントエンド プラットフォームにも適用できます。
MovilePay がこれを実装したとき、彼らは追加のウィジェットを簡単に追加できる、コードが読みやすい、および単一の責任などの要件を考慮しました。物事をよりシンプルかつネイティブにするために、MovilePay はシリアライズに Codable API を使用することにしました。
ウィジェット (iOS)
柔軟性の観点から、MovilePay は、プロトコルによって解析する必要があるウィジェットを一般化することが最善の解決策であると感じました。MovilePay は、データを解析するウィジェット構造を決定する enum も設定しています。
protocol Widget: Decodable {}ウィジェットには、次のようなプロトコルが設定され、データを解析するために必要なすべての情報が含まれています。 enum WidgetIdentifier: String, Decodable {) case バナー = "BANNER"> case コレクション = "COLLECTION" = コレクションを使用する。 case リスト = "LIST">var メタタイプ: Widget.Type {. switch self { ? case .banner: 。 return BannerWidget.self case .collection: .. return CollectionWidget.self case .list: . return ListWidget.self }} }Widget プロトコルで実装された Decodable プロトコルを通じて、Codable API を利用しています。
struct BannerWidget:Widget { }Widget 構造を定義する例です。 private let imageURLString: String }。 struct CollectionWidget: Widget {. struct Item: Decodable { (デコーダブル) let imageURLString: String let title: String let サブタイトル: String }。 let sectionTitle: String let list: [Item] List [List] [Item }: Widget {. struct Item: Decodable { (デコーダブル) let imageURLString: String let text: String }。 let sectionTitle: String let list: [Item] List [List] [Item }最終的に、Decodable によって与えられたカスタム初期化を変更する必要があるとき、必要な初期化メソッドを持つ任意のウィジェットを解釈する、タイプ消去として定義されました。
final class AnyWidget: Decodable {private enum CodingKeys: CodingKey {... case 識別子 } となります。 let widget: Widget?の場合。 required init(from decoder: Decoder) throws { do {? let コンテナ = try decoder を使用します。container(keyedBy:CodingKeys.self) ="try" となります。 let 型 = try container.decode(WidgetIdentifier.Type.WidgetIdentifier(WidgetIdentifier).Type(WidgetIdentifier).Type(WidgetIdentifier).Typeself, forKey: .identifier) .Decode . self.widget = try type.metatype.。init(from: decoder) init. } catch { self.widget = nil }}}この消去タイプは、Widget の識別子とその "meta-type" プロパティを解読して、解析された Widget の残りのデータを解析するために使用すべきウィジェット構造を決定するために使用します。
このすべての結果、以下の構造はウィジェットに関するすべての情報を含むレスポンスを解析することが可能になります。これは、ウィジェット プロトコル タイプの配列を持ち、上で定義したタイプ消去を使用して各ウィジェットを復号化できます。
struct HomeResponse: Decodable {::private enum CodingKeys: CodingKey {... case ウィジェット } . let widgets: [Widget] Let widgets init(from decoder: Decoder) throws { let コンテナ = try decoder.container(keyedBy:CodingKeys.self): ="トライ"。 self.widgets = try container.decode([AnyWidget]).self, forKey: .widgets).Key.Key:Widget:compactMap {$0.widget } 。 }init(widgets).WidgetWidget: [Widget]) {Widget 。 self.widgets = widgets }}
MovilePay は、プロトコルを使用せず、バックエンドが解析のためにサポートされている各ウィジェットの配列を返すことに依存するなど、他のオプションを選択することも可能でした。しかし、私たちのプロトコルの選択は、メンテナンスと可読性の点で最良の選択であることがわかりました。このアプローチでは、新しい構造を作成し、新しいウィジェットの作成が必要になるたびに enum にケースを追加すれば十分でした。同様の状況で別のシステムでは、HomeResponseの設計を変更しなければならないでしょう。
以下は、このモデルが解析する可能性のあるJSON API応答です。
{"widgets": [] {. "identifier": "BANNER", "BANNER": "identifier""imageURLString": "url_image_to_be_downloaded" "image_to_downloaded" },,? { (トークン句読点)。 "identifier": "COLLECTION", "COLLECTION" は識別子です。 "sectionTitle": "Section Title", "Section Title": "Section Title" "list": [] ::。 { (トークン句読点)。 "imageURLString": "url_image_to_be_downloaded", "image_to_downloaded":"title": "Title item 1", "title item 1": "subtitle": "subtitle item 1" "subtitle item 2" :},,? { です。 "imageURLString": "url_image_to_be_downloaded", "imageURLString" "image_to_downloaded""title": "Title item 2", "title item 2" "subtitle": "subtitle item 2" "subtitle item 2" :},,? { です。 "imageURLString": "url_image_to_be_downloaded", "imageURLString" "image_to_downloaded""title": "Title item 3", "title item 2" "subtitle": "subtitle item 3" "subtitle item 2" :}。 ] のようになります。 },,? {... "identifier": "LIST", "LIST":。 "sectionTitle": "Section Title", は、"セクションタイトル "です。 "list": [] ::。 { (トークン句読点)。 "imageURLString": "url_image_to_be_downloaded", "image_to_downloaded":"text": "Text item 1" "Text item 2" : },,? { です。 "imageURLString": "url_image_to_be_downloaded", "imageURLString" "image_to_downloaded""text": "Text item 2" "Text item 2" : },,? { です。 "imageURLString": "url_image_to_be_downloaded", 、。 "text": "Text item 3" "Text item 2" : }。 ] のようになります。 },,? {... "identifier": "BANNER", "identifier":"imageURLString": "url_image_to_be_downloaded" "image_to_downloaded" "imageURLString"}。 ] のようなものです。 }このアプローチは、MovilePay がさまざまなユーザーに対して異なるサービスを提示し、多くの表示オプションをテストし、仮説を立て、どのウィジェットがどのサービスに使われるかを決めることができた Super App の開発に非常に近いと言えます。画面の並べ替えとサービスのグループ化の変更は、MovilePay が以前に行ったことに近いものでした。
ナビゲーション (iOS)
ウィジェットの問題を修正した後、MovilePay は同様にナビゲーションを改善しようと試みました。
アクションは、いくつかの MovilePay API の JSON 応答で返される構造化オブジェクトで、ID と、それが表すシーンで表示されるべき任意のパラメーターがあります。そのため、Actionプロトコルは構造化オブジェクトの分解を支援する役割を果たします。
プロトコル Action: Decodable {: : : scene() -> UIViewController }enum ActionIdentifier: String, Decodable {). case home = "HOME" = ="HOME" case screenOne = "SCREEN_ONE" = ="SCREEN_ONE"case screenTwo = "SCREEN_TWO" = = = var メタタイプ: Action.Type {. switch self { ? case .home:. return HomeAction.self case .screenOne: .:の場合 return ScreenOneAction.self case .screenTwo: . return ScreenTwoAction.self }} } view rawAction プロトコルと Widget プロトコルの唯一の違いは、Action 定義で各 Action に適したシーンを返すメソッドを提供していることです。例として、これらのアクションがどのように実装されているかを見てみましょう。
struct HomeAction: Action { func scene() -> UIViewController {return HomeCoordinator.scene() ..を返します。 }}: Action { (アクション) let title: String func scene() -> UIViewController {{) return ScreenOneCoordinator を返します。scene(title:self.title) : titleを返します。 }}: Action { (アクション) let title: String let サブタイトル: String func scene() -> UIViewController {{) return ScreenTwoCoordinator.scene(title: self.title,subtitle:self.subtitle): self .title: }}上記の例では、作成時に、Action はシーンを初期化し、UIViewController をインスタンス化するために Coordinators メソッドを呼び出すために必要なすべてのプロパティを含んでいなければならないことが示されました。Coordinators は、UIViewController を提供する構造体です。ここで、触れておきたいことがある。MovilePayはCoordinators構造体のデザインパターンを使用しており、各ステージのUIViewControllerインスタンスを生成する静的なscene()メソッドで表現されています。
final class HomeCoordinator: Coordinator { :。 static func scene() ->.Pirates(talkingの "punctuation">)は、"talking "の略です。 UIViewController {? // ViewController とこのシーンのすべてのアーキテクチャコンポーネントを作成します...。 return createdViewController } } } class ScreenOneCoordinator: Coordinator {? static func scene() ->.XXXと、
の2つのキーワードがあります。 UIViewController {? // ViewController とこのシーンのすべてのアーキテクチャコンポーネントを作成します...。 return createdViewController } } } class ScreenTwoCoordinator: Coordinator { ScreenTwoCoordinatorstatic func scene() ->.Piratesと、
と、があります。 UIViewController {? // ViewController とこのシーンのすべてのアーキテクチャコンポーネントを作成します...。 return createdViewController } }また、選択したアーキテクチャーのデザイン パターン (MVC, MVP, MVVM-C, VIPER-C, VIP、またはコーディネーターを使用してシーンを作成し、あるシーンから別のシーンに移動する他のアーキテクチャ) にかかわらず、アクションとバックエンド駆動開発による実装が非常に適していることに留意する必要があります。
Actions MovilePayコンテキストでは、ウィジェットの場合と同じタイプの消去を使用しましたが、若干の適応がありました。
final class AnyAction: Decodable { :::。 private enum CodingKeys: CodingKey {... case 識別子 } となります。 let action: Action?? required init(from decoder:Decoder) throws { do {? let コンテナ = try decoder になります。container(keyedBy:CodingKeys.self)になります。 let 型 = try container.decode(ActionIdentifier.Type Type): Type.とがあります。self, forKey: .identifier) .Selfをデコードします。 self.action = try type.metatype.=Metatipe.init(from: decoder) initを実行します。 } catch { self.action = nil }}}ここで適切な注意点は、MovilePay は Generics デザイン パターンを使用して、型消去コードの重複を避けることができたということです。しかし、彼らはそうしないことを選択しました。以下は、Action を含む構造の例です。
struct ResponseModelForActions: Decodable { :::private enum CodingKeys: CodingKey {... case action, text } となります。 let action: Action?。 let text: String init(from decoder: Decoder) throws { let コンテナ = try decoder.container(keyedBy:CodingKeys.self): ="トライ" 。 self.text = try container.decode(String.Text は、"strong "です。self, forKey: .text) :: forKey : forKey ..let anyAction = try? container .net があります。decode(AnyAction) となります。self, forKey: .action) :: forKey. forKey:.action = anyAction?.アクション }}APIから提供されるJSONで、例えば画面上にUIButtonを作成することができる。ユーザーがこのボタンをタップした後に処理されるアクションオブジェクトは、提供されたアクションを実行し、アプリケーションにホーム画面を表示させることができます。
{"text": "Demonstrative text",:・"demonstrative"。 「アクション」: { 。 "identifier": "HOME_SCREEN" "identifier" }。 }Coordinator プロトコルを拡張して、すべての coordinator が Actions オブジェクトを介して新しいシーンを取得できるようにすることで簡単に実現できました。
これは Coordinator が行動、つまりその行動のために次の UIViewController インスタンスを生成してそれを表示できます。
extension Coordinator { func scene(using action: Action) -> UIViewController { return action.scene() を返せ。 }}A hint on server implementation
このすべてがサーバー側でどのように見えるのか、おそらく不思議に思っていることでしょう。サービスや中核的な機能を外部情報と混同しないようにするにはどうしたらよいでしょうか。ソフトウェア開発で成功する秘訣は、層で作業することです。
そこで、すべての複雑なコア サービスに加えて、MovilePay はサーバーに別の層を追加し、他のすべてのサービスを抽象化してすべてのアプリケーション ロジックを適用し、データをフロントエンドが期待する応答へ変換しました。この層はBFF(Backend For Frontend)と呼ばれ、システムの2つの独立した無関係な端の間の通信を提供する層です。文字列、画像、ストリーム、およびスタイルのバリエーションが構成され、アプリケーションに送信する前に基礎となるデータに適用される場所です。
結論
バックエンド駆動型のアプローチを使用することにはいくつかの利点があり、記事を通して明らかにしようと試みました。しかし、これは単なる別のソリューション テンプレートです。アプリ開発の魔法の薬ではありません。さらに、アプリケーションが使用されるコンテキストを考慮する必要があります。複数のプラットフォーム用のアプリケーションを作成する必要があるのでしょうか?どのような種類のテストを行いたいですか?すべての画面を完全に制御する必要があるのか?ペイロードはどのくらいの大きさになりますか?これらのタスクを処理できる開発チームを含め、このプロジェクトを遂行するのに十分なリソースがあるか。
何よりも、どの程度の柔軟性が必要であるかについて常に注意する必要があります。極端に多用途な UI コンポーネントを作成したり、非標準のフォントや余白を使用したりすると、コードベースが非常に複雑になり、ユーザー エクスペリエンスの悪化につながります。バックエンド駆動型のアプローチを選択する場合、そのようなプロジェクトを開発および維持するための資金や開発チームなどのリソースがあるかどうか、また、アプリケーションのペイロードを正しく計算できるかどうかを考慮することが不可欠です。