freee流、クレジットカードのマイクロサービス設計構築術

こんにちは、DevBrandingのellyです。10月29日に配信した「freee Tech Night 〜freee流、クレジットカードのマイクロサービス設計構築術」の様子をご紹介します。

先日freeeは、法人向けに企業成長を支援するための統合型ビジネスカード「freeeカード Unlimited」β版の提供を開始しました。

今回はfreeeカード Unlimitedのシステムをつくったエンジニア2人をゲストにクレジットカードの仕組みをどのように作っていったのかを聞きました。リリースまでの道のりをfreee Developers Blogでも全8回で連載していますのでぜひ合わせて読んでいただけると嬉しいです。

登壇者集合写真
登壇者集合写真

tabachain:写真左上。決済チームのエンジニア。2016年7月入社。freee会計、freee申告のエンジニアを経てエンジニアリングマネージャーを約3年担当。2020年10月に再度エンジニアとして決済チームに異動。

imamura:写真右上。2017年4月に新卒入社。入社当初はfreee申告を含む税理士向け機能開発を担当。2019年から金融事業部に異動し新規のサービス開発に取り組み、2020年からfreeeカード Unlimitedの開発を担当。

のぶじゃす (@noblejasper): 写真右下。ラジオパーソナリティ、2017年に中途入社。mixi、ソーシャルゲーム企業でソフトウェアエンジニアを経験し freee に。入社後はエンジニア→エンジニア採用担当→エンジニアと DevBranding を担当。しゃべりたがり。声が大きい。

法人向けのクレジットカードをリリース

ーfreeeカード Unlimitedとはどのようなサービスなのですか?

imamura:端的に言うと、freeeが提供する法人向けのコーポレートカードです。freee会計と連携することによってfreee会計のデータと独自の審査モデルを使って高額な与信を付与することができます。

スモールビジネスを経営されている方にとって、創業間もないタイミングだと、クレジットカードを契約しても限度額が低かったり、そもそも契約できないという課題がよくあるんですが、今回freee会計のデータを使うことによって、サーバー代や広告費といった高額サービスの決済もできるようになっています。

また、通常だとクレジットカードの明細は1ヶ月程待ってから来ると思いますが、freee会計と連携していることによって、決済するとすぐに通知が来て月次の決算処理がよりタイムリーにできるようになるので、そこが大きなメリットだと思います。

ークレジットカードの裏側の仕組みってどうなっているんですか?

tabachain:入会、審査、発行、与信、決済、請求、そして決済後のユーザーへの通知などの仕組みがあり、すべてをモノリシックにやっているのではなくそれぞれの機能を1対1対応でマイクロサービスでやっているという感じです。

ーマイクロサービスは何で実装しているんですか?

imamura:基本的にバックエンドの部分は Goを使っています。フロントエンドはTypeScriptとReactを使って開発していたり、一部、画面側に対するAPIを提供するためのバックエンドはRailsを使って開発しています。

tabachain:イメージしやすく言うとフロントエンドとしてRailsを使って、これがBFFの役割をしていて、バックエンドはGoでマイクロサービスがいくつか立っていて、gRPCを使ってフロントエンドのRailsがバックエンドのマイクロサービスにリクエストを投げるイメージです。ユーザーはフロントエンドのRailsにHTTPリクエストを投げるイメージですが、裏側ではいろんなマイクロサービスにリクエストを投げるイメージです。

ーfreeeは全社的にはRailsが多いですが、今回Goで作ることにした理由は何ですか?

imamura:実は社内でも既にいくつかGoを使ったマイクロサービスがあり、ロギングやエラー通知、SQLの発行などマイクロサービスに必要な機能が共通パッケージとして提供されていました。(こちらの記事で詳しく紹介しています)今回新しくマイクロサービスを開発するにあたって、車輪の再発明を避けてドメインロジックの開発に集中したいと思い、その共通パッケージを使うために採用したというのが一番の理由です。

マイクロサービス化を選んだ理由と切り分け方

ーサービス自体が大きいことからマイクロサービス化することを決めたと聞きましたが、どのように切り分け方を判断したのですか?

