以下の内容はhttps://techblog.zozo.com/entry/zozotown-microservice-layer-guidelineより取得しました。


マイクロサービス間通信の複雑化にどう立ち向かうか ── ZOZOTOWNが実践したレイヤ構成による統制

マイクロサービス間通信の複雑化にどう立ち向かうか ── ZOZOTOWNが実践したレイヤ構成による統制

はじめに

こんにちは、ECプラットフォーム部マイクロサービス戦略ブロックの半澤です。普段はアーキテクト領域のテックリードとして、ZOZOTOWNリプレイスにおける全体的な課題解決に注力しています。

今回は、複雑化したZOZOTOWNのマイクロサービス間通信を整理するため、レイヤ構成を定義し、ガイドライン化した取り組みについてご紹介します。

目次

背景と課題

2017年に始まったZOZOTOWNのリプレイスプロジェクトは、モノリシックなアーキテクチャから段階的にマイクロサービス化を進め、現在では数十個のマイクロサービスが稼働するまでに成長しました。しかし、サービスが増えるにつれて通信経路が複雑化し、以下のような課題が顕在化していました。

  • サービス間通信の是非に関する、チーム間での認識のばらつき
  • 認識の相違による開発の停滞と非効率な実装
  • 統一されていない設計方針による技術的負債の蓄積リスク

これらの課題を解決するため、現状のマイクロサービス間通信を分析し、通信パターンを網羅的に検討しました。その上で、各レイヤの責務と通信ルールを明確に定義し、ガイドラインとして明文化する取り組みを実施しました。

本記事が、マイクロサービス設計における技術方針の策定や明文化を検討されている方々にとって、実践的な知見となれば幸いです。

ZOZOTOWNマイクロサービスガイドライン - レイヤ構成と通信ルール

ここからは、実際に社内で運用しているガイドラインの内容を紹介します。

このガイドラインは、ZOZOTOWNのシステム特性と開発体制に合わせて独自に設計したものです。特に各レイヤの名称(コミュニケーター、コア等)は、役割を明確にするため私たちが定義した独自の用語です。また、社内固有の用語や詳細については、汎用的な表現に置き換えています。


ZOZOTOWNのマイクロサービスは、以下のレイヤで構成されています。

レイヤ サブレイヤ 責務
ゲートウェイ 認証、ルーティング、トレースID付与など、外部リクエストの入口制御
BFF UI用データ集約・整形、デバイス別ロジック、ユーザセッション・Cookie操作
基盤 オーケストレーター 複数基盤をまたぐドメインロジック・分散トランザクション制御
コミュニケーター 特定機能を提供し、コアに依存するドメインルールやデータ管理
コア 特定ドメインのデータ・ロジック管理、シンプルかつ汎用的なAPI提供
ユーティリティ 補助的かつ汎用的な機能を提供

以下の図で、矢印はレイヤ間の通信可能な方向を表します。また、通信経路を表すため、図中ではフロントエンド層を最上部に追加しています。

ZOZOTOWNマイクロサービスのレイヤ構成図

ゲートウェイ(Gateway)

マイクロサービス専用の内製ゲートウェイです。

マイクロサービス外からのリクエストは原則APIゲートウェイを経由します。ゲートウェイは以下のような責務を担っています。

  • APIクライアントやユーザの認証・認可
  • マイクロサービスへのパスルーティング
  • 分散トレーシング(複数のサービスをまたがるリクエストの追跡を可能にする技術)のためのトレースID生成・付与
  • ユーザセッションの作成・期限の延長

BFF(Backend for Frontend)

主にUIに直接関連する処理を担い、フロントエンドの負荷や複雑性を軽減するためのレイヤです。

キャッシュ管理やUIの設定情報を保持するため、RedisやS3等のデータストアを使用することがあります。バックエンドエンジニアが開発・運用しています。

商品や在庫情報はYahoo!ショッピングやFulfillment by ZOZOといった他サービスにも提供していますが、ZOZOTOWN BFFはそれらには関与せず、ZOZOTOWNのWebサイト・アプリ専用に設計されています。

