【書き起こし】拡張性を備えたソフトウェア設計 – Rupesh Agrawal【Merpay & Mercoin Tech Fest 2023】

Merpay & Mercoin Tech Fest 2023 は、事業との関わりから技術への興味を深め、プロダクトやサービスを支えるエンジニアリングを知ることができるお祭りで、2023年8月22日(火)からの3日間、開催しました。セッションでは、事業を支える組織・技術・課題などへの試行錯誤やアプローチを紹介していきました。
この記事は、「拡張性を備えたソフトウェア設計」の書き起こしです。

@Rupesh:こんにちは。今日は、「拡張性を備えたソフトウェア設計」というトピックで、マーケティング担当者向けのエンゲージメントプラットフォームというシステムのケーススタディを交えて紹介します。

私の名前は@Rupeshです。ソフトウェアプロダクトの開発を専門としており、ITインフラ管理などのドメインでソフトウェアプロダクトを構築した経験があります。2021年にバックエンドエンジニアとして入社しました。それ以降、CRMシステムの開発を担当しました。お客さまへさまざまなキャンペーンを展開しているプラットフォームです。

ではエキサイティングなトピックに行きたいと思います。ソフトウェアの拡張性とは、ソフトウェアを構成可能でスケーラブルにするものです。ソフトウェアの変更や新機能を、大規模なリファクタリングなしに構築可能にします。

ConfigurabilityとScalabilityの拡張性の概念の、密接な関係性を考えてみましょう。

Configurabilityはソフトウェアでアトリビュートの構成をしていきます。Configurabilityを活用することによって、ソフトウェアの寿命を長くすることができます。

Scalabilityに関しては、ソフトウェアの機能でリソースを使いながら、リクエストのボリュームを処理します。ソフトウェアのScalabilityとは、ソフトウェアのExtensibilityともつながります。

参照元:https://en.wikipedia.org/wiki/Extensibility

なぜこの拡張性が必要なのでしょうか?

ターゲットユーザーは多様です。全てのユーザーをソフトウェアのライフタイムで把握できるわけではありません。数十年に渡るときもあります。ソフトウェアの製品に関しては、開発を開始する時点で全てのユースケースを把握できるわけではありません。

次に、プロダクトをサードパーティと連携していかなければいけません。ソフトウェアの開発や拡張性に対しては重要です。それにあたって、どのような道をたどっていくべきでしょうか。

ソリューションとプロダクト/プラットフォームの違いは、スライドをご覧ください。

ソフトウェアソリューションは、特定の問題のために構築されています。その要件は、開発の初期段階で収集され決まります。

一方、プロダクト/プラットフォームは、長いランタイムに向けて作られ、要件は継続的に整備されます。ソリューションは、要件に合わせたオーダーメイドで問題に非常にタイトにフィットします。しかし、プロダクト/プラットフォームには成長の余地があります。

またソリューションの場合、拡張性は重視されません。与えられた時間やリソースの中で問題を解決することに主眼が置かれます。しかしプロダクトの場合、拡張性は避けて通れません。

ソリューションは、時間とリソースがプロジェクトの決定要因である場合に最適です。しかしプロダクトの場合、プロダクトロードマップのビジョンが明確であれば、プロダクトを出発点として選択するのがベストです。

ソリューションは通常短命です。しかしプロダクト/プラットフォームの場合、時間とともに進化し続けます。これらが、両者の違いです。

では、どのように開発するのでしょうか。

ソフトウェアプロダクトを開発する最も基本のステップがこちらです。

まずは、モジュールの定義です。このスライドでは、特にモジュールという用語を使っていますが、コンポーネントとも言います。まず最初にすることは、モジュールやマイクロサービスを定義することです。
これらのマイクロサービスを定義した上で、何をすべきかの責任を定義します。各モジュール、エンティティを定義し、関係を定義します。

エンティティの中で、ビジネスエンティティのカプセル化をします。そこでエンティティを定義し、関係の提言をします。これはビジネス要件に基づいた形でプロダクトを設計をしていきます。