tabachain:そもそもマイクロサービスでずっと開発してましたと言えるような経験者がいない状態から始まり、どういう切り分け方をするかという共通認識もなかったので、まずは勉強会という形で本を買ってきて、1ヶ月くらいみんなで切り分け方を話し合って、まとまったものをドキュメントに落とし込むというのを繰り返していましたね。

その中で、例えば「決済のマイクロサービスの障害で他のサービスも動かなくなったら問題なので、そういった単一障害点が生まれないようにここは非同期通信を使おう」「この本によるとDDDの手法でマイクロサービスの切り分け方を決めると良さそう」みたいな話が出てきて、実際に実践してみました。エンジニアだけじゃなくPMにも入ってもらって、どこで非同期通信を使うか、どういったモデルを使っていくかを言語化していきました。

大体こんな感じだよね、と認識が合ったのが去年の12月くらいで、そこから実際に実装していきました。事前にじっくり話し合ったおかげで実装がスムーズに進められたと思っています。

エンジニア→EM→エンジニアという少し変わった経歴の持ち主tabachainさん
エンジニア→EM→エンジニアという少し変わった経歴の持ち主tabachainさん

ー手を動かす前に、開発メンバー全員で議論して認識を合わせることに時間を使ったんですね。

tabachain:そうですね、みんなで自分の設計のベストアイディアをぶつけ合いました。

ーそれぞれ違うものなんですか?

imamura:全く違うわけではないのですが、システム全体がかなり大きいので、特にサービスの分割単位の部分はそれぞれ意見がありました。

tabachain:「このサービスにはこの機能が必要」「この機能はスピード重視」「この機能はクオリティ重視」みたいな肌感も違うので、どうあるべきかを話し合うのに結構時間を使いました。

imamura:マイクロサービス化を選んだのはサービスの大きさももちろんですが、他にも観点はありました。

例えば今回の場合だと、ユーザーが決済をした時にもいつでもリクエストが送られ残高を確認して即時で応答する機能や、毎月決まっているカードの請求といった機能は、他の機能に障害が起こったとしても常に起動させておく必要があります。

それぞれが互いに影響しあって全部が止まってしまうという状況は避けたかったのでマイクロサービス化を選んだというのも大きな理由です。

5年目とは思えない落ち着きのimamuraさん
5年目とは思えない落ち着きのimamuraさん

ークレジットカードの機能の特性上、障害発生時でも動いていなきゃいけないということですね。

imamura:マイクロサービスの分割の議論も時も、「互いにサービスが疎結合になっても起動できるか」であったりとか、「この分割案だと同じようなデータを持っているのに頻繁に連携しないといけないからまとめてしまった方がいいんじゃないか」とか、そういう議論がよくありました。

ー現在はβ版でサービスがリリースされていますが、いまのところ大きな課題は発生していないですか?

tabachain:所々全体に関わる修正が入ったりはするんですが、根本的に間違っていたみたいなもの幸い起きていないです。そこはやはり初めにしっかり議論できていたのが大きくて、その時の判断が間違っていなかったんだと思います。

imamura:一番良かったなぁと思ったのが、分割案を決めるときにエンジニアだけで決めてしまうんじゃなくて、社内に決済領域にかなり詳しいPMの方がいたので、その方に今作ろうとしているものはもちろん、今後作ろうとしているものやそもそも業界としてどういう仕組みになっているのかというのを相談しながら分割したので、リリースした後も大きな乖離が発生せずに済んでいるのだと思います。

本格的なマイクロサービスを開発する上での苦労や工夫

ー初めて本格的なマイクロサービスの構成で開発する上で難しかったところ、困った所はどんなところですか?

imamura:サービスを分けた場合、サービス間通信をしないといけなくなると思うんですが、一方のサービスが通信に失敗した場合や受け取る側が失敗する場合、遅延が起こった場合、エラーが起こった場合など様々なケースを想定して、それをリカバリーできるような方法を考えるのがすごく難しかったなと思っています。

実際freee Developers Blogでも非同期通信について書いたんですけれども、失敗しても再送して再処理を進めるようにしたりとか、同じようなメッセージが届いても問題ないように重複排除できるようにしたりとか、そのあたりは最初の設計のタイミングで皆とかなり議論したので、納得感を持って進められたと思っています。