また、ZOZOTOWN BFFの単位は、「検索」「カート」「会員」「商品」といった大まかなドメイン毎に作成します。BFFは以下のような責務を担っています。

  • UIに直接関連する処理全般の担当
  • UIに必要なデータを複数の基盤レイヤのサービスから取得し集約して返す
  • UIに合わせたデータのフォーマット変換や整形
  • Webサイトやアプリ等のデバイスによって異なるUIロジック実装
    • ※仕様が共通であっても、UIに関連する処理であれば基盤ではなくBFFに実装する
  • ユーザセッションやCookieの操作

次の条件を満たす場合、BFFは次に記述する基盤の一部の責務を担います。

  1. 基盤内に適切なコミュニケーターが存在せず実装できない
  2. 基盤内にオーケストレーターを新設するよりも、BFFに実装した方が開発・運用コストが低いと判断した

以下の責務をBFFが基盤の代わりに担います。

  • 複数の異なる基盤をまたぐドメインロジック
  • 複数の異なる基盤をまたぐ分散トランザクションの制御
  • 複数の異なる基盤へのリクエストの順序制御

基盤(Platform)

特定のドメインに特化したUIに依存しない本質的な機能を提供するためのマイクロサービス群です。ZOZOTOWN以外のサービス(Yahoo!ショッピングやFulfillment by ZOZO等)にも機能を提供することがあります。

基盤内には更に4つのレイヤが存在し、上から「オーケストレーター」「コミュニケーター」「コア」「ユーティリティ」と定義します。

サービス間通信をなるべくシンプルにするため、4つのレイヤは上下関係を持ち、原則として上から下のレイヤへの通信のみ許可されています。

オーケストレーター(Orchestrator)

基盤内のサービスの相互依存を避けるため、基盤レイヤ内の最上位に位置し、下位レイヤへ通信可能なマイクロサービスです。

複数サービスを跨いだ、UIに依存しないコアなドメインロジックの実装や、分散トランザクションを制御します。マイクロサービス設計における一般的な「オーケストレーター」パターンと同様の役割を担います。

コアやコミュニケーターのようなプリミティブなデータは持ちませんが、特定ドメイン内で完結する一時的なデータを持つことがあります。オーケストレーターは特定のドメイン単位で作成し、その機能のメインとなる基盤チームが管理します。

オーケストレーターの開発・運用コストが見合わない場合は、オーケストレーターを新設せず、BFFが処理を代替します。1つの基準として、オーケストレーターに複数本APIが必要となるかどうかで判断しています。

なお、機能の実現にあたりコミュニケーターとして設計可能な場合は、まずコミュニケーターとしての実装を検討してください。コミュニケーターとして成り立たず、かつサービス間の相互依存が発生する場合にのみ、オーケストレーターの新設を検討します。まとめると、以下の優先順位で検討します。

  1. コミュニケーターとしての実装
  2. オーケストレーターまたはBFFでの実装

オーケストレーターは以下のような責務を担っています。

  • 複数基盤をまたぐドメインロジック
  • 複数基盤をまたぐ分散トランザクションの制御
  • 複数基盤のリクエストの順序制御

例えば、オーケストレーターは次のような場面で必要になります。

  • 注文基盤 → カート基盤:注文を作成し、カートを削除する
  • カート基盤 → 注文基盤:カート投入時に、購入制限のある商品の注文履歴をチェックする

カート決済オーケストレーターを新設することで、基盤同士の相互依存を回避します。

コミュニケーター(Communicator)

UIに依存しない本質的な機能を提供するため、特定のコアサービスへ通信による依存を許可されたサービスです。コミュニケーターは自身でも特定のドメインに特化したデータ・ロジックを管理します。

コミュニケーターは以下のような責務を担っています。

  • UIに依存しない本質的な特定機能の提供
    • BFFとして実装するには本質的すぎる画面非依存なドメインルール
  • 自サービス(コミュニケーター)内のデータ管理
  • 自サービス(コミュニケーター)内のデータを操作するAPIの提供
  • 自サービス(コミュニケーター)内のデータと依存するコアのデータで実現可能なドメインロジック

