Merpay & Mercoin Tech Fest 2023 は、事業との関わりから技術への興味を深め、プロダクトやサービスを支えるエンジニアリングを知ることができるお祭りで、2023年8月22日(火)からの3日間、開催しました。セッションでは、事業を支える組織・技術・課題などへの試行錯誤やアプローチを紹介していきました。
この記事は、「メルペイのあと払いとスマートマネーを支える返済基盤マイクロサービスの進化」の書き起こしです。
@pedro:私からは、「メルペイのあと払いとスマートマネーを支える返済基盤マイクロサービスの進化」について紹介します。
まず、軽く自己紹介します。Cui Peichongと申します。社内ではSlackネームの@pedroと呼ばれています。2019年4月に、バックエンドエンジニアとしてメルペイに入社しました。現在はCredit Designチームに所属し、メルペイのあと払いやメルペイスマートマネーの請求・精算に関わるマイクロサービスの開発に携わっています。
本セッションのアジェンダはこの通りです。まず、返済基盤の現状のマイクロサービス構成について紹介します。そして、返済基盤の機能を提供するinvoiceマイクロサービスを導入した経緯を説明し、導入に伴って生じた技術負債の話もします。最後に、負債の解消となるinvoiceマイクロサービスの独立化について紹介します。
それでは、返済基盤の現状を説明します。今、お客さまに返済機能を提供しているサービスは二つあります。一つ目は、メルペイのあと払いです。
メルペイのあと払いは、メルカリでのお買い物やメルペイが使えるお店・Webサービスでの月々のお買い物の代金を翌月に支払えるサービスです。このサービスは、三つの返済方法を提供しています。一つ目は、自動引落し。接続した銀行から自動で引き落とすことで支払う方法です。それ以外に、メルペイの残高と、コンビニATMの支払い方法があります。
もう一つ、返済機能を提供しているサービスは、メルペイスマートマネーです。メルペイスマートマネーは、メルカリアプリでお金を借りられるサービスです。
メルペイスマートマネーの毎月の返済は、接続した銀行口座からの「自動引落し」と、「残高でいつでも返済」の二つの手段があります。
この二つのサービスの構成は、このようになっています。lendingマイクロサービスはスマートマネーの利用、請求額の算出、契約管理などの機能を提供しています。
deferred-payマイクロサービスは、あと払いの利用、請求金額の算出、定額払いの契約管理などの機能を提供しています。
スマートマネーとあと払いが提供する返済機能には共通部分が多いので、両方のサービスに対応できる返済機能を、invoiceマイクロサービスが実現します。
これらの返済機能を実現するために、invoiceマイクロサービスがメルペイのCommon Foundationとなるいくつかのマイクロサービスとやり取りをしています。
主に銀行接続とチャージを管理するbankマイクロサービスでチャージして、決済処理の全般を管理するpaymentマイクロサービスを通して、スマートマネーやあと払いの債権を管理するdebtマイクロサービスに債権の返済をリクエストします。
以上が、現状のマイクロサービスの構成です。実は、初期にはinvoiceマイクロサービスが存在しませんでした。ここでは、invoiceマイクロサービスを導入した経緯を説明します。
メルペイスマートマネーは、あと払いより後にリリースされたサービスです。そのため、リリースの前に返済機能を提供する必要があるのは、あと払いだけでした。
そして、返済機能専用のマイクロサービスを作る必要もなく、deferred-payマイクロサービスが返済機能を含めて、あと払いに関連する全ての機能を提供していました。もちろん、返済の機能を実現するためにdeferred-payもCommon Foundationとなるマイクロサービスも直接やり取りしていました。
この状態で、メルペイスマートマネーの開発プロジェクトが開始され、その中でいくつかのrequirementsが出てきました。まず、基本的なrequirementsは、メルカリアプリでお金を借りることができ、かつ返済できるサービスでしたが、返済機能に関していくつかの課題がありました。
まず、あと払いの自動引落しと同じ日に実行した場合は、銀行へのAPIキャパシティの制御を考慮しなければなりません。できれば、まとめて銀行へのチャージをリクエストしたいです。
そして、請求書延滞後にメルペイ残高や売上金から自動で充当したいときに、あと払いの請求書との優先順位を制御しないといけません。
このようなrequirementsのもとに、いくつかのマイクロサービスの構成案を検討しました。一つ目の案は、lendingマイクロサービスを作り、スマートマネーの利用・請求・返済の全ての機能を実装するものでした。
この案のメリットは、lendingマイクロサービスが独立しているため、独立して全ての機能の設計・開発ができます。一方で、デメリットもいくつかあります。まず、lendingマイクロサービスとdeferred-payマイクロサービスが離れていることで、それぞれ返済の機能を提供しますが、銀行へのAPIのキャパシティ制御が困難になります。また、あと払いの請求書とスマートマネーの請求書の中と順位の制御も難しくなります。
あと払いの返済機能が既に存在しているのに、lendingマイクロサービスに似た実装を行うことにもなります。
二つ目の案は、独立した返済のマイクロサービスを作ることです。lendingサービスはスマートマネーの利用・請求や契約管理などの機能だけを提供し、invoiceマイクロサービスという独立の返済用マイクロサービスを作り、最初はスマートマネーの返済を管理しますが、将来的にあと払いの返済も実装するというものです。
この案は、一番理想に近い形ですが、デメリットもあります。あと払いの返済機能のマイグレーションを実現する前は、一つ目の案と同じように、銀行へのAPIキャパシティ制御も難しいです。
invoiceマイクロサービスに返済機能を実装するため、deferred-payマイクロサービスと重複しそうです。また、将来あと払い返済機能のマイグレーションを考慮しなければなりません。
三つ目の案は、あと払いの返済機能を利用するというものです。
lendingマイクロサービスがスマートマネーの利用・請求と、契約管理の機能だけを提供し、deferred-payマイクロサービスがこれまでの責務以外にスマートマネーの返済も対応します。具体的には、deferred-payマイクロサービスの返済機能を拡張して、invoiceマイクロサービスとして、あと払いとスマートマネー両方の返済に対応します。
この案のメリットは、返済機能を一本化でき、一つ目・二つ目の案のような銀行へのAPIキャパシティや充当順位の制御に関する問題がなくなります。あと払いの既存の返済機能を拡張するため、重複の実装も少なくなります。
しかし、ご覧のように、マイクロサービスの責務と実際の構成の乖離があります。もともとdeferred-payマイクロサービスはあと払いの機能だけを提供すべきでしたが、スマートマネーの返済も対応することで、複雑になります。
最終的に、リリースのスケジュールと実現の難易度などを考慮し、初期リリース時に一時的な技術負債を覚悟した上で、案3で実装し、その後deferred-payマイクロサービスにあるinvoiceマイクロサービスを独立させる案に決定しました。
この案を実現するために、deferred-payマイクロサービスにある返済関連のデータモデルの整理・拡張が必要です。
ここで請求書のデータモデルを例に説明します。まず、あと払いドメイン専用の項目と、invoiceマイクロサービスが扱う抽象化した請求書の項目を見分ける必要があります。
例えば、smartpay_contract_idは、あと払いドメイン専用の項目です。amountやpaid_amount、invoice_monthなどの項目は、invoiceマイクロサービスとして抽象化した請求書の項目です。
それだけではなく、スマートマネーに対応するために、いくつかの項目を追加する必要があります。例えば、どのサービスの請求書なのかを判別できるように、サービスIDを追加しました。
既存の返済ロジックも、スマートマネーに対応できるように拡張しました。
そして、今後invoiceマイクロサービスの独立化を意識して、既存のインターフェースと分けて新規のinvoice gRPCサービスを作成しました。この案によって、メルペイスマートマネーを無事リリースできました。
ある程度、技術負債を覚悟しましたが、実際に運用のフェーズに入ったら、さらに実感できるようになりました。
まず、コードベースが混在することで、さまざまな影響が出ます。例えば、あと払いサービス機能を開発するときに、invoiceマイクロサービスへの影響を意識しなければなりません。反対に、新たな返済機能を開発するときに、あと払いサービスへの影響も意識する必要があります。
また、データベースが一つになることによる影響もあります。あと払いサービスの運用中にインシデントが発生したときに、データを調査する必要があります。その際、スマートマネーのデータを除外しなければならず、メンテナンスコストが上がります。
あと払いサービスの運用だけではなく、データ分析の複雑さもあります。この技術負債を解決するために、invoiceマイクロサービスの独立化のプロジェクトが開始されました。
まず、独立したinvoiceマイクロサービスを作ります。このマイクロサービスのデータモデルは、deferred-payマイクロサービスにある返済関連のデータモデルから、抽象化したものです。そして、deferred-payマイクロサービスにある、invoice gRPCサービスと全く同じインターフェースのgRPCサービスを提供します。
既存と同じインターフェースを提供することで、Customer単位で簡単にマイグレーションできました。
具体的には、Customerごとにマイグレーションの状態を管理して、マイグレーション済みの場合に、deferred-payマイクロサービスにあるinvoice gRPCへのリクエストを処理せずに、そのままinvoiceマイクロサービスへProxyします。この形にして、clientとしてのlendingマイクロサービス側は、修正しなくてもマイグレーションできるようになりました。
マイグレーションのバッチは、このような形になっています。deferred-payマイクロサービスにあるマイグレーションのバッチがCustomer単位の返済関連データを抽出し、invoiceマイクロサービスにあるマイグレーション専用のgRPCエンドポイントをリクエストし、データをinvoiceマイクロサービスのデータベースに保存します。
実際のマイグレーションの進捗は、この通りです。特に初期段階はかなり慎重にマイグレーションを実行しました。
メルペイあと払いは、割と長い歴史があるサービスなので、想定外のデータもあります。マイグレーションの比率の拡大とともに、イレギュラーなデータを検知し、改修することを繰り返しました。そして、一定期間の安定運用が確認でき次第、マイグレーションのペースを上げ、完了しました。
全てのCustomerのマイグレーションを完了した後に、clientであるlendingマイクロサービスと、deferred-payマイクロサービスの向き先を新しいinvoiceマイクロサービスへ変更できるようになります。
最後のまとめです。
マイクロサービスの開発においては、最初に理想的なアーキテクチャを作るのは難しい場合があります。その際に、技術負債を意識したマイクロサービス構成の意思決定ができ、技術負債の解消計画を立てた上で、理想のアーキテクチャを求めることがいいアプローチだと思います。
マイグレーションに関しては、段階的に実行することで安全に終えることができました。今回はマイクロサービス開発の一つの事例として、invoiceマイクロサービスの導入と独立のお話をしました。参考になれば幸いです。
以上で、本日のセッションを終わりにします。ご清聴ありがとうございました。