>_tech-draft
Firebaseのアイコン
Firebase
動画公開日
タイトル

How to unify your app's tech stack with Full-stack Dart and Firebase Functions

再生時間

11分 54秒

Dartでフルスタック開発!Firebase Functions活用術とFlutter連携

ポイント

  • Flutter開発者が抱えるクライアントとサーバー間のロジック乖離やセキュリティ課題を解決したい方向けの記事です。
  • DartをフロントエンドだけでなくFirebase FunctionsやGoogle Cloud Runのバックエンドにも活用する「フルスタックDart」について解説します。
  • 単一言語での開発による効率化に加え、高速なコールドスタート、デプロイ、セキュリティ強化、迅速なアプリ更新といったメリットが得られます。

はじめに:Flutterとバックエンド開発の課題点

GoogleのRoddyと申します。本日は、Firebase Functionsを活用した「フルスタックDart」についてご紹介します。

Flutterは、100万人以上の開発者に利用されている人気のマルチプラットフォームアプリ開発ツールキットです。モバイル、Web、デスクトップ、組み込みなど、あらゆる場所で動作し、「どこで動かすか」ではなく「何を作るか」に集中できる開発体験を提供します。Google Earth、Google Cloud Mobile、Notebook LMなど、多くの企業がFlutterでアプリを開発しています。

アプリを大規模に開発する際、ビジネスロジックをクライアントとサーバーのどちらに配置するかは常に課題となります。機能が進化するにつれて最適な選択は変化し、常にトレードオフが存在します。クライアント側では高速で快適なUI(オプティミスティックUI)を求めますが、サーバー側では堅牢でセキュアなプライベートロジックが必要です。

Backend as a Service (BaaS)はMVP(Minimum Viable Product)を迅速に構築するのに役立ちますが、スケーリングが課題となることがあります。オフラインファーストのアプリを構築する場合や、バックエンド開発に不慣れな場合、多くのロジックがクライアント側に置かれがちです。バックエンドがある場合でも、サーバーで保護すべきロジックをクライアント側で検証している箇所があるかもしれません。クライアントサイドのチェックはUX(ユーザー体験)向上に優れていますが、根本的に安全ではありません。サーバーサイドのルールはセキュリティを強化できますが、クライアント側で行っていたような柔軟な検証やロジックの実装は困難です。

このギャップにより、まったく異なる言語で同じビジネスロジックを二重に保守することになりがちです。これにより「ドリフト」(異なる言語で実装されたロジック間の乖離)は避けられないものとなります。また、クライアントを軽量に保とうとすべてをクラウドに移行すると、せっかくの型付けされたDartモデルをTypeScriptやPythonに変換し、リッチなFlutterアプリが「バックエンドに依存するただの端末」に退化してしまうように感じるかもしれません。これはドキュメントの二重化、モバイルとバックエンドの同期の二重化といったコミュニケーションコストを生み、本来機能開発に費やせる時間を奪ってしまいます。2026年には、異なる言語間でロジックが重複している箇所が、AIコーディングエージェントとの協業においてエラーや見落としの原因となる可能性も指摘されています。

このような課題を解決するためには、「単一の真実源(Single Source of Truth)」を持つことが重要であり、そこで登場するのがDartです。

Dartの真価:Flutter開発を超えて

DartはFlutterアプリケーションを構築するための言語であり、Flutterの非常に低レベルな部分を除いてすべてを構築するために使用されています。Dart言語のパワー、ライブラリ、ランタイムが、Flutterに素晴らしい開発ライフサイクル、タイトなネイティブバイナリ、高速な起動、スムーズなアニメーションをもたらしています。

しかし、DartはFlutterアプリのためだけの言語ではありません。Flutterが動作するあらゆる場所、ピクセルを描画しない場所でも動作する、強力で柔軟なモダンなオブジェクト指向言語です。DartはFlutterエコシステム内のすべてのツールも支えています。コード内の赤い波線を表示する静的解析ツールも、コードフォーマッターも、JavaScriptとWebAssemblyの両方に対応するWebコンパイラも、すべてDartで書かれています。

実際、Dartは非常に柔軟であるため、Dart言語の最も低レベルな部分であるレキサーとパーサーもDartで実装されています。これにより、これらの部分をネイティブコードにコンパイルし、C++ネイティブランタイムやコンパイラツールに組み込むことが可能になっています。開発マシン上のツールだけでなく、DartとFlutterチームは長年にわたりクラウド上でDartを実行してきました。毎月100万人以上の開発者に利用されているパッケージングサイト(pub.dev)は、すべてDartで書かれています。初めてFlutterを試したい場合(ぜひお試しください)、dartpad.devにアクセスすると、ブラウザ内でFlutterアプリケーションを構築できますが、そのインタラクティブなコンパイルを処理するバックエンドも純粋なDartでできています。

Google Cloud FunctionsにおけるDartの優位性

Dartをモバイルアプリのパフォーマンスに優れたものにする言語およびランタイムのすべての機能、つまり強力な型付け、Null Safety、AOT(Ahead-of-Time)コンパイルは、Google Cloud Functionsにとっても素晴らしい特性となります。