次がインターフェースの定義です。コンポジットタイプを使います。コンポジットタイプを使う理由は、柔軟性を担保できるからです。要求に応じてインターフェースを変えなくても属性を追加できるからです。

次に、ボリュームの予測です。こちらもプロダクトを考えた場合に複雑になります。サービス間やモジュール間のコミュニケーションにとって、このボリュームの正確な予測は重要となってきます。

これらのソフトウェアプロダクトの理解をもとに、エンゲージメントプラットフォームのケーススタディをしてみましょう。

エンゲージメントプラットフォームは、お客さまとのコミュニケーションを提供し、お客さまの成長と維持につながるソフトウェアで、成長ニーズに応えるために構想された社内プラットフォームです。

ソフトウェアの開発に際しては、非常にシンプルなユースケースから始めています。まず登録したお客さまにクーポンというリワードを与えたいという単純なユースケースから始めました。

ユースケースとしては非常にシンプルなもので実現できましたが、ここでプロダクトとして構築をしたのは当社のPMが「進化し続けるニーズに対応できる単一のプラットフォームとして開発する」というビジョンを持っていたからです。そのため、将来的な拡張にも対応できるようなコード設計になっています。

エンゲージメントプラットフォームがどのように機能するかというランタイムも入れたハイレベルな図です。最初はキャンペーンの提供を始めています。これはマーケティング担当者が実行します。

このキャンペーンモジュールを使います。ここでのお客さまにクーポンというリワードを紐づけたら、お客さまに何らかの方法でお知らせします。

このキャンペーンの構成が終わりましたら、どのようにこのキャンペーンのランタイムがお客さまへのリワードと通知の配信を扱っているのかを見ていきます。

左下が、Mercari Appです。例として、メルカリに新しく登録したお客さまにリワードを与えるようなキャンペーンをマーケティング担当者が構成したとしましょう。

フローはお客さまがアプリに登録したときにトリガーされます。アプリへの登録イベントがセグメントモジュールに通知され、セグメントモジュールの方でこれらのイベントを七つのカテゴリーに分けていきます。

キャンペーンは、セグメントの定義がされています。プラットフォームに登録をしているお客さま全てにリワードを与えなければいけないという定義になっていますので、お客さまが登録されたときに、キャンペーンが走り、その後通知がお客さまに対して送られます。

Distribution Hubがモジュールとして、報酬・通知を担当するモジュールに通知を行います。
報酬とはポイントまたはクーポンでも構いません。通知を送るための三つのモジュールが、報酬の送信と通知のユーザースクリーンへの送信を扱っています。このようにイベントのジャーニーが、アプリへリアルタイムで行われています。

どのようなことをプラットフォームの中で検討・考慮したのかについてです。

これらのモジュールは疎結合になっています。これは大変重要です。これらのモジュールが密に結合していると、一つのモジュールに対する変更が他のモジュールの変更にも影響してしまいます。

もう一つは、エンティティモデリングです。さまざまな可能性のあるシナリオというのを検討・考慮しています。全てのエンティティは、いろいろなユースケースへ将来的に対応できるように考慮しています。

さらにもう一つ、全ての設計の中で、リクエストとレスポンスにおいてコンポジットオブジェクトを公開するように設計をしています。

また、それぞれのサービスのスコープを定義し、境界がしっかりあって重複しないように設計しています。

サービスの変更をする人は、変更の範囲を把握して、新しい要件が出たときには、この要件をサービスに応じて分けて、より早く簡単に開発できるようにしていきます。

次に、サービス間のコミュニケーションの戦略も定義していきます。これらの検討の裏側のアイディアは、新しい要件が出てきたときにできるだけ開発がしやすくなるようにすることです。

次にデータモデルを見ていきます。最初のステップは、ソリューションを構成しているさまざまなエンティティを見ていくことです。エンゲージメントプラットフォームには四つのエンティティがあります。