ーユーザーの利便性に直結する機能なので大事ですね。

tabachain:僕はデータベース設計が難しかったです。マイクロサービスってひとつひとつのデータベースを分けて管理しているので、すべてのサービスが独自のデータベースを持つという形になっているのですが、難しかったのは他のサービスが持つデータをどう持ってくるか、参照するかみたいなところです。

同期的に他サービスから参照する形だと他サービスが落ちていると影響を受けてしまうので、あるサービスに書き込みが走ったら「書き込みが起こった」というイベントとして非同期で他のサービスに伝播させて、他のサービスではその変更を検知して書き込みが起こるというようにして、全てのサービスで不整合が起きないように設計するというのが難しかったです。

ー聞いているだけで難しそうですね。

tabachain:結果的に順番依存しない機能がほとんどだったのですが、いろんなユースケースを考えて、全員でレビューして、順番が前後してもなんとかなるような設計にして基本的に非同期通信でつながるようになっています。

ーマイクロサービスを開発してみて、普段のモノリシックな開発とは違う部分で工夫していたりとか気をつけていたりするところはありますか?

tabachain:モノレポを採用したのは工夫のひとつです。一つ一つリポジトリを分けるというやり方もあるんですが、今回はモノレポで一つのリポジトリに複数のサービスが入っています。

どういう狙いがあったかというと、例えば0→1フェーズだとサービス横断での変更がたくさん発生しますが、モノレポだと横断してレビューすることができます。

また、リポジトリが分かれていると、リポジトリA、リポジトリBがそれぞれこの状態じゃないとレビュワーが正常に動作確認できないということが起きるのですが、モノレポでひとつのリポジトリにすることで、あるコミットハッシュを持ってくれば動作確認することができます。

他にもProtocol Buffersというメッセージのスキーマを定義するデータファイルをモノレポで管理したり、デプロイも一気にできたりというメリットもあります。

ーモノレポのデメリットはありますか?

tabachain:いまのところデメリットは感じていないですが、サービスが大きくなって複雑化していって、もしひとつのサービスをひとつのチームが見るというような規模になった場合、サービス横断での変更もなくなってくるしプルリクとかも分かれているほうがやりやすいので、リポジトリを分けたほうがいいと判断する時が来るかもしれないです。

生産性でいうとモノレポを採用してかなり良かったです。そうじゃなかったらどうなっていたか、想像がつかないです(笑)

ー今後やっていきたいこと、課題などはありますか?

imamura:何かシステムエラーが起こったときに手動で対応しないといけない部分がまだ残っています。エラーや不具合の発生は避けて通れないので、規模が広がる前に自動化してリカバリできるような仕組みにしていきたいなと思っています。

tabachain:品質を担保するためにクリーンアーキテクチャでいうユースケースのテストをかなり厚めに書いたりしているんですけど、サービス間連携でのバグやデグレをまだ完全に防ぎきれていないので、どのテストでどのレイヤーをまかなうかみたいなところを明確にしていかないとなという課題感を持っています。例えばe2eテストをいまQAの方に書いてもらってますが、エンジニアが書いたほうがいいかもしれないといった話です。

ー機能を増やすというよりも、品質・クオリティを上げていくところを重視しているんですね。今後の進化も楽しみです。

最後に

すでにβ版をリリースしたfreeeカード Unlimitedもまだまだ開発が続きます。サービスの品質にこだわり抜いてきた方、サービス全体を見てアーキテクチャを考え構築したい方を募集中です。ご興味のある方はぜひこちらからエントリーください。お待ちしております!

エンジニア採用 | freee株式会社 採用情報

▶次回freee Tech Night

11/19(金)「マネーフォワード vs freee、もし名古屋で開発するなら?」というテーマでお送りします。 これから名古屋に開発拠点を立ち上げるマネーフォワードと 2020年に拠点を構えたfreee。 なぜ名古屋に開発拠点が必要なのか、メリットやデメリット、作っているプロダクト、目指している本社との関わりなど、 両社が思っていること考えてることを対比しながら赤裸々に語ってもらいます。

freee-tech-night.connpass.com

▶今回のイベントのアーカイブ(録画)


www.youtube.com