Dart SDKは豊富で、優れたツール、静的解析、リアルタイム解析、x86、ARM、RISC-V、JavaScript、WebAssemblyなど向けのコンパイラを備えています。サーバーアプリケーションには、使用するクラウドサービスと連携する多数の推移的な依存関係があるかもしれませんが、それでも小さなネイティブバイナリを生成し、数秒でデプロイ、ミリ秒単位でコールドスタートさせることが可能です。ウォームアップが必要なJITコンパイル言語とは異なり、Dartのネイティブ実行は高速に起動します。そして、高速にスケールアップできるだけでなく、ゼロにスケールダウンすることも自由です。これは、使用されていないサービスに対して料金を支払う必要がないことを意味します。

Firebaseとの統合:待望のDart Functions

Firebaseは、コネクテッドアプリケーションのアイデアから実現までを容易にします。認証、メッセージング、Firestoreなど、Google Cloudのパワーに支えられた数十の強力な機能を提供します。FlutterとFirebaseは、まさに「チョコレートとピーナッツバター」のように相性が良く、モバイルとWeb全体で、美しいFlutterユーザー体験を美しいコネクテッド体験へと変えるのに役立ちます。

しかし、長年にわたりギャップがありました。「Functionsはどうなの?」という声です。なぜDartのスキルと超高速なDartランタイムをFirebase Functionsに持ち込めないのか。本日、ついにこの課題を解決できることを発表できて大変嬉しく思います。真にシンメトリックな開発体験を解き放ちます。

フルスタックDartがもたらす開発体験の変革

言語やツール間のコンテキストスイッチをなくし、開発者はIDEに留まり、Dartに集中し、フローを維持することができます。フルスタックDartがついに登場しました。これにより、美しいピクセルを描画するだけでなく、Googleの大規模なスケーラブルなインフラストラクチャを活用して、ミッションクリティカルなドメインロジックを処理できるようになります。

Google Cloud Runは、Dartのために特別なユースケースを追加したわけではありません。より良い方法を提供しました。Cloud Runが「OSオンリーランタイム」をサポートするようになったのです。これは、開発マシン、あるいはCICDパイプラインから直接サーバーバイナリをコンパイルしてデプロイできることを意味します。GoogleがベースOS(この場合はUbuntu)を提供し、その上にバイナリとアセットをパッケージ化します。開発マシンにDockerは不要です。Google Cloud Buildを待つ必要もありません。WindowsワークステーションやMacラップトップから、DartはAMD64(Linuxバイナリ)へのコンパイルをサポートし、数秒でデプロイが可能です。

フルスタックDartで解決する具体的な課題

現在の開発では、以下のような問題に直面することがあります。

1. クライアントアプリの肥大化とセキュリティ脆弱性

重いビジネスロジック、複雑なバリデーター、または機密性の高いAPIキーがFlutterアプリに直接組み込まれていることがあります。この肥大化はバンドルサイズを大きくするだけでなく、クライアントにプロプライエタリなIPやキーを露出させることになり、セキュリティ脆弱性を引き起こす可能性があります。

2. アプリケーション更新の迅速化

小さなルールを修正する必要がある場合でも、パッチを送信し、アプリストアの審査を待ち、完全に伝播するまで待つ必要があります。これは時間と労力がかかるプロセスです。

実践例:共通Dartパッケージによるシングルソースオブトゥルース

Flutter開発者にとって馴染み深い「デフォルトのカウンターアプリ」を例に、フルスタックDartの活用方法を見ていきましょう。今回は、できるだけコード変更を少なくし、純粋なDartのままで、すべての接続されたクライアント間でグローバルにインクリメントするカウンターアプリを作成します。

1. Pure Dart共有パッケージの作成

まず、Pure Dartの共有パッケージを作成する必要があります。これは、FlutterアプリとDart Functionsの両方で再利用可能なすべてのモデルを格納する場所となります。Firebase FunctionsはAOTコンパイルされるため、Dart UIは含まれません。したがって、このパッケージはFlutterに依存すべきではありません。ここが、究極の「単一の真実源」となります。データモデル、Enum、コアドメインロジックのすべてがここに集約されます。これは、FlutterアプリとCloud Functionsの両方にとっての「ユニバーサル翻訳者」です。両方でまったく同じパッケージをインポートします。

2. Firebase Dart Functionsとデータ連携

Firebase Dart FunctionsはShelf上に構築されており、呼び出し時にレスポンスからJSONを簡単に返すことができます。DartクラスをJSONに変換するには、json_serializableパッケージを利用できます。これにより、定数を保存することも可能になります。

まとめ

フルスタックDartは、Flutter開発者が長年抱えてきたクライアントとサーバー間のロジックの乖離、言語のコンテキストスイッチといった課題を解決します。Dartの強力な特性とFirebase Functionsの連携により、単一の言語で堅牢かつスケーラブルなフルスタックアプリケーションを効率的に開発できるようになります。セキュアなビジネスロジック、迅速なデプロイ、そして単一の真実源を持つことで、開発体験は大きく向上するでしょう。

参考動画

https://www.youtube.com/watch?v=VXFDkg96f4I