マーケティングを加速させろ!ZOZO Marketing Platform(ZMP)の紹介

ogp

こんにちは、MA部の齋藤(@kyoppii13)です。

ZOZOTOWNでは、プッシュ通知やLINE、メールでのキャンペーン配信を実施しています。キャンペーン配信の例としては、お気に入り商品の在庫数が少なくなったときにプッシュ通知を送るといったものです。LINEやメールといった配信チャネル以外にも、キャンペーンごとにセグメントや実施タイミングも様々で、システムも配信キャンペーンの種類によって複数存在している状況でした。そのため運用保守のコストが大きくなっていました。また、キャンペーンの内容を変更するために開発側での工数が発生している状況でした。

そこでキャンペーン配信を効率的に実施するため社内向けのマーケティングプラットフォーム「ZOZO Marketing Platform(ZMP)」を開発しました。

本記事では、マーケティングプラットフォームの開発にあたって考慮した点とアーキテクチャについて紹介します。

ZOZOTOWNでのキャンペーン配信

ZOZOTOWNでは様々なキャンペーン配信を実施しています。キャンペーン配信をする際は以下の項目を検討する必要があります。

概要 説明
セグメント 誰に配信するか
コンテンツ どのようなデザインでどのような情報を配信するか
配信タイミング どんなイベントをトリガーとするか
その他設定 ABテスト、最適化などの設定

セグメント

セグメントとは配信対象者のグループです。このセグメントは全ユーザーの場合もあれば、性別や年齢などのユーザー属性で絞り込む場合もあります。

コンテンツ

コンテンツとはユーザーに配信する内容です。コンテンツは配信チャネルによって異なります。例えば、プッシュ通知の場合はタイトルと本文、画像を設定します。一方で、メールの場合はHTMLを設定します。

配信タイミング

配信タイミングとはキャンペーン発火のタイミングです。トリガーの種類によって、「バッチ配信」と「リアルタイムイベント配信」の2つに分かれます。バッチ配信は時間をトリガーとして配信するものです。例えば、新しいセールが始まるとき全ユーザーに通知するといったキャンペーンがあります。

リアルタイムイベント配信はユーザーの行動や商品在庫をトリガーとして配信するキャンペーンです。例えば、あるアイテムをお気に入り登録しているユーザーに対して、そのアイテムの在庫数が残り1つになったら通知する配信です。

バッチ配信は特定のユーザーセグメントに対して一括で送信するため「マス配信」とも呼んでいます。

その他設定

その他にはABテストを実施するか、配信時間を最適化するかなどを設定します。配信時の最適化は例えば配信する時間帯をユーザーごと最適なタイミングにする、配信数が増えすぎないように調整するといったものがあります。

これまでの運用フロー

MA部では様々なキャンペーン特性に応じて複数のシステムを運用・管理していました。

バッチ配信ではワークフローエンジンのDigdagを利用したり、LINE Friendship Manager(LFM)やMail Banner Manager(MBM)といった社内ツールを自分たちで開発して利用しています。

リアルタイムイベント配信はリアルタイムマーケティングシステム(RTM)を利用しています。こちらも自分たちで開発したツールになります。

このように様々存在するキャンペーンを特性ごとに異なるシステムを利用して、担当者は以下のフローで運用していました。

運用フロー

各システムの詳しい説明については以下のテックブログを参照ください。

techblog.zozo.com

techblog.zozo.com

techblog.zozo.com

techblog.zozo.com

課題

現状のキャンペーン配信では以下のような問題点がありました。

  • 運用・保守コストの肥大化
  • 新規開発にリソースが割けない
  • キャンペーンを簡単かつ柔軟に追加・修正できない

運用・保守コストの肥大化

キャンペーンは様々な種類が存在しており、キャンペーン特性によってシステムも異なっていました。前の章でも説明したとおり、リアルタイムイベント配信はRTM、バッチ配信はワークフローエンジンのDigdagのように分かれていました。配信システムもチャネルや配信タイミングで分かれています。このようにシステムが分かれているのは、その時々で部分最適化しながら開発してきたためです。また、システムパフォーマンスを維持するための定期的なメンテナンスも必要です。中には古くから運用されているものもあり、属人化も顕著になっていました。