お客さまとのコミュニケーションとして、新規登録のキャンペーンやお客さまのオンボーディングキャンペーンなどが含まれていて、これらのキャンペーンエンティティは、マーケティング担当者がキャンペーンを定義するときに使います。どのセグメントのお客さまとコミュニケーションをとるのか、どういうリワードや通知を提供するのかが含まれます。

次のエンティティがセグメントです。セグメントとは、お客さまのセグメントの定義です。シンプルなユースケースとしては、メルカリというプラットフォームに出品しているお客さま、それ以外に購入だけしているお客さまなどがあります。もっと複雑なユースケースとしては、24時間以内に5つの商品を出品しているお客さま、あるいは、24時間以内に出品と売却をしているお客さまなどもあります。

こういったセグメントを重要なエンティティとしたインセンティブというエンティティがあります。インセンティブの中には、リワードの考え方が含まれております。ほとんどのキャンペーンは、何かしらのリワードが関わっています。これらのリワードは、ポイントやクーポンです。あるいは将来的に他のリワードが出るかもしれません。

最後が通知です。通知のエンティティには、お客さまに対してコミュニケーションするチャネルが含まれています。リワード関連のコミュニケーションであれば、通知エンティティを通じてお客さまに対して通知が行われます。通知オンリーのキャンペーンは、マーケティング担当者がお客さまとコミュニケーションをしたいときに使われます。

それでは細かいデータモデルを見ていきます。こちらのスライドでは、エンティティの関係性を示しています。

キャンペーンがメインのエンティティで、一対多の関係をセグメントとして持っています。それぞれのキャンペーンは、一つあれば複数のお客さまのセグメントにターゲットを絞ることができます。それぞれのセグメントはどのようなお客さまの行動に対してインセンティブを提供するのか、あるいは通知するのかを提供します。そのため、セグメントごとに複数のインセンティブや通知が関わることもあります。

このスライドで重要なポイントは、インセンティブの通知の間には直接的な関係はないことです。これらはセグメントと独立した形で関連されています。それぞれのキャンペーンは複数のセグメントを持つことができます。

ここで仮説としてお客さまのオンボーディングキャンペーンを例にとってみたいと思います。オンボーディングキャンペーンは、キャンペーンの仕様で、キャンペーンとしてプラットフォーム上でいろいろなアクティビティをするように動機づけようとするものです。

このキャンペーンでは、登録時に1000ポイントを与えます。そして、お客さまが登録するとできるだけ出品してもらいたいと思います。

もう一つのセグメントとして、出品者向けのクーポンと出品の行動を関連付けるものもあります。商品を出品するとクーポンが与えられて、もう一つ、販売することによって購入時に使用できるクーポンが得られます。

これらは、異なる通知の仕組みとも連動しています。リワードを渡していますので、お客さまに対して通知をしていく必要があります。

それでは、キャンペーンがどのようにリアルタイムで動いてるのかをビジュアルで見ていきます。

これはキャンペーンを可視化したもので、キャンペーンのエンティティがあり、それに関連したセグメントが示されています。

今回は簡単にするため、三つのセグメントを示しています。これらのセグメントはシリーズにわかれています。最初のセグメントは、アプリに登録をしているお客さまです。二つ目は、アプリに登録し出品をしているお客さまです。三つ目はアプリに登録して、出品した商品を販売しているお客さまです。

リワードと通知がどのように送られるのか。あるセグメントに入ったときにリワードと通知がどのように送られるのかを見ていきます。最初のセグメントで登録をすると1000ポイントが渡されます。プライベートメッセージあるいはプッシュ通知で通知が行われます。

登録後は、キャンペーンのコミュニケーションとして出品をするとリワードが与えられます。お客さまの次のステップとしては出品をすることになります。出品をするとまたクーポンが与えられます。

出品したものが売れると、さらに購入時に使用できるクーポンを受け取ります。そうすると、プラットフォーム上でのお買い物が起こりえるわけです。これがキャンペーンの一連の流れとなっています。

次に、イベント処理の戦略についてです。これは、拡張性あるいは信頼性などにも役立ちます。

