はじめに
最近出版された『AWSで実現するモダンアプリケーション入門』を読んだので、個人的に重要だと感じたキーワードや考えたことをまとめてみました。
全体的な所感
書籍のタイトルにもある通り、AWSを使ってモダンアプリケーションを開発する上での考え方やアーキテクチャのパターンが紹介されています。
内容については、具体的なAWSのサービスに触れ、サンプルのアプリケーションのアーキテクチャを改善していくというストーリー形式で話が運ばれているため、理解しやすかったです。
本書の中にこんな一文があるのですが、
ちょっとした変更なのに影響範囲が読めず開発期間が長期化したり、アプリケーションを更新するデプロイ作業に不安があったり、新規開発をしたくても定常的な運用作業や障害対応に追われていたり…
上記のような問題を解決するためのアプローチであることが明確に提示されており、スムーズに読み進めることができます。
本書を読んでアプリケーションの設計やアーキテクチャの幅が広がったことで自社サービスのアーキテクチャに対する理解が深まり、改善点の着想も得ることができました。
以下では、各キーワードについて詳しく見ていきます。
モダンアプリーション
そもそも「モダンアプリケーション」とは何なのでしょうか。本書では以下のように説明されています。
モダンアプリケーションとは、アプリケーションの設計、構築、管理を継続的に見直し、常に変化を受け入れ続ける開発戦略のこと
つまり「モダンアプリケーションとは変化の速い市場に迅速に対応するためにアプリケーションの設計・構築・管理をしていくこと、またはその考え方」だと理解しました。
そして、モダンアプリケーションの考え方を採用することで得られるメリットとして、「市場投入が加速される」ことや「サービスの信頼性が向上する」ことが挙げられています。
さらにモダンアプリケーションを採用する上でのベストプラクティスとして、「サーバーレステクノロジーの使用」や「モジュラーアーキテクチャの採用」があるとされています。
モダンアプリケーションに関連してMVPという言葉が紹介されていたので取り上げます。
MVP
MVP(Minimum Viable Product)とは、「実用最小限の製品」を意味しており、
ユーザーに価値を提供できる最小限のサービスや製品を提供し、ユーザーからのフィードバックを活用して昨日の改善や追加を繰り返していきます。
という活動のことを指すとされています。
この考え方は個人的にも納得感がありこの考え方に則って開発していくべきであると考えています。
膨大なコストと工数を投下したサービスや機能をリリースした結果、それがほとんど使われなかったとなると意味がない浪費になってしまうからです。
最近参加したイベントで知ったのですが、Pendoの2019年の調査によると、作られた機能の約80%の機能はほとんど、もしくは全く使われないといった結果が出たそうです。
ここからもいかに「顧客が必要としている作るべきサービスや機能は何なのか」を明確にすることが重要であると理解できます。
まず必要最小限の成果物を作ったのち、それをリリースしてユーザーのフィードバックを受け、改善する、というプロセスは理に適っていると思えます。
サーバーレス、コンテナ
サーバーレスについて、書籍では以下のように定義されています。
特定の技術仕様を満たすことではなく、本節で取り上げたようなメリットを利用者が享受できること
「本節で取り上げたようなメリット」には以下のようなものが挙げられています。
- サーバーを管理せずにすむ
- 柔軟なスケーリング
- 従量課金
- 自動化された高可用性
言葉そのものから受けるイメージである「サーバーを使わないで済む」ということではなく、あくまでサーバーを意識しなくていい上にこれらのメリットを享受することができるものです。
このサーバーレスを採用するメリットやデメリット、比較対象となるサービスは何なのでしょうか。
そこで「コンテナ」というキーワードが登場します。
コンテナとは、アプリケーションの実行環境をパッケージ化し、それをデプロイ、実行するためのテクノロジーです。
数あるサービスやアーキテクチャの中から比較検討し最適なものを選択することが重要だと考えていますので、これらを適切に比較しつつ選択できるようにしたいです。
ではこの2つのうちどちらを選択すればいいか?という問いに対して、本書では一つの指針が示されています。
「アプリケーション開発に、より多くの時間や人を投資できる選択肢はどれか」
この指針に基づくと、まずは、実行環境のための作業をAWSに任せてよりアプリケーション開発に集中できる「サーバーレス」で要件を満たせるかどうかを検討し、そうでないなら「コンテナ」を採用するという考え方がよさそうです。
書籍にも以下のようにある通り、
いずれのサービスでも要件を満たせるのであれば、AWS Lambdaの方がAmazon ECSやAmazon EKSと比較してサービスの抽象度が高く、多くの作業をAWSにオフロードできます。
まずサーバーレスで要件が満たせるかどうか検討してみるというのは理にかなった方針であると言えそうです。
ただし、Lambdaの場合タイムアウトの上限が15分であるという点や、ステートレスであるという点などその他の制約条件を考慮する必要があると思います。
The Twelve-Factor App
アプリケーション開発におけるベストプラクティスとして、Heroku社のエンジニアによって提唱されたThe Twelve-Factor Appが紹介されています。
I. Codebase
One codebase tracked in revision control, many deploys
II. Dependencies
Explicitly declare and isolate dependencies
III. Config
Store config in the environment
IV. Backing services
Treat backing services as attached resources
V. Build, release, run
Strictly separate build and run stages
VI. Processes
Execute the app as one or more stateless processes
VII. Port binding
Export services via port binding
VIII. Concurrency
Scale out via the process model
IX. Disposability
Maximize robustness with fast startup and graceful shutdown
X. Dev/prod parity
Keep development, staging, and production as similar as possible
XI. Logs
Treat logs as event streams
XII. Admin processes
Run admin/management tasks as one-off processes
このうち、「3. Config(設定)」について触れたいと思います。
本書ではこの設定について、異なる環境下でコードを変更することなく設定を適用するために、設定を外部化すべきとしています。
ここでいう「設定」とはたとえばデータベースの接続情報や外部APIのエンドポイント等のことです。
これらの値を開発環境やステージング環境、本番環境でコードを変更することなく設定を入れ替えるために、環境変数として定義すべきとしています。
それではこの外部化すべき設定とは具体的にどのような値になるのでしょうか。
本書ではこれについて具体的に触れられていませんでしたが、個人的にこの外部化すべき設定として以下が挙げられると考えています(コードを変更することなく設定を変更できるので管理しやすくなりますし、コードに機密情報をハードコーディングせずに済むのでセキュリティも担保されます。)
- データベースの接続情報
- 外部APIのエンドポイント
- 機密情報(クラウドサービスのアクセスキーなど)
マイクロサービス
マイクロサービスとはアプリケーションを独立した小さなサービスに分割するソフトウェアアーキテクチャです。
サービスがそれぞれ疎結合になるため、保守性や拡張性が高くなり開発スピードが向上するなどの利点があります。
が、一方で
- それぞれのサービス間でAPI通信が発生するためレイテンシーが大きくなる
- サービス間のデータの整合性を保つのが困難になる
等の課題もあります。
最近ではマイクロサービスを導入して失敗してしまった事例などが散見されたり手放しで歓迎されるアーキテクチャではないのかなという印象です。
マイクロサービスアーキテクチャに関連して本書の最終章に以下のアーキテクチャパターンが紹介されています。
- SPA
- API Gateway
- メッセージング
- Saga
- CQRS
- イベントソーシング
- サーキットブレーカー
- サービスディスカバリ
- サービスメッシュ
- フィーチャーフラグ
- 分散トレーシング
この中で以下3点について触れていきます。
- メッセージング
- CQRS
- フィーチャーフラグ
メッセージング
メッセージングパターンは、サービスが非同期的な通信をするアーキテクチャです。
メッセージングパターンについて、本書では2種類に分けて解説されています。
キューモデル
キューモデルは、非同期的なデータの出し入れを実現するアーキテクチャです。
キューモデルを実現する具体的なAWSのサービスとしてはAWS SQSがあります。
キューモデルのメリットの一つとして、大量のリクエストを一旦SQSで受け付け、負荷を平坦にした上で後続の処理に渡すことができる点が挙げられます。
SQSがほぼ無制限のAPI呼び出しをサポートしていることがこれを可能にしています。
ただし、SQSは処理の順序を保証しないため、厳密な処理の順序を考慮しなければいけない要件下ではFIFOキューを採用する必要があります。
パブサブモデル
キューモデルが1対1でメッセージを送信するモデルであるのに対し、パブサブモデルは1対多でメッセージを送信するパターンです。
パブサブモデルでは、パブリッシャー(送信側)がメッセージを送信し、サブスクライバー(購読側)がそのメッセージを受け取ります。
キューモデルとパブサブモデルを組み合わせれば、パブサブモデルで同じイベントを複数のサービスに送信したのち、後続の処理でキューモデルを採用することで高負荷のリクエストにも対応することが可能になります。
本書でも紹介されていますが、実際のアプリケーション開発ではこれら二つのパターンを組み合わせることがより効果的だと言えそうです。
CQRS
CQRS(Command Query Responsibility Segregation)とは、データを登録する処理(Command)とデータを参照する処理(Query)を分離するアーキテクチャパターンのことです。
たとえばデータを登録する処理については、大量のデータを時系列順に保存することに長けたNoSQLであるDynamoDBを選択し、データを参照する処理については複雑なクエリ処理が可能なRDBMSを選択するといったことがそれにあたります。
つまり、読み込みと書き込みそれぞれに適したデータストアを選択しパフォーマンスや保守性の向上を目的としたアーキテクチャパターンです。
データベースのインデックスの設定については参照の際は検索の速度を向上してくれますが、更新の際のパフォーマスを低下させるといったようなことがあるように、参照と更新でそれぞれデータベースを分けるというはそれぞれに適したDBの選択ができるという点で効果的なアプローチであると言えると思います。
ただ個人的には実装のコストが高いと想像でき(既存のDB設計を見直すとなるとなおさら)、パフォーマンスや保守性の観点から見て本当にそれが必要かどうか検討した上で採用すべきかと思います。
フィーチャーフラグ
フィーチャーフラグとは
リリース時にコードを書き換えることなく、動的にアプリケーションの振る舞いを切り替えるしくみ
のことを指します。
フラグをアプリケーションコードに差し込んでおき、そのフラグをONにした場合に対象の機能が反映されるイメージです。
たとえば、
- 一部のユーザーにのみ新機能を反映させたい
- 機能のデプロイまでは済ませておき、あとはフラグをONにすればその機能がリリースされる
といったものがそれにあたります。
確かにこのようなケースは開発時によくあります。
また本書ではこのようなフラグをtrue(ON)、false(OFF)、10%などの数値のような環境変数で扱う手法が紹介されています。
個人的にですが、このフィーチャーフラグという用語はイベントや勉強会なんかでよく耳にするのでトレンドなのかなと思います。
弊社ではバックエンドにLaravelを採用していますが、リリースされたLaravel10でもフィーチャーフラグの機能が実装されたことは記憶に新しいです。
ただ一方で、どうしても機能のON/OFFを切り替えるための処理がアプリケーションコードの中に差し込まれるので、複雑性が増すというのと、不要になった際の後始末が必要である点がややネックであるという印象です。
まとめ
本書を読んで設計やアーキテクチャ採用の幅が広がったように思います。
しかし、本書でも説明されているように、このようなアーキテクチャは正解が一つだとは限らず、メリットとコストのトレードオフの中で最適なものを選択するというような難しい作業であることが理解できます。
一方でこのようなアーキテクチャについて「選択肢」を持っていることは重要であると思います。その選択肢の中から比較検討し最善のものを選択できるかどうかでよいWebアプリケーションを開発していけるのではないかと思います。
最後に
GoQSystemでは一緒に働いてくれる仲間を募集中です!
ご興味がある方は以下リンクよりご確認ください。