新規開発にリソースが割けない

運用・保守コストが大きいため、新規開発にリソースが割けない状況でした。また古くから運用されているシステムは特殊な技術スタックを採用しているものも多く、新規での人材採用が困難になり、新しいメンバーの育成にもコストがかかっていました。

また、古いシステムにはその仕様からテストやロールバックが困難なものもありました。そのため、新規の改修が難しく、開発工数も大きくなっていました。

キャンペーンを簡単かつ柔軟に追加・修正できない

キャンペーンは新たに追加したり、既存キャンペーンの内容を修正したりもします。このキャンペーンの追加・修正作業は配信するキャンペーンの種類によっては管理画面が存在し、施策担当者のみで作業が完結するものもあります。一方で、管理画面が存在せずキャンペーン追加・修正時に開発側での工数が発生しているものもありました。例えば、RTMがその1つです。結果として、キャンペーンの企画から実施までを施策担当者だけで完結できず、キャンペーン実施までのリードタイムが長くなっていました。

また、前述の課題から新規開発にリソースが割けない状況であるため、最適化ロジックの追加修正もできず、最適なキャンペーンが実施できていない状況でした。

ここまで述べた課題を解決するものが必要でした。

新しいプラットフォームに求められること

前章で述べた課題を解決するために新規プラットフォームの構築を計画しました。プラットフォームに求められることは以下です。

  1. システムがシンプルで、運用・保守や機能開発の工数を低減できる
  2. 既存システムと機能やパフォーマンスが変わらない
  3. 施策担当者(非エンジニア)だけでキャンペーンの追加・修正ができる

1つ目はシステムの運用・保守や新たな機能追加の工数を削減し、エンジニアの負荷を低減するためです。これによって、前述の運用保守コストの課題を解決し、結果として新規開発のリソースに人員を割けます。

2つ目は既存システムでのキャンペーン内容が変わったりやパフォーマンスが落ちたりしては、これまでのキャンペーンをユーザに提供できなくなってしまいます。そのため既存システムでできることはそのままである必要があります。

3つ目は施策担当者だけでキャンペーンを実施できるようにし、キャンペーン実施までのリードタイムを短くするためです。これによって、施策担当の部門が主体となってキャンペーンを実施できるようになり、キャンペーンの効果を高めたり、実施回数を増やしたりできます。

これらの要件を満たすプラットフォームを開発コストを抑えて実現するために、最初にマーケティングツールSaaSの導入を検討しました。しかしながらZOZOTOWNは会員数が1000万人を超え、多くの訴求をします。扱うデータも大規模であり、リアルタイムイベント配信ではリアルタイム性が求められます。キャンペーン経由での売上も非常に大きいため、安定的に稼働する必要もあります。

外部SaaSのみでの実現だけではなく、一部システムは自社開発しハイブリッドアーキテクチャとして開発した場合でも検討しました。しかし、ZOZOTOWNで必要な高いパフォーマンスと安定的な稼働が求められるシステムを実現できないとの結論になりました。

このような経緯から自社開発に至りました。そして作成したプラットフォームがZOZO Marketing Platform(ZMP)です。

開発フェーズと実現できること

自社開発にあたって、まず開発フェーズを設定しました。

フェーズ 実現できること
1 管理画面上からのバッチ配信キャンペーンの登録
2 管理画面上からのリアルタイムイベント配信キャンペーンの登録と配信最適化
3 他システムの統合

フェーズ1ではZMPを新規で開発し、画面上からバッチ配信キャンペーンを設定・配信できるようにします。

フェーズ2ではフェーズ1で作成したZMPに機能追加をする形でリアルタイムイベント配信と配信最適化ができるようにします。

フェーズ3では他社内ツールを統合し、統合的なプラットフォームとして利用できるようにします。

記事執筆の時点ではフェーズ1までが完了しています。

