Funds システム アーキテクチャ
これは Funds Advent Calendar 2022 22 日目の記事です!
Funds でサーバサイドチームのテックリードをしております田邊(@emaggame) です。今年結婚しました。あとダイビングはじめました。ちょっと溺れかけて怖い思いをしたものの、なんとか生きてます。海、概して楽しい! 結婚生活はすごく楽しい!
・・・
今回は弊社が展開する Funds のシステム アーキテクチャをご紹介します。以下エンジニア採用資料中でも触れられていますが、ここでは主にアプリケーション全体構成やその連携についてお話します。
1 つの図で全て説明しようとすると煩雑になるので、以下のようにどのようなアプリケーションがどのインフラ上に存在するかという概観レベルから始め、その後カテゴリ別にアプリケーションをピックアップします。
アプリケーション全体構成
フロントエンド
バックエンド
BI
アプリケーション全体構成
本 Note 投稿時点ではシステムとしては全面的に AWS 上に構築しています(一部 GCP 利用)。
その中でいくつかアプリケーションがあり、主に(※) EKS 上の Pod および ECS のタスクとして動作しています。
次節からはカテゴリごとに各アプリケーションをご説明します。
※ コーポレートサイト が Cloud Run で動いているのですが、ご紹介は別の機会に譲るとして本記事では割愛します。また、EKS にする前はアプリケーションごとの EC2 インスタンスにデプロイしていました。EKS への移行については以下記事もご参照ください。
フロントエンド
システムのエンドユーザーから近い順に紹介したほうがわかりやすいかなと、フロントエンドから。
フロントエンド アプリケーションは以下の 2 つです。
funds-service
funds-admin
funds-service
funds-service は Web サイトとしての Funds を提供します。
公開されているファンドを確認・投資したり、これまでの投資状況を確認できます。社内ではサービスサイトと呼称しています。
依存するアプリケーションや外部サービスは以下です。
後述するバックエンドの funds-api から顧客やファンドの情報を取得しています。また、ファンドの詳細において資金使途といった情報はヘッドレス CMS の Contentful で管理しており、funds-api が提供する情報と合わせて完成します。
ElastiCache については投資家ログインのセッション保持に利用しています。
なお現在この funds-service は CSR に React + Flow を、SSR に Express を利用していますが、開発効率を向上させるため Next.js + TypeScript への移行を進めています。
funds-admin
funds-admin はいわゆる管理画面です(社内でもそう呼称しています)。顧客(= 投資家)や提供ファンド の管理を行います。
ビルドした資材を Nginx 上に配置しています。依存するアプリケーションは funds-api のみです。
また、このアプリケーションは UI ライブラリとして Webix を利用しています。Webix の採用理由としては以下が挙げられます。
HTML / CSSを極力書かないでほぼ JS だけで開発できること
提供 UI コンポーネントが豊富であること
ドキュメントが豊富であること
管理画面は社内利用のみに限定されるため、デザイナの工数をかけてまで UI デザインするか? というと若干判断に迷うところです。管理画面では扱うデータの種類も多く、データ構造を把握している(が、HTML / CSS を駆使して 1 から Web UI デザインできる能力に長けているとは限らない)サーバサイドエンジニアが兼任して管理画面を作るシーンが多いことを考えると、この選択は正解だったと思います。
ちなみに funds-admin は純粋な SPA であるため、k8s 上の Pod として配置する必要性は全く無いことに最近気づきました。。管理の手間を省くという意味で、今後はビルドした資材を s3 上に配置したものをオリジンとし CloudFront 経由でのアクセスとするのがよいかなと考えています。
バックエンド
次に以下バックエンド アプリケーションについて紹介します。
funds-api
funds-worker
funds-email
rakten-rendering-app
funds-api
funds-api は以下のような API を提供するアプリケーションです。
フロントエンド(funds-service, funds-admin) 用
外部用
利用 SaaS からの Webhook ペイロード受付
提携パートナー向け
内部用
ヘルスチェック
外部からのジョブ実行
社内やローカル開発支援
Scala + Play Framework で書かれており以下のような構成となっています。
Nginx はサイドカーとして動作し、ヘッダの追加や API パスに応じた IP アドレス制限などを行ったうえでアップストリームである Scala アプリケーションにプロキシします。
API によってはメール送信といった後続処理のトリガになるものもあります。そうした処理は往々にして時間がかかるほか、必ずしもその API 内で全て完了してからレスポンスを返す必要はないということもあり非同期処理に逃したくなります。こういった場合はメッセージキューの SQS に処理内容(ジョブ)をメッセージとしてキューイングし、後述の funds-worker に実際の処理を委譲します。
データの保存には Aurora MySQL を利用しています(まだ 5.7 なのでそろそろ 8 系にしたい)。
ElastiCache は利用頻度の高いデータのキャッシュ、管理画面ユーザーのログインセッション保持、一時データの保存に利用しています。
funds-worker
funds-worker は前述した非同期処理を行うアプリケーションです。SQS にキューイングされたメッセージを取得し、内容に応じた処理(= ジョブ)を実行します。
データの保存は funds-api と同一の Aurora MySQL・ElastiCache を利用しています。
メッセージは funds-api がキューイングする場合もあれば、funds-worker があるジョブの処理中に別のメッセージをキューイングすることもあります。ジョブにフローがある場合ですね。
なお funds-worker (に限らずすべての Pod)はレプリカが存在しますので、funds-worker がキューイングしたメッセージを同一 Pod が処理するとは限りません。このため単に funds-worker のレプリカ数を増やすことで全体の処理数を向上できるようになっています(N/W や DB など別の要因がボトルネックとならない限り)。
また、funds-api には任意のメッセージをキューイングさせる内部用のエンドポイントがあるため、開発者がデバッグのために手動実行したり、EventBridge をトリガとした Lambda から実行することで日次や毎時でスケジューリング起動させる(※)ことも可能です。
※ 今思うと k8s の CronJob でも代替可能そうですね。これは EC2 時代の方式を EKS 移行後も踏襲したためです。
なお、funds-api と funds-worker はそれぞれ別の Pod として動作しますが、そのコードベースは同じです。アプリケーション起動時に指定する動作モードによって funds-api もしくは funds-worker として起動するようになっています。これは Funds システム開発当初において、ビジネスロジックや DB アクセスといったコードの共通化が利くため有用であったと思います。
一方 funds-worker は現状、極端に言えば SQS 中のメッセージを監視するデーモンでさえあればよく、必ずしも Web インターフェースを組み込む必要はありません。実際、クラスタ外から funds-worker に HTTP アクセスすることはできませんしシステム上の要件もありません。ジョブ実行基盤としての課題も別で出てきており、刷新する際は別のアプリケーションとして作り直す(または既存のワークフローエンジンを取り入れる)可能性もあるかもしれません。
funds-email
Funds システムでは口座開設申請結果や投資結果に関するお知らせなど、さまざまなメールをお送りします(マーケティング目的のメールと区別するため社内ではシステムメールと呼称しています)。その際、メール内容のレンダリングを提供するアプリケーションです。
メールは原則 非同期で送るため主に funds-worker が依存します。逆に依存するものはありません。
(細かいことを言うとクラスタ内通信ではなく一度インターネットを経由してアクセスしています。実際には上図のようにクラスタ内通信とする方がパフォーマンス的には良さそうですね)
本アプリケーションの作成経緯を以下記事で紹介していますので、ご興味があればご覧ください。
rakuten-rendering-app
Funds では楽天証券様と提携してのファンド募集も行っています。
rakuten-rendering-app は楽天証券様サイト側での Funds ファンド掲載・投資申込みや口座開設への連携を柔軟に行うための BFF として作られました。
また、このアプリケーションは現在 Funds システム内で唯一 ECS 上で動作しています。
今まで見てきたとおりほぼすべてのアプリケーションが EKS 上に配置されているのですが、これらも ECS への移行を検討しています。このお話はまた別の機会に。。
BI
最後に BI 関連についてです。主にマーケティング用途の分析や、データ整合チェックに利用しています。
funds-etl
構成としては以下のようになっています。
現状すべてのデータは前述の Aurora MySQL 上に保存しています。このデータをマーケティング用途に使えるよう匿名化したうえで BigQuery に転送します。funds-etl は Embulk を用いた MySQL 上のテーブル読込・BigQuery への書込を行うほか、この処理を Digdag で定期実行しています(今思うとこれも Digdag を使わずとも k8s の CronJob で事足りそう)。
BigQuery に転送されたテーブルは GKE 上に構築した Redash のデータソースとなり、各種クエリやダッシュボードを作成しています。
のですが、この Redash の 自前ホスティング運用も手離れしたいため、今後は BI 基盤として Amazon QuickSight といったマネージドサービスの利用を検討しています。
終わりに
これで Funds システムを構成するおおよそのものは紹介できたかなと思います。思ったよりシンプルだ、複雑だなど、どんな感想を持たれたでしょうか。
わたしといえばこの記事を書いていて、「派手すぎず、シンプル過ぎず、今のチーム規模的にはこんなもんかな」「お、ここぞ! というときに隙あらばチャレンジしているな」「ここ直したいんだよな~」とひとりごちておりました。これでも入社 2 年半経ちましたので、愛着もかなり出てきましたねえ。。
ビジネスを継続する限りシステムも変わり続けます。毎年この時期には去年と比べてここが変わったよ! どう!? と皆様にご報告できればと思いますので、ご期待いただければ幸いです。
お約束
Funds は開発者として働くのも楽しい職場です。なにせ同僚が一緒に働いていて気持ちがよいひとばかりです。このチームのみんなとがんばりたいな! といつも思ってます。
わたしの所属するプロダクト開発部としても、国民的な資産運用サービスに向けた各種問題のエンジニアリングによる解決、事業の成長に追従するためのシステム構築、チームとして持続可能な仕組みの模索など考えることがまだまだあります。ご興味あれば以下募集一覧をご覧いただければと思います。
採用に関する特設ページもあります。
ちょっと話だけでも、という場合は Twitter で DM いただくのも大歓迎ですのでお待ちしております!
この記事が気に入ったらサポートをしてみませんか?