コミュニケーターは必ず依存先のコアと上下関係を持ち、逆方向の依存は禁止されています。サービス間の相互依存や複雑な依存関係は、変更容易性・可用性を損ない、障害時の影響範囲を広げる恐れがあるためです。

これらの依存関係を確実に管理するため、私たちはホワイトリスト方式を採用しています。コミュニケーターとコアの依存関係はすべてホワイトリストに登録され、一方向の依存が担保されています。コミュニケーター同士の横の依存については、原則禁止としていますが、アーキテクチャ相談会で承認を得た場合のみ例外的に許可し、これもホワイトリストで管理しています。このような運用により、サービス間の依存関係を可視化し、無秩序な相互依存を防いでいます。

実際のホワイトリストは以下のような形式で管理しています(※記載内容は例)。

コミュニケーター(上) コア(下) コミュニケーター(横) 補足
カート基盤 在庫基盤 カート投入・削除時の在庫数増減処理のため
カート基盤 商品基盤 カート投入時の商品情報検証のため
レコメンド基盤 カート基盤 カート内商品を元におすすめ商品を返すため

コア(Core)

特定のドメインに特化したデータ・ロジックを管理するサービスです。

ZOZOTOWN以外(Yahoo!ショッピングやFulfillment by ZOZO等)からも利用可能にするため、シンプルかつステートレスで汎用的な作りが求められます。同一レイヤ内、上位レイヤへの通信は禁止されています(外部サービスへの通信は許可)。

代表的なコアサービスには、会員基盤や商品基盤等があります。コアは以下のような責務を担っています。

  • 自サービス(コア)内のデータ管理
  • 自サービス(コア)内のデータを操作するAPIの提供
  • 自サービス(コア)内のデータで実現可能なドメインロジック

ユーティリティ(Utility)

レイヤ全体の最下層に位置し、補助的かつ汎用的な機能を提供するマイクロサービスです。フロントエンド、ゲートウェイ、BFF、基盤のすべての上位レイヤから呼び出しが可能です。

ユーティリティは以下のような責務を担っています。

  • 補助的かつ汎用的な機能(例:ABテスト基盤やログ基盤)

ユーティリティとして認定されたサービスは、すべてのレイヤから自由に呼び出すことができます。ただし、どのサービスがユーティリティとして扱われるかを明確にするため、ユーティリティ認定リストを管理しています。新たにユーティリティを構築する際は、アーキテクチャ相談会での承認を経て、このリストに追加される必要があります。これにより、本当に汎用的で補助的な機能のみがユーティリティとして扱われることを担保しています。

レイヤ構成から外れるマイクロサービスの種類

Privateサービス(Private Service)

レイヤを問わず、特定のマイクロサービスから一部の機能を切り出したプライベートなサービスです。

機能を切り出されたサービスと上下関係を持ち、Privateサービスへのリクエストは必ずそのサービス経由で行います。プログラミング上のPrivateメソッドをイメージしてもらうと分かりやすいでしょう。Privateサービスは以下のような責務を担っています。

  • あるサービスから切り出した一部の機能

ガイドライン策定の成果

これまで各組織で独自に設計・実装されていたサービス間の依存関係が、今回のガイドライン策定により以下のような改善を実現しました。

  • 設計の統一化
    • 全チームが共通の設計原則に基づいて開発を進められる状態になった
  • 認識の標準化
    • サービス間通信の是非に関する判断基準が明確になった
  • 開発効率の向上
    • 一貫性のある設計により、チーム間の連携がスムーズになった

今後に向けて

ガイドラインは策定して終わりではなく、実際の運用を通じて継続的に改善していくことが重要です。私たちも、サービスの成長とともに生まれる新たな課題や知見を基に、ガイドラインをアップデートしていく予定です。

マイクロサービスアーキテクチャの複雑さに悩まれている方や、チーム間での設計方針の統一に課題を感じている方にとって、本記事が少しでも参考になれば幸いです。

最後に

ZOZOでは、一緒にサービスを作り上げてくれる方を募集中です。ご興味のある方は、以下のリンクからぜひご応募ください。

corp.zozo.com




以上の内容はhttps://techblog.zozo.com/entry/zozotown-microservice-layer-guidelineより取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14