フェーズ1では、新規のシステム開発とキャンペーン処理の整理・共通化をします。ZMPをゼロから開発しバッチ配信ができるようにします。キャンペーン処理の整理・共通化では、キャンペーンごとに独自の処理を持っていたものを共通化し、キャンペーンの設定や配信を共通化できるようにします。

アーキテクチャと開発の進め方

ZMPのアーキテクチャについて説明する前に、既存システムとアーキテクチャについて説明します。

既存システム

既存のアーキテクチャは配信の種類によって異なり、大きく分けてバッチ配信とリアルタイムイベント配信があります。配信フローと配信特性が異なるため、システムも異なっています。

バッチ配信

バッチ配信は特定のユーザーセグメントに対して、決まった時間に送信するものです。以下のフローで配信されます。

順番 概要
1 時間トリガー発火
2 セグメント作成
3 コンテンツ作成
4 最適化処理
5 配信処理

設定した時間になったタイミングでトリガーが発火し、対象となるセグメントを作成します。

次にコンテンツを作成します。コンテンツは対象者全員で共通の場合もあれば、デザインのフォーマットは同じでも掲載される情報が異なる場合もあります。例えば、おすすめアイテムの通知はユーザーごとに掲載される商品が異なります。

次に最適化処理です。直近でキャンペーン配信が多ければ配信をしない通数最適化などをします。そして、最後に配信処理を実施します。

リアルタイムイベント配信

リアルタイムイベント配信は在庫状況の変動などをトリガーにする配信です。以下のフローで配信されます。

順番 概要
1 ユーザー行動や商品情報の変化を検知
2 キャンペーン判定
3 セグメント抽出
4 最適化処理
5 コンテンツ作成
6 配信処理

まずユーザーの行動や商品情報の変化を検知します。在庫が2つから1つに減少したなどの変化です。

次に変化のイベントをもとにキャンペーンの判定を実施します。例えば「在庫数が少なくなりました」とメッセージを配信するキャンペーンがあります。キャンペーンごとにトリガー条件が決まっており、これを判定するのがキャンペーン判定です。

次にセグメント抽出です。キャンペーン判定の結果、そのキャンペーンの対象となるユーザーを抽出します。在庫が少なくなった商品をお気に入り登録しているユーザーを抽出するといった処理です。

次に最適化処理です。開封されやすいチャネルを選択するチャネル最適化、開封されやすい時間に配信する時間最適化などの最適化をします。そして、最後に配信処理を実施します。

既存アーキテクチャ

既存システムで説明したように、配信フローや配信特性の違いから、既存アーキテクチャは以下のようになっています。

old architecture

バッチ配信とリアルタイムイベント配信でシステムが異なっており、配信チャネルによってもシステムが分かれているような状態でした。また、配信に必要なデータを全社基盤から連携したり、連携したデータを元に配信データを作成したりするシステムも存在しています。

ZMPのアーキテクチャ

既存アーキテクチャをリプレイスするために考えたZMPのアーキテクチャは以下です。

new architecture

フェーズ1だけでなくZMP全体としてのゴールを達成するためのアーキテクチャとなっています。

アーキテクチャは大きく分けて管理画面、MA基盤、配信基盤の3つのモジュールに分かれます。

管理画面モジュールは社内の施策担当者がキャンペーンの設定をするための画面です。

MA基盤モジュールは「MAマネージャー」と「MAモジュール」の大きく2つに分かれます。MAマネージャーは管理画面への管理画面のAPIを提供します。管理画面で必要なデータや設定されたキャンペーンパラメータは単一のDBに保存されます。MAモジュールは管理画面から設定されたキャンペーンを元に、配信に必要なデータの準備とキャンペーンを発火します。

配信基盤モジュールはMA基盤モジュールで生成されたデータを配信する部分です。

これら以外のモジュールとしてBigQuery上に構築された全社基盤からの変更データを連携するデータポンプが存在しています。データポンプはリアルタイムデータ連携システムで、必要なデータの変更を検知して、MAのシステムへリアルタイムで連携するシステムです。このシステムは既に開発済みでした。

データポンプの詳細については以下のテックブログをご覧ください。

techblog.zozo.com

