サーバとクライアントの間の通信手段は伝統的にHTTPが利用される。ただしサイバーセキュリティの重要性が高まりHTTPSがデファクトになりつつある。HTTPを基盤としてより上位の通信プロトコルとしてはgRPC、リアルタイム通信用途ではWebSocketが用いられる。またHTTPとは別系統のリアルタイム通信プロトコルとしてWebRTCも用いられる。より良い通信効率・リアルタイム双方向通信を実現する次世代のプロトコルとしてHTTP/3(HTTP over QUIC)、QUIC等が開発されている。 Webアプリはクライアント・サーバモデルを基本としており、サーバへのリソースリクエストを高い頻度でおこなう。より高速な動作、ネットワーク途絶下での動作を目的に、リソースのコピーを提供するキャッシュが要件に合わせて用いられる。以下のように、キャッシュはクライアントからサーババックエンドまで様々な段階でおこなわれる。クライアントに近ければ近いほどネットワーク遅延は小さくオフライン動作に強く、一方で同期の難しさも発生する。
キャッシュ・同期
アプリ内キャッシュ
client-side proxy: Service Worker API Cache
ブラウザキャッシュ:HTTPキャッシュ
キャッシュは原理上、同期の問題を常に抱える。なぜならキャッシュとは基本的にコピーされた時間的に古いリソースの提供だからである。ゆえに各Webアプリの要件に合わせた採用が求められる。またPOST時にキャッシュへまず書き込みその後にキャッシュとバックエンドを同期する方式もある。この場合でも同期・競合の問題は原理的に存在する。
同期の手法として差分同期(delta sync)がある[9]。同期のもっともシンプルな方法はキャッシュのリフレッシュ、すなわちすべてのデータを再取得する方法である。充分な計算・通信リソースがあるならば全てのデータが最新であることを容易に保証できる。しかしリソースに制限がある場合、キャッシュと最新データの差分(delta)のみを更新することで制限を解決できる。delta sync方式では複数のクエリ、BaseQueryとDeltaQuery(+SubscriptionQuery)が存在する[9]。BaseQueryはキャッシュリフレッシュに相当する全件取得であり、DeltaQueryはデータソースからの差分情報取得である。データソースは更新情報をデータとは別に持っておき、DeltaQueryに応じて更新情報Queueから情報を送り出す。これにより既存データへのアクセスリソースを抑えながら同期が可能になる。DeltaQueryは定期的なfetch実行によって実現でき、リアルタイム更新を求めるならWebSocket等を用いたsubscriptionによる差分情報購読によっても実現できる。
Delta Syncを実装するうえではBaseQueryとDeltaQueryの使い分け、オフライン対応が重要な問題になる。ネットワーク途絶が発生した場合はBaseQueryしなおす設計にするのか、DeltaQueryのリクエスト頻度はどう制御するのか(c.f. exponential backoff+jitter)、Subscriptionの再接続は誰が責任を持つのかなどである。またデータソース側でどう差分情報を生成し保持するのか(生成方法、保存期間・形式)なども重要である。 伝統的にはID・パスワードによる認証AuthN/認可AuthZを用いたアクセス制御がおこなわれてきたが、セキュリティと利便性の観点からトークンベースの手法に移行しつつある。ソーシャル・ログイン・OAuth・OpenID Connect等に対応したクラウドサービスが提供する認証・認可サービス(IDaaS)がしばしば利用される。 Webアプリではしばしば、データ更新に伴うUI更新・UI操作によるデータ更新をデータバインディングによって暗示的におこなう。React・LitElementなどのフロントエンドフレームワークがデータバインディングを担っている。宣言的に構築したHTML(likeな)UI定義にデータを混ぜることでデータバインディングを実現する場合が多い。 Webアプリでは外部データベース等へのデータアクセスがしばしばおこなわれる。リモートデータの場合、クライアントはfetchAPI より良いアプリケーションを目指しWebアプリは日々機能が強化され、同時に複雑性も増加している。複雑性に対応するため、様々なソフトウェアアーキテクチャが利用・考案されている。WebアプリはWebすなわちネットワークを介した分散コンピューティングとしての側面を持つため、それに応じた特性をもつアーキテクチャが選ばれる場合が多い。 複雑性に対処する方法論として「独立・自立した機能 = サービス」を連携させて大きなアプリケーションをつくる方法がある。 これに着目したアーキテクチャとしては以下が挙げられる。 通底する思想はどれも同様で、ある1つの機能をサービス(独立機能単位)として扱い、それらの間の連携によって大きな機能を達成するという思想である(c.f. 関心の分離、分割統治、KISSの原則)。サービス同士が分離することで単一責任の原則が成立し機能の変更が1つの小さなサービスに閉じ複雑性が低減する。また組み合わせるサービスの種類によって多様なアプリケーションが構成できる。一方でサービスの連携をいかに行うかが重要になる(容易に複雑性が発生する)。機能分割が意識されないアプリケーションはモノリシックと呼ばれる。 連携にWebを用いるという構想から始まり、厳密な連携プロトコルかRESTのような柔軟なプロトコルか、開発工程・デプロイメント・チームビルディングまでを含んだ方法論か、クラウドコンピューティングサービスを前提にするかなど、範囲と標語/キャッチフレーズを変えながら、Webアプリケーションが登場した当初からアーキテクチャの提唱が続いている。プログラムの分割界面という側面では「サービスに着目」という点でドメイン駆動設計に近い思想を持っている。 高レベルの要素から低レベルのAPIまでWeb標準で提供されている。 Webサーバとの通信(ネットワーク)が途絶している状態をオフラインという。ソフトウェアがオフライン時にも動作するには などが必要とされる。Webアプリは一般にインストール(プログラムのローカルへの保存と組み立て)を必要としないため、オフライン状態ではそもそもアプリのプログラムにアクセスできない。そのためWebアプリではオフライン対応に特別な仕組みが必要になる。オフライン対応を前提としたWebアプリへの標語として「オフラインファースト/offline first」がある。 Webアプリでは、ブラウザがWebアプリのURLへrequestを送りそのresponseを実行することでアプリが起動・動作する。よってWebアプリのオフライン対応ではまず、ネットワークrequestをインターセプトしローカルに保存されたプログラムを代わりにresponseとして返す、すなわちプロキシとキャッシュの仕組みが必要になる。現代のWebアプリではService Worker API 多くのWebアプリは起動後もネットワークを介した情報のやり取りを行う。オフライン時にはネットワーク途絶によりこれらのネットワークリクエストが失敗する。ゆえに などに対応する必要がある。 例えばGmailなどのメールアプリは、適宜メールサーバへ新着メールの問い合わせをおこなっている。オフライン時にはネットワーク途絶によりメールの送信ができなくなるが、リクエストを単に破棄すると書いたメールを捨てることになってしまう。失敗リクエスト時にメールを送信待ちリストに加えるのか、あるいは下書きに戻すのかなどが重要な設計項目になる。待ちリストに加える場合はオンライン復帰時の適切な再送信(リトライ)機能が必要である。またオフラインの間にPCブラウザのGmailから下書きを編集し、同時にスマホのGmailからも下書きに別の変更を加えた場合、オンライン復帰時に2つの相反する編集が存在するため、競合を解決・同期しなければいけない。
アクセス制御
データバインディング
データアクセス
アーキテクチャ
機能単位=サービスの連携
サービス指向アーキテクチャ
マッシュアップ
マイクロサービス(microservice): 単一のバックエンド機能をサービスとして切り出して独立させる
マイクロフロントエンド(Micro frontends): 単一のフロントエンド機能をコンポーネントとして切り出して独立させる
Self-contained System (SCS): マイクロサービスとマイクロフロントエンドを含めて単一の独立機能単位(システム)として提供する[10]
機能による分類
メディア(動画・音声)
<audio> 要素
HTMLMediaElement DOMインターフェイス
Web Audio API
オフライン
ローカルに存在するソフトウェアプログラム
失敗するネットワークリクエストの処理
オンライン復帰時の同期
ローカルに保存されたプログラム
失敗するネットワークリクエストの処理・オンライン復帰時の同期
失敗リクエストの処理(適切に失敗し、アプリを落とさない)
失敗リクエストの保存・破棄
オンライン復帰時の失敗リクエストリトライ
オフラインの間に外部で発生した情報との競合解決・同期