BASEプロダクトチームブログ

ネットショップ作成サービス「BASE ( https://thebase.in )」、ショッピングアプリ「BASE ( https://thebase.in/sp )」のプロダクトチームによるブログです。

BASEの顧客管理はどのようにして実現されたか

f:id:kimuchan0811:20211126225511p:plain

この記事はBASE Advent Calendar 2021の2日目の記事です。 devblog.thebase.in

はじめに

こんにちは!BASEでエンジニアをやっている @kimukei です。
現在BASEの顧客管理に関係する機能の開発に携わっています。
今回は、10/14 にリリースされた「BASE」の顧客管理がどのように開発されていったかについて、その一部についてお話ししたいと思います。

顧客管理とはどのような機能か

今回リリースした顧客管理は、BASE Uでも紹介されていますが、もともとあった拡張機能である「顧客管理 App」のリニューアルとなり、簡易的に提供されていた機能をパワーアップし、標準機能としてすべてのショップに提供したものです。

今回のリニューアルで顧客の生成元を見直し、購入回数や購入期間などの条件を指定して顧客グループを作成できるようになりました。
購入回数と購入期間から顧客をセグメントに分けることができ、そのセグメントを顧客グループとして扱えるイメージです。

顧客をセグメントに分けるイメージ
顧客をセグメントに分けるイメージ

この機能によりショップオーナーさんによる顧客分析を可能にし、限られた範囲にリーチさせたい施策需要(例えば、離反顧客にカムバッククーポンを配布するなど)を満たす土台ができました。
現在は「メールマガジン App」から顧客グループを利用でき、顧客グループに向けてメールマガジンの配信を行うことができます。
今後も顧客管理が連携できる他のAppや機能などは増えていくと思います。

以下では、主に技術的な構成についてお話していきます。

システム構成概要

はじめに顧客管理のシステム構成の概略図です。
ご覧の通り、非同期なアーキテクチャをしています。
また、一部簡略化、省略している部分があります。雰囲気を掴んでもらえればと思います。
このシステムを採用した理由やポイントがいくつかあるため以下で説明していきます。

システム構成の概略図
システム構成の概略図

なぜ非同期にしたのか

いくつか理由がありそれぞれ説明します。

顧客システムのエラーを理由に注文が失敗してはいけないこと

「BASE」のようなECプラットフォームにおいて最も重要なトランザクションは言わずもがな「注文」です。
お金が発生するトランザクションはビジネス上、法律上、UX上、あらゆる点において最も整合性が保証されていなければなりません。
「顧客」は購入金額などを持ち合わせますが、仮に同期的な処理を取った場合、顧客システムで発生したシステム不整合を理由にそれらの重要なトランザクションをロールバックすることが発生するためビジネス上、大きな打撃になります。
そのため、注文のトランザクションとは別に顧客システムのトランザクションを実行する必要があり、「顧客」に関係するトランザクションを逃そうと思いました。

処理する場所は一箇所にする

「顧客」の作成や変更が伴う操作(ここではこれを「顧客イベント」と呼称する)は、BASEのシステムのあらゆる箇所に点在します。
例えば、

  • 注文
  • 注文編集
  • 注文キャンセル

が顧客イベントの一例にあたり、注文はweb上からもできますし、Android/iOSで提供されている購入者向けのショッピングサービス「Pay ID」上からもできます。注文の編集やキャンセルはショップオーナーが利用するBASE Creatorからも可能です。
注文以外でも「顧客」は作成され、

  • コミュニティへの入会
  • 抽選商品の当選/落選

でも作成されたりします。
そのように数多く存在する顧客イベントの処理を一元化する上でも非同期アーキテクチャにすることは有用だと考えました。

マイクロサービスアーキテクチャへの移行

BASEのシステムは現在モジュラモノリスに近いものとなっております。
もし、部分的にマイクロサービス化をしていくなら、顧客システムはマイクロサービス化されるべきものの筆頭であると考えています。
顧客システムはそれ単体で十分成り立つ単位であり、扱うドメインはある程度閉ざされていて、継続的な機能開発が見込まれる領域でもあります。
組織がグロースするとおそらく顧客管理チームが出来上がるのではないか、とも予想されます。仮にそうなったら、逆コンウェイの法則から顧客システムは自然とマイクロサービス化へ向かうことでしょう。
これらの懸念があったことも大きな理由です。

また、余談ではありますが、完全なるマイクロサービス化はしていないためsagaパターンやAPIゲートウェイパターンは実践していないものの、マイクロサービスパターン[実践的システムデザインのためのコード解説]で紹介される顧客サービスの仕組みは、私たちがつくった顧客システムとかなり近しいものでした。

ビジネス上のOKが出た

最終的にこれは大きいですねw
どうやっても同期的に更新されないといけないものはありますが、顧客情報についてはその限りではなかったためここまで考えて決断することができました。
また、非同期と言っても5秒もあればほとんどの顧客情報の反映は終わっており、そういった遅れるとしても何秒くらいだろうという検証を事前に実施し、温度感をPdMと共有し議論できたのが良かったです。


非同期な仕組みにすることで考えることは増え、設計の複雑さや難易度が上がることは事実だと思います。
ただ、上記の理由より、顧客システムをこのタイミングで非同期な仕組みにしてアーキテクチャを分離しておくのは十分有用だと考えました。

それでは次にこのシステムを構成する上でいくつかポイントがあるのでそれを紹介します。

SNS -> SQS の冗長化

顧客イベントについては、メッセージキューイングの仕組みに1段SNSを挟んで冗長化させています。
これは、顧客システムの未来が確定していない中で、システムの拡張性を持たせたかったためです。
SNSがメッセージのpublisherになることで、subscriberの変更は容易になるし、複数のsubscriberに配信することもできます。
詳しくはクラスメソッドさんの、【AWS】SQSキューの前には難しいこと考えずにSNSトピックを挟むと良いよ、という話 がとても参考になります。

顧客イベント駆動の処理をする上で

いわゆるイベントソーシングというやつです。

  1. 「顧客」に関わるイベントをいくつか定義して、SQSのメッセージに顧客システムが処理できる情報を詰める
  2. 顧客システムの worker がメッセージを受け取る
  3. 顧客イベントを処理する
  4. その顧客イベントを処理した履歴を保存する

2 の worker とは、一般的にはwebサービスのバックグラウンドで特定の処理を実行するプロセスのことですが、ここでは、SQSのキューを監視し、キューに登録されたメッセージに対して何かしらの処理を行う常駐プロセスのことを指します。
Supervisor を用いてバッチ処理をデーモン化することで実現しています。

4 が最も重要なことで、これがあるからSQSを利用できているし、過去データを元にした「顧客」の作成もオンラインで実施できました。
まず、今回採用したSQSのキュータイプはFIFOキューではなく標準キューです。顧客イベントの量的に複数の worker プロセスで並列してメッセージを取得して処理したかったためです。
SQSの標準キューは少なくとも1つ以上のメッセージを保証しています( https://docs.aws.amazon.com/ja_jp/AWSSimpleQueueService/latest/SQSDeveloperGuide/standard-queues.html )。
つまり、同じメッセージを繰り返し処理することがあり得て、処理した履歴を保存していないとそれが処理済みのメッセージかどうかが不明になり、2重計上の恐れが出てきます。
履歴を持つことで冪等性を持った処理が可能になり、2重計上を防げるということは、過去の注文データなどから顧客データの生成も安全に行えるようになります。

2重計上が防がれ安全にデータ移行が行われるイメージ図
2重計上が防がれ安全にデータ移行が行われるイメージ図

これにより、今までのすべてのデータから「顧客」を生成する(チームではこれを通称「データ移行」と呼んでいます)大規模な操作をサービスメンテを入れずにオンラインで実施することができました。
仮にサービスメンテを入れるとなると、「BASE」ももう誕生して10年目を向かえているサービスで膨大なデータ量があり、それらをすべて処理する必要があるため1日はサービスを止めることになっていたでしょう・・
ちなみにこのデータ移行は確認を含め完了まで2営業日要しました。

データ不整合をリカバリする仕組みは必須

非同期な分、データ不整合はどうやっても起こり得ます。
SNSのpublishが失敗することがあるかもしれませんし、DB負荷など何らかの理由で顧客イベントをpublishする前に処理が落ちてしまうかもしれません。
それらのデータ不整合を検知して修正する仕組みは非同期なアーキテクチャをとる以上必要不可欠です。
また、データ不整合を検知する仕組みは、データ移行の際にも役立ちます。
データ移行を何をもって完了と定義するか、最終的な受け入れテストツールとしてデータ不整合を検知する仕組みが利用できるからです。
これは本当に大事で、データ移行ってだいたいバッチ処理とかで実行することが多いと思いますが、すべてのショップにデータ移行が正常に完了したかを別の仕組みを用いて確認するまでが1セットのデータ移行です。帰るまでが遠足みたいなことです。最初このことに気づかず慌ててデータ不整合を検知する仕組みを作りました。是非ここまで読んでくれた方が我が身となったときには慌てずにデータ移行を完遂してくれることを願っています 🙏
なので、データ生成と同じタイミングでデータ不整合を検知する仕組みを一緒に用意しておくと良いです。

おわりに

BASEは現在、未来に向けた土台を作りながらも日々サービスを成長させていっている最中です。
最近ではNew Relicを用いたサービス監視も盛んになってきました!
興味のある方、やっていくぞ〜 💪 な方!お待ちしております!是非一緒に未来をつくっていきましょう!
下記のリンクやぼくに連絡ください!
https://herp.careers/v1/base

明日は弊社CSE(Corporate Solutions Engineering)チームによる、IT全般統制について取り組んだ話です。おたのしみに!

それでは〜〜 👋