また、配信に必要なZOZOTOWNのデータはすべてBigQuery上に集約しています。ログデータもリアルタイムでBigQueryに連携しています。このBigQueryのデータを使って集計だけではなく、セグメントやコンテンツを作成します。

アーキテクチャの考慮点

このようなモジュール分割になった理由は以下です。

  • 耐障害性
  • データ特性と処理特性
  • 複数チームでの開発
  • 拡張性
  • 機能の統一

耐障害性

ZOZOTOWNでの配信では耐障害性が重要となります。非常に多くの配信をしているため、少しの間でも配信が止まってしまうと、売上影響が大きく機会損失となってしまいます。そこで障害が発生した場合に配信影響が最小となるようにしました。

まず、障害発生時に各モジュールで利用しているインフラやツールでの処理に影響があるかを検討しました。下記の処理について、障害発生時にどの処理で影響があるかを検討しました。

処理 モジュール 概要
配信設定 管理画面 管理画面からのキャンペーン設定
セグメント作成 MA基盤 セグメント作成処理
コンテンツ作成 MA基盤 コンテンツ作成処理
配信処理(メール) 配信基盤 メール配信処理
配信処理(プッシュ) 配信基盤 プッシュ配信処理
配信処理(LINE) 配信基盤 LINEの配信処理

管理画面で設定されたキャンペーンの情報はMA基盤のDBに保存され、トリガーによってセグメント作成とコンテンツ作成をし、配信処理を呼び出して配信します。

配信設定、セグメント作成、コンテンツ作成、配信処理が独立したシステムで存在しているので、前段のシステムで障害が発生して使用できなくなっても、後続のシステムまで処理が届いていれば処理が可能です。また、前段で処理されたデータが必要なのにシステムが使用できない場合、最終手段として手動でデータを用意して後続の処理を呼び出すなどの対応が可能となります。例えば、コンテンツ作成を手動でして、配信処理を手動で呼び出すなどの対応です。

管理データの特性と処理特性

管理画面から設定したキャンペーン情報は単一のDBに保存します。次のデータモデルで説明しますが、データには依存関係が多く存在しており、また、開発もMA部に閉じています。そのため一貫性を担保しやすくするのを優先し、マイクロサービス化はせずに単一のDBとしました。

MA基盤モジュール内ではバッチ配信とリアルタイムイベント配信の処理は別となっています。これは配信特性やフローが異なり、ボトルネックの箇所や要件が異なるためです。そのためここに関しては分割しています。

複数チームでの開発

MA部は複数の開発チームで構成しています。モジュールごとに責任を分け、各チームがモジュールを境界として開発を分担できるメリットがあります。障害試験や負荷試験もモジュールごとに実施ができます。

拡張性

将来的にMA基盤モジュール以外に独立させたいモジュールが必要でかつ、管理画面での管理が必要になったとします。その都度、管理画面を作ると部分最適化された管理画面が複数作られてしまいます。今回、管理画面を独立化させて、図のように管理画面を1つに統一できます。

separate responsibility

機能の統一

既存のシステムでは、配信処理がアプリケーションごとに分かれており、処理が重複して存在していました。配信に利用する外部SaaSではクオータ制限もあり、配信処理を統一しないとフロー制御も難しい状況でした。また、配信基盤については、社内の他システムからも利用する可能性がありました。そのため、配信処理をモジュールとして独立させました。

概念モデル

今回、開発を進めるにあたって、最初に開発者全員で概念モデルを考えました。ここでいう概念モデルとは既存の配信フローを整理して考えたモデル構造です。概念モデルを最初に決定することで、共通認識を持って進められ、チームを分割しても開発を進められました。

既存の配信フローにおけるバッチ配信とリアルタイムイベント配信についてはそれぞれ前の章で述べました。2つの配信に共通するのは、「だれ」に「なに」を「どのように」送るかです。

このような共通点から考えたのが次の概念モデルです。

ma-manager-model

セグメント

