古田氏の自己紹介

古田大志氏:では「出前館マイクロサービスにおける加盟店管理画面のBFFアーキテクチャ」というお題で発表します。古田大志と申します。よろしくお願いします。

まず自己紹介を軽くします。LINEでサーバーサイドエンジニアをやっていて、主な技術領域はJavaやKotlinやSpring Boot、最近だとTerraformを触ったりしています。(スライドを示して)GitHubのアカウントはこんな感じになってます。

僕はもともと競技プログラミングをしていて、CSの分野に興味を持ち、Webを触り始めた背景があります。

現状、LINEでは出前館の開発を行っていて、プロダクトでいうと、加盟店管理画面とクーポンサービスとの2つのプロダクトをリードしています。

主にやっていることは要件定義から設計、実装、テスト、運用と端から端までという感じですね。領域的にはインフラ、バックエンド、アーキテクトなど、サーバーサイドは基本なんでもやるという感じです。

最近は特にコードレビューに力を入れていて、直近半年で650プルリクぐらい見ていて「いろいろ見てるかなぁ」という感じです。

出前館のサービス構成

では今日は出前館について話していきます。まず「出前館とは」ですが、システム目線でいうと、大きな3つのプロダクトから成るサービスとなっています。1つがエンドユーザー、1つがドライバー、1つが加盟店という、この3つの領域になってます。

いわゆる一番想像しやすい(のが)エンドユーザー(側ですかね)。みなさんが注文をすると、その(エンドユーザー側から)注文した情報が加盟店側に届くので、「ごはんを作ってください」という依頼ができます。

それと並行してドライバー側にも連絡がいって、ドライバーが加盟店からできあがったごはんを受け取って運ぶようなサービスになっています。いわゆるフードデリバリーですね。

加盟店プロダクトのAS−ISとTO−BE

今日は先ほどの加盟店部分のプロダクトについて話していきます。加盟店管理画面と呼ばれる、店舗の人がいろいろ操作できる画面があります。(スライドを示して)ちょっとモザイクとかがかかってるんですが、こんな感じで。

ちょっと想像しづらいかもしれませんが、いわゆる店舗の情報とか、商品の情報とか、一般のエンドユーザーには見えない部分で加盟店側が後ろから操作している画面があります。このプロダクトの1年半の開発過程についてお話ししていきます。

まずAS−ISの特徴。僕は(出前館のプロダクトには)1年半前ぐらいに参画したところですが、約1年半、2年前ぐらいの加盟店管理画面の状況がどうなっていたかというと、いわゆるモノリシックなアプリケーションでサーバーも冗長構成も取られておらず、単一のモノリスなアーキテクチャという感じでした。

ブラウザがあって、Webサーバーもフロントもサーバーサイドも一緒になっていて、DBが後ろにあるようなシンプルなシステムになっています。いわゆる結合度の高いアーキテクチャですね。

ここに多くの機能ドメインが絡み合っていて、管理画面はけっこういろいろな機能を求められるんですが、店舗、商品、注文、クーポン、レビュー、請求、レポート。まぁ他にもいろいろあるんですが、多くの機能ドメインが絡み合っているような状態になっています。

ここから目指すTO−BEですが、先ほどが元の状態で、ここからどう改善していったかという話になります。まずサーバーサイドとUIを分けるのを目標として、フロントとBFFという2つのアーキテクチャを用意します。

BFFはいわゆるBackend For Frontendといって、フロントのためのサーバーサイドです。さらに後ろにドメインごとの小さなコンポーネントを作ったマイクロサービス化、いわゆるInternal−APIを量産してDBも物理的に分けることで、適切なドメイン分割とBFFによるマイクロサービス化を目指そうということがTO−BEとしてあります。

アーキテクチャを分離する、例えばフロントとサーバーが分かれることで、フロントエンジニアがUIだけに集中できるので、開発を並列化させたり(できる)。あと、モダンな技術に置き換えるのも1つあって、今でいうと、ReactとSpring Bootで作ろうという話でTO−BEが進んでいっています。

TO−BEに向かう中で出てきた問題点

ここで1つ問題点が出てきました。TO−BEに向かってリプレイスがどんどん進んでいくんですが、ドラスティックなリプレイスは難しいという大きな問題があります。

というのも、現状というか1年半前は1つのサーバーで動いていた状態なのですが、TO-BEに変える時って、アーキテクチャ的には大きな変更が入っている(ことになる)んですよね。

インフラ的にフロントとサーバーを分離したり、様々なドメインロジックに応じたInternalなAPIを作っていくことはすごくパワーのかかることです。

今までは1つに載っていた機能が分かれて別々のチームで作ることになり、大きなパワーも工数もかかるので、(ビジネス的な成長を踏まえると、)ドラスティックなリプレイスは難しいという背景がありました。

モジュラモノリスの採用