全てのイベントは非同期な形で処理をすることに決めました。これは私たちが判断したことです。UIが関わっておらず、お客さまがアクションを取ったときには、通知チャネルのどれかを使って通知をする必要があるからです。

次に行ったのは、イベントの冪等性を決めることです。つまりリトライやリカバリーをできるようにすることです。イベントが起きたときに、インフラの問題で重複する可能性があります。そのため、イベントをシステムが受け取ったときに、新しいイベントなのかそれとも過去に受け取ったイベントなのかを区別して把握できるようにする必要があるからです。

続いて、ログについてです。イベントのサービスに入って出てくるときにログを取る必要があります。イベントでインシデントが起きたときに、インシデントの境界を明確にし、追跡が可能なように担当しているサービスを明確にする必要があります。

そして、同期のコールをするときはキャンペーンの設定だけです。これは、マーケティング担当者がユーザーインターフェースとやり取りをしている場所だからです。

それがエンハンスメントではどのように役立つのかを見ていきます。

こういった小さな機能の強化が簡単にできます。元々はクーポン配布用に作ったのですが、同じようにポイントも配布できるようになっています。

お客さまがリワードを受けている回数ですとか、お客さまが受け取ってくる最大のポイントをもとに機能強化を簡単にできるようになっています。

それでは、プラットフォーム上で行った主な機能強化についてお話しします。

元々このプラットフォームはリアルタイムの処理に対応することになっていたのですが、バッチでの配布もサポートするようになりました。お客さまが過去に行った行動に対しても、リワードを与えたかったからです。元々この製品はこういったことには対応していませんでしたが、最初の調査後、変更が必要なモジュールはセグメンテーションモジュールだけで、他のモジュールは影響を受けないことがわかりました。そのため、セグメンテーションモジュールでSQLクエリを確認する対応をとっています。

これをやっている中で大きな問題としてあったのが、イベントのバーストです。リアルタイムと比べると、これが実行されたときにイベントがバーストしてしまって、これによってフローコントロールやイベント優先順位などの新しい課題が生まれました。

プラットフォーム上で、大きな変更としてサポートしたのが通知だけの配布です。元々のシステムの設計としては、お客さまにリワードを与えて通知を与えることです。データモデルの中では、意図的にこの通知とリワードの間には関係は持たせませんでした。リワードはセグメントの中のオプションとすることで、簡単に対応できました。

この要件は元々バッチフローで要求されていたものだったのですが、のちのちにリアルタイムのキャンペーンに対しても追加の変更なく扱えるようになりました。

次に、ソフトウェアを本番で実行するとどうなるのかをお話しします。

プラットフォームを本番環境で実行する場合には、こういった項目をチェックする必要があります。

まずは後方互換性を担保することです。一つの変更点が、過去にサポートされていたユースケースを壊すことがあるからです。長期的に実施されているキャンペーンもあれば、後方互換性がない変更をしてしまうと古いキャンペーンが実行できず、Failしてしまいます。

次にしっかりとしたマーケティング担当者向けのドキュメントが必要です。自分自身で簡単にオンボーディングできるドキュメントが必要です。製品は大変幅広いお客さまのセグメントがあり、必ずしも直接やり取りができるわけではありません。マーケティング担当者が使いやすいインターフェースがあり、しっかりとしたドキュメントがあることによってオンボーディングが簡単になります。

セキュリティとお客さまのセグメント間でのデータアクセスの管理も同じ理由で大変重要です。幅広い多様なお客さまのセグメントがあると、それぞれのお客さまごとに実行範囲があるかと思いますので、しっかりとしたアクセスコントロールを持っていることが大変重要です。

最後に、モニタリングと継続性も重要です。一つの不具合が複数のビジネスオファリングに影響する可能性があるからです。こういったプラットフォームも本番で実行するためには、堅牢な信頼のできるモニタリングが必要です。不具合が起きたときにビジネスの継続性をしっかりと担保する必要があります。

皆さんに役立つ、エキサイティングな内容であればと思っております。ご清聴ありがとうございました。

  • X
  • Facebook
  • linkedin
  • このエントリーをはてなブックマークに追加