セグメントは「だれ」に相当する部分で、配信対象ユーザーグループです。既存の配信ではBigQueryのビューでセグメントを定義していましたが、将来的にGUIで作成する要件がありました。具体的にどのようなツールを使うかは現時点では未定ですが、例えばLookerのExploreを使ってGUIでの柔軟な条件指定でデータ抽出・可視化ができるようにするなどが考えられます。このように複数のセグメント定義方法に対応する必要がありました。そこでセグメントソースの概念を導入し、セグメント定義方法が増えても対応できるようにしました。

コンテンツ

次にコンテンツです。これは「なに」に相当する部分で、配信する内容です。コンテンツは配信チャネルごとに設定する項目が変わるので、コンテンツの下に各チャネルと対応するモデルがあります。

コンテンツにはデザインフォーマットは同じでも、内容となるデータをユーザーごとに変えたい場合があります。例えば対象セグメントのユーザーごとにお気に入りブランドの商品画像と価格を表示する場合です。このようなコンテンツは既存の配信においては、任意の記号(ここでは%%)で囲まれた文字列であるタグをコンテンツに記述し、配信時に実行するクエリで取得したデータを埋め込む形で対応していました。HTMLのメールコンテンツであれば、以下のようなHTMLをコンテンツに埋め込みます。

<p>GOODS_ID_1:%%GOODS_DETAIL_ID_1%%</p>
<p>GOODS_NAME_1:%%GOODS_NAME_1%%</p>
<p>PRICE_1:%%PRICE_1%%</p>
<p>GOODS_2:%%GOODS_DETAIL_ID_2%%</p>
<p>GOODS_NAME_2:%%GOODS_NAME_2%%</p>
<p>PRICE_2:%%PRICE_2%%</p>

このような共通して利用するデザインパーツをマージタグと呼ばれる概念で定義できるようにしました。動的パラメーターを含むHTMLなどのデータをその名前(マージタグ名)とともに定義します。上の例だと商品ID、商品名、価格を2つ表示するパーツになります。以下のようなyamlで定義します。nameがマージタグ名、descriptionがマージタグの説明、valueがマージタグの値です。

name: '{# goods_list #}'
description: 商品リスト
value: |
  <p>GOODS_ID_1:%%GOODS_DETAIL_ID_1%%</p>
  <p>GOODS_NAME_1:%%GOODS_NAME_1%%</p>
  <p>PRICE_1:%%PRICE_1%%</p>
  <p>GOODS_2:%%GOODS_DETAIL_ID_2%%</p>
  <p>GOODS_NAME_2:%%GOODS_NAME_2%%</p>
  <p>PRICE_2:%%PRICE_2%%</p>

このマージタグ名をメールテンプレートなどのコンテンツに記述すると対応するvalueが動的に埋め込まれます。

マージタグでの値(例:GOODS_ID_1)をどのように取得するかはコンテンツマージタグパラメータで定義できるようにしています。

name: 商品リストのコンテンツマージタグパラメータ
description: 商品リストのコンテンツマージタグパラメータ

schema:
  name: goods_list
  key: email_id
  columns:
    - name: email_id
      type: INTEGER
      description: EMAIL ID
      example: 123456789
    - name: GOODS_DETAIL_ID_1
      type: INTEGER
      description: 商品詳細ID1
      example: 1
    - name: GOODS_NAME_1
      type: STRING
      description: 商品名1
      example: "かっこいいシャツ"
    - name: PRICE_1
      type: STRING
      description: 価格
      example: "10000"
    - name: GOODS_DETAIL_ID_2
      type: INTEGER
      description: 商品詳細ID2
      example: 2
    - name: GOODS_NAME_2
      type: STRING
      description: 商品名2
      example: "かっこいいシャツ"
    - name: PRICE_2
      type: STRING
      description: 価格
      example: "10000"
query: |
  SELECT
    a.email_id AS email_id,
    b.GOODS_DETAIL_ID_1 AS GOODS_DETAIL_ID_1,
    b.GOODS_NAME_1 AS GOODS_NAME_1,
    b.PRICE_1 AS PRICE_1,
    b.GOODS_DETAIL_ID_2 AS GOODS_DETAIL_ID_2,
    b.GOODS_NAME_2 AS GOODS_NAME_2,
    b.PRICE_2 AS PRICE_2,
  FROM GoodsListTagle