じゃあどうするのかと言った時に、現実的な折衷案として、モジュラモノリスを採用しています。モジュラモノリスとは、きれいにパッケージ分割が行われたモノリスのことです。モジュラモノリスって名前はついてるんですが、結局普通のモノリスではあるんですよね。ただ「きれいに」というのが大事なポイントです。

具体的に何をやるかというと、まずさっき言ったとおり「フロントとサーバーを分けよう」ということが大前提としてありました。ここは妥協できないというか、「コストをかけてよい」という判断で分離しています。

サーバーサイドはInternalなAPIを作らずに、店舗、商品、注文(など)。先ほど言った、機能ドメインごとに小さなモジュールを1つのアプリケーション内で複数作り、統合された大きなアプリケーションを作る感じになっています。

小さなマイクロサービスではなくソースコードレベルですが、モジュールがたくさんあるような状態を目指すのが、モジュラモノリスとなっています。

もちろんBFFがFATになってしまいます。DBとも直接接続しているし。ただ、FATにはなるものの、いったんBFFで機能を実装して、いわゆる機能ドメインで分割して依存を切り分けていく感じになります。

なのでAS−ISとの違いとして、例えば「店舗」というモジュールと「注文」というモジュールに依存がない状態をまず目指すようなかたちですね。

先ほどのモジュラモノリスを達成した後から、段階的なマイクロサービス化を行っていきます。例えば注文とか店舗とかクーポンとかの各機能がありますが、クーポンチームとか店舗チームとか注文チームが今出前館内でできあがっているので、そういうチームにどんどん処理や機能、InternalなAPIを持ってもらって、機能を移譲していく感じになります。

ドメインごとのロジック処理をマイクロサービスが移譲していく。現実的にチームがないものはいったん請け負う。チームができてきたものは処理ごと移譲するというような、段階的な移行戦略をとっています。

実装レベルで何をやってるかを説明します。いわゆる普通のDDDレイヤリング、コントローラーがあってモデルがあってリポジトリがあるような、簡易なドメイン駆動設計を採用しています。

加えて、機能ドメインごとにパッケージを縦に分割しています。縦と呼んでるのは、先ほど言った店舗とか商品とか(のこと)です。縦ごとに1つのアプリケーションがあるようなイメージで、店舗という機能モジュールの中でコントローラー、ドメインモデルサービス、リポジトリと一連の流れがあります。またこれと別で例えば商品でも一連のコントローラーからモデル、リポジトリという流れがある感じです。

ここはJava、Spring Bootでやっています。Javaのパッケージ階層見るとなんとなくイメージがつくと思いますが、一例を挙げると、chargeというディレクトリの中で、請求機能のアプリケーションのモジュールが構成されています。ここにニュースやお知らせとか、注文とか商品とか。こんな感じで複数のモジュールが存在していて、1つの大きなアプリケーションを成すという感じです。

ここで一番大事なのが、モジュラモノリスとマイクロサービスにおけるドメイン境界ですね。結局大事なのは、どこに機能の境界線があるかです。例えば微妙な機能があったとして、「これは注文の機能なのだろうか、商品の機能なのだろうか」と見極めていくのが大事かなと思います。

ここでちゃんとした境界線を見極めてモジュラモノリスを構築できていると、後からマイクロサービスにすることは対して難しくないというか、容易なのですよね。その処理をそのまま持って行ってもらうだけなので。

なので、モノリスが大事とかマイクロサービスがよいとかということではなく、その時のビジネスや工数に合わせた戦略が大事で。どちらでも後から選べるように、後から意思決定できるようにするのが一番大事なポイントかなと思います。

モジュラモノリスを採用してよかったこと

まず、モジュラモノリスの時点である程度依存が分散されているので、各機能ドメインごとにチームを割り振ることができて、今は3チームで並列開発をしています。

1つのプロダクトを3チームで触るって、けっこうすごいような気がします。その結果開発も活発になって、常にプルリクエストを20個ぐらい、1年半で3,600個ぐらい(まわせている)。けっこう大きいかなあと思います。

コミッターも多くて、1つのレポジトリって4、5人とかだったりすることもあるんですが、50人ぐらいいるような大きな規模のレポジトリになってます。3チームで機能ごとに分担して並列開発。で、高速化ですね。依存が少ないので独立に進めることができます。

先を見据えた柔軟なアーキテクチャ構成が大事

今日のまとめですが、モジュラモノリスにもマイクロサービスにもドメイン境界が存在しているということです。インフラレベルで分けているのがマイクロサービス、アプリケーションレベルで分けているのがモジュラモノリス、というニュアンスがあるかもしれないですが、ドメイン境界が存在しているという観点で、どちらも構造としては同じかなと思います。

結局、先を見据えた柔軟なアーキテクチャ構成が大事で、この“柔軟な”がなにかというと、「意思決定を先送りにできること」があげられるかなと思います。

駆け足でしたが、ご清聴ありがとうございました。これで発表を終わりにします。