Dan Luu氏は、単純で退屈なアーキテクチャが最適なビジネスモデルのケーススタディとして、Waveを紹介する記事を公開した。Waveは、最先端を行くサービスベースの非同期アーキテクチャではなく、データベースの支援によって統合的なAPIを提供する同期モノリスを採用している。
(...) ほとんどの種類のアプリケーションにとって、トラフィックの上位100サイトレベルであっても、コンピュータは十分に高速です。複雑なアーキテクチャを採用しなくても、もっと安価で構築の簡単なアーキテクチャを使って、通信量の多いアプリを提供することができるのです。
Waveのアーキテクチャは、Postgresデータベース上でCRUD要求を提供するPythonモノリスに基づいている、と氏は説明する。
ネットワーク要求を含むI/O処理を待つ間、サーバプロセスはブロックされる。Eventletのような非同期フレームワークも試したが、自分たちの未熟さゆえの運用オーバーヘッドが非常に大きいと分かったため、最終的にその使用を断念したのだ、と同社は説明している。
待機以外に何も行わないCPUリソースを持つことのコストは無視できないが、Waveの処理する要求量であれば、エンジニアリングチームのコストの方がはるかに大きいのだ。同社のビジネスモデルと現在の通信レベルでは、収益単位あたりの計算負荷が比較的低いため、ハードウェア利用コストの最適化に必要なエンジニアリング時間を正当化することはできない。
その代わりに、開始要求に対して応答を返す必要のない、長時間実行型のタスクがキューに送信される。このキューにはRabbitMQが使用されている。
プラットフォームレベルではKubernetesを選択した。同社のビジネスが成長し、新たな国へと進出するに従って、さまざまな規制を受けることになり、最終的にはシステムやデータベースをローカルで維持する必要が生じたため、というのがその理由だ。複雑なサービスベースのアーキテクチャに比べれば、モノリスベースのアーキテクチャは、必要に応じてバックエンドを分割して、それぞれの地域の法や規制を遵守することが容易にできる。
Waveでは、API層にGraphQLを採用した。文書を作成し、正確な戻り値型でコードを生成する能力が、結果としてより安全なクライアントの開発へとつながった。クエリ言語の合成機能によって、すべてのWaveアプリケーションが単一のAPIをほぼ共有できることから、複雑性が低減できると同時に、クライアントが必要なデータのみを取得できることにより、不要なネットワークラウンドトリップを回避することが可能になる。
アプリケーションのデータはHTTP/3で転送される。基盤として使用するQUICプロトコルは、モバイルデータサービスの信頼性や帯域の低さのような、フィールドにおける運用上の制約に適している。現在、Waveが運用する独自プロトコルは、緊急時にUSSD上で使用するトランスポートプロトコルのみである。
KubernetesやGraphQLを選択したことは、システムの複雑性を増大させてはいるが、メリットがデメリットをはるかに上回っている、と氏は指摘する。
アプリケーションのアーキテクチャを可能な限りシンプルに保つことで、複雑性に対するコスト(あるいは人員)を、ビジネスにメリットをもたらす複雑性の方に割り振ることが可能になるのだ。
企業の立ち上げ時には、当初は小規模なエンジニアリングチームの作業時間を節約するという観点から、一般的にソフトウェアを開発するよりも購入することが好まれる。その後、ベンダが特定の問題を修正できなくなったり、ニーズに合ったソリューションを提供できなくなれば、増大した複雑性を社内チームが引き取ることが、財務面および運用面で意味を持つようになってくる。
その一例が、通信プロバイダとの統合である。WaveにはSMSサービスが必要だが、同社のターゲット国のすべてにサービスを展開するメジャーなSMS SaaSがない上に、そのサービス費用も膨大なものになる。このような状況であれば、"通信インテグレーションを提供するチームを持ったとしても、費用面で十分に採算が取れる"ことになる、と氏は言う。
後から考えれば、現在のようなシステムを(RabbitMQあるいはPythonを使って)当初から構築するつもりであったならば、初期のシステム設計や構築の段階で行った選択のいくつかは採用されていなかったかも知れない。とはいえ、現時点での運用上のデメリットは、別のテクノロジへの移行を正当化できるほと重要でないことも事実だ。