また、マージタグやマージタグパラメータで定義された値の中にはキャンペーンによって変える値があります。例えば、タイムセールの終了日時や表示する商品の条件(表示する商品の件数や最低割引率)などです。先のHTMLの例だと、場合によっては件数を10件や20件にしたい場合があります。このような値を画面上で設定できるようにZMPプリセットの概念も導入しました。以下のような定義をあらかじめしておきます。

key: mail_min_goods_num_5_through_10
name: 最小商品数(メール用)
description: |
  対象商品の最小商品数を指定する項目です。
type: INTEGER
input:
  from: offer
  type: select
  default: 5
  options:
    - label: 5
      value: 5
    - label: 6
      value: 6
    - label: 8
      value: 8
    - label: 10
      value: 10

マージタグには関連するZMPプリセットのキー名を定義します。そうすると、先のZMPプリセットの例の場合は管理画面で最低件数をセレクトボックスで選択できるようになります。

これらのコンテンツで使用するパラメータ(マージタグ、マージタグパラメータ、ZMPプリセット)はyamlファイルで定義します。このyamlファイルをCI/CDによってDBに反映できます。この仕組みによって、DBを直接操作せずにエンジニア以外でもコンテンツで使用するパラメータの定義ができるようになっています。

オファー

次にオファーです。これは、セグメントとコンテンツを組み合わせてそれを「どのように」配信するかを設定する部分です。定義したセグメントとコンテンツを指定し、いつ配信するのかのトリガーを指定します。トリガーはバッチ配信とリアルタイムイベント配信で設定項目が異なります。前の章でも説明したように、バッチ配信の場合は「時間」を基準にリアルタイムイベント配信の場合は「ユーザ行動や商品データの変更」がトリガーとなります。

施策によっては、複数チャネルへの配信や同一チャネルでのABテストを実施します。また、将来的には配信以外にもポイント付与やWeb接客といった施策もZMPで設定できるようにしたいです。そこで、オファーに直接コンテンツを紐づけるのではなく、アクションを間に導入しています。これにより、配信だけではない様々な施策をZMPでしたいとなったときに、その施策に対応するモデルを開発・導入してアクションに紐づけられます。

キャンペーン

最上位のキャンペーンはオファーをまとめるものです。キャンペーンによってはコンテンツを変えたり、対象者を変えたり内容を変えて配信します。そのように同一のキャンペーンであっても内容が異なる配信をまとめるためのものです。

次の章では各モジュールについて説明します。

各モジュールについて

ZMPを構成する各モジュールについての詳細と責務について説明します。ここではフェーズ1までに完成している部分について主に説明します。

管理画面

管理画面モジュールは管理画面を提供するモジュールです。

管理画面を独立したモジュールとして開発した理由は2つあります。

1つ目の理由は、フロントエンドとバックエンドで開発の責務を分けてチーム分割をできるようにするためです。

2つ目の理由は、管理画面を伴う機能拡張の際に単一の管理画面アプリケーションで管理できるようにするためです。例えばフェーズ3で他の社内ツールをZMPに統合する際、先に管理画面だけをZMPに統合する手段が取れます。また、将来的にキャンペーン設定だけではなく、配信基盤のパラメータ設定もしたい場合でも、拡張性で説明したように管理画面アプリケーションを増やさずに対応できます。

管理画面を使ったZMPの運用フロー

管理画面ではフェーズ1終了時点で以下のようなフローで配信を実施します。

setting flow

バッチ配信キャンペーンのお気に入りブランドの新着アイテムを配信する場合を考えます。

最初に最上位概念のキャンペーンを作成します。キャンペーン名と担当者を設定します。

次にセグメントを作成します。セグメントの設定項目は名前とセグメントソースです。セグメントソースとして今回はBigQueryのビューを使用します。BigQueryでビュー作成後にそれをセグメントに設定します。

次にコンテンツを作成します。今回はメールで配信する場合を想定します。また、内容として対象者ごとにお気に入りしているブランドの新着アイテムを10件表示します。メールコンテンツの設定項目は名前、タイトル、デザイン(HTML版・テキスト版)です。デザインはスマートフォン・PC向けのHTML版とフィーチャーフォン向けのテキスト版が必要です。このHTML版のデザイン作成に外部SaaSを利用します。対象者ごとに新着アイテムを出し分けるためにマージタグを利用します。

最後にオファー作成です。設定項目としては名前、セグメント、コンテンツ、トリガー条件です。セグメントとコンテンツはここまでで作成したものを設定します。トリガー条件はいつ配信するかです。今回はバッチ配信のため時間を設定します。

このようなフローで設定が可能です。作成したセグメントやコンテンツは使い回せるため、これまでの運用と比べて効率的に配信設定ができるようになります。

フェーズ2ではこの管理画面でリアルタイムトリガーの設定ができるようにします。

管理画面ではセグメント・コンテンツ・キャンペーンを設定しますが、管理画面の要件の1つに、GUIでのセグメント作成とメールコンテンツ作成がありました。

セグメント作成では、既存の運用においてはSQLを書く必要があります。しかし、担当者によってはSQLを書くための学習コストが発生したり、テーブル定義を理解したりする必要があります。これらを解消するために画面上から設定できる必要がありました。この理由からセグメント作成のツールの導入が必要でした。

メールコンテンツの作成では既存のフローでは施策内容を元にデザイナーがHTMLを書いて、それを施策担当者が確認し、適宜修正をして確定するフローでした。しかし、工数が多く発生していたので、これを解消するために施策担当者が自らデザインできるようにする必要がありました。この理由からコンテンツ作成ツールの導入が必要でした。

これらの機能は一般化されている部分であり、自分たちで開発するよりも既存のものを利用したほうが良いと判断し、外部SaaSを導入しました。一般化されている部分のため、導入したツールが利用できなくなっても他ツールで代用可能です。また自社開発よりも開発工数が削減出来ます。

今回導入したメールコンテンツ作成のSaaSではメールHTMLを部分パーツのブロックで定義・編集できる機能があります。例えば、フッターバナーのHTMLを1つのブロックで定義しておき、様々なテンプレートでの再利用を可能にします。この機能を応用し、運用効率の向上に繋がりました。施策担当者が掲載内容の一部をブロック単位で変更できるようになり、また、ブロックの組み合わせで新しいコンテンツを作成できるようになりました。SaaS導入にあたって、施策担当者とデザイナー共同で既存のHTMLメールデザインを全てSaaS上でパーツ化しました。

フェーズ1終了の現時点では、メールコンテンツ作成のみ外部SaaSの導入が完了しています。

MA基盤

MA基盤モジュールは管理画面のAPIの提供と配信に必要なデータの準備とキャンペーンを発火します。

MA基盤モジュールはその中にMAマネージャー、MAモジュールを持ちます。MAマネージャーは管理画面が使用するAPIを提供します。MAモジュールは管理画面から設定されたキャンペーン情報をもとにデータを準備し、配信処理をトリガーします。

MAモジュール

MAモジュールの中には全社データ基盤からのデータ連携、キャンペーンごとに必要なセグメント・コンテンツ作成処理、チャネルごとのコンテンツ生成処理が含まれます。

バッチとリアルタイムイベントキャンペーンで処理を分けているのはキャンペーン特性や処理の違いがあって共通化が難しく、ボトルネックの箇所や要件が異なるためです。

配信の直前までの処理として、バッチ配信では時間のトリガーが発火した後にセグメント作成、コンテンツ作成となります。一方でリアルタイムイベント配信の場合はデータを連携し、条件が満たされた場合にセグメントとコンテンツを作成します。どちらも必要であれば最後に最適化を実施します。このように処理の流れが異なります。

また、配信のボトルネックの箇所や要件も異なります。バッチ配信は大量のユーザーに向けて1度に配信します。大量のユーザーに対してコンテンツを作成するのでボトルネックになりやすいです。リアルタイムイベント配信の場合は、データの変更を検知してすぐに配信する必要があるため、リアルタイム性が求められます。

このようなキャンペーン特性の違いからモジュールを分割しています。フェーズ1終了時点では、バッチ配信のモジュールであるMAバッチのみ作成が完了しています。

MAマネージャー

MAマネージャーは管理画面のAPIを提供するアプリケーションとDBを持ちます。MAマネージャーは管理画面とMAモジュールの間に入りハブの役割を持ちます。

単一のDBにしているのは前で説明した通りデータの一貫性を保つためです。

MAマネージャーはMAモジュールへデータを一方向に流れるように設計しています。MAモジュールからは参照しないようにし、MAマネージャーで障害が起きても、配信処理に影響が出ないようにするためです。

配信基盤

配信基盤はMA基盤モジュールで生成されたデータを配信する部分です。

ここを独立させている理由は2つあります。

1つ目は他モジュールで障害が発生してもデータさえあれば配信できるようにするためです。管理画面やMAマネージャーが使えなくなった場合でも、すでに設定されているキャンペーンについては独自でトリガーを実行し配信処理はできるようにしています。

2つ目は将来的に全社向けの配信基盤とできるようにするためです。今はZMPだけでの利用となっていますが、将来的に他のシステムや部門からも利用できるようにするためです。

開発の結果

現時点ではフェーズ1のバッチ配信キャンペーンのみが設定・配信できる状態ですが、キャンペーン実施を施策担当者のみでできるようになり、これまで発生していた開発やデザイナーでの工数を削減できました。また、既存のDigdagなどにおける処理は、キャンペーンごとに定義された大きなワークフローとなっており、処理が重複し複雑になっていました。それをZMPへの構成に載せ替え、処理を共通化しシンプルな形でリプレイスも出来ました。

運用保守については、運用保守のコストが大きいRTMの移行ができていないため、大きなコスト改善はできていないものの、バッチ配信の部分についてはコスト削減ができました。

機能ごとにモジュール分けができたため、障害発生時でも原因追及がしやすくなり、チームごとに柔軟に開発ができるようになりました。

集計情報を施策担当者と整理したうえで1つのプラットフォームに載せ替えることで、集計情報をまとめられ、分析しやすい環境になりました。

開発を振り返って

フェーズ1終了後に開発メンバーでKPTを実施しました。開発における進め方で良かった点や改善点を振り返りました。

良かった点としては、品質の担保ができたことと開発しやすさが挙げられました。

品質の担保については、障害試験・負荷試験の実施や、仕様やリリースフローについてCTO室レビューの実施で担保できたことが理由としてあります。

開発しやすかった理由としては、概念モデルを最初に決定し、大きな後戻りがなかったためです。現状の仕様や要件を調査し、実装フェーズへ入る前に開発メンバーと施策担当者で概念モデルについて念入りに認識合わせをし、決定出来たためです。

また、開発モジュールの分割でチームごとに実装を進められ、仕様決定までの経緯や開発フローについてドキュメントを残したことで途中参画メンバーでもスムーズに開発に入れたと意見もありました。

今後の展望

現在はフェーズ1の開発までが終了しており、2024年1月から運用開始しています。

現在はフェーズ2以降の開発に着手するための準備と、フェーズ1でやり残した細かいタスクやZMPユーザーからのフィードバックを受けて改善に取り組んでいます。フェーズ2までが完了すれば、リアルタイム配信システムを退役でき、運用保守コストの大きな改善が期待できます。フェーズ3までが完了すれば、他システムとの統合が完了し、全てのキャンペーンがZMPで管理できるようになります。

まとめ

マーケティングプラットフォームの開発にあたって考慮した点とアーキテクチャについて紹介しました。このプラットフォームの作成によって、施策担当者やマーケティング担当者でキャンペーンの運用ができるようになりました。また、開発側においてもキャンペーン実施時に発生する開発コストやシステムの運用保守コストを削減できました。本記事が皆様の参考になりましたら幸いです。

最後に

ZOZOでは一緒にプロダクトを開発してくれるエンジニアを募集しています。ご興味のある方は下記リンクからぜひご応募ください!

corp.zozo.com

カテゴリー