一休は技術負債をどう解決したのか ──7年間の取り組み事例から学ぶマイクロサービス化の勘所
アーカイブ動画
モノリスからマイクロサービス化し、アーキテクチャをリニューアル
株式会社 一休
CTO室 プラットフォーム開発チーム 徳武 聡氏
最初に登壇したのは、一休 CTO室 プラットフォーム開発チームの徳武聡氏。プラットフォーム開発チームは、主に全サービス・プロダクトに共通するインフラや技術基盤、機能などの開発・運用を担当している。
「一休.com」の開設は、2000年5月。アーキテクチャは「かなり古いものだった」と徳武氏は振り返る。2008年にVB.NET(ASP.NET)のWebフォームを採用。2015年まで、典型的なモノリスであらゆる処理が一つのアプリケーションで行われていた。
プロダクト側からはいろいろ開発要求があるが、VB.NETのWebフォームでは書きにくいので、Windows系の資産を生かしつつ、サービスを分割することを3年ぐらい行った。
VB.NET自体が言語的に進化しないことがわかり、.NET Coreに資産の転用が難しいことも判明した。そこで2019年からは、モノリス自体のリニューアルを開始。サーバサイドの主力の言語としてGoを選択した。
また、フロントの開発生産性が事業成果に直結することから、Nuxt.jsを使ってフロントを書き換えていくことにしたという。「プロダクト開発要求を牽引力にして、リプレースやサービス分割の推進は、今も続いている状態」と徳武氏は話す。
サービス分割して技術負債を返却していく途上では、カオスな状態になることもあったが、現在はカオスな状態を合理化。「リニューアルがかなり進み、レガシーが少なくなった」と徳武氏は語る。
■考察1「カキヤスサ分割であいまいになるシステム境界」
モノリスはレガシーで変更しにくいので、新機能はスクラッチで開発しやすい技術スタックで実装することが多い。
だが、モノリスの全面的なリニューアルを最終目的とするのなら、この技術的関心事ドリブンな分割は、その目標に対するはじめの一歩になっていない可能性があるからだ。カキヤスサ分割をしたことで、システム境界が曖昧になってしまったのだ。
一休.comには、会員を管理するシステムがあり、このシステムはClassic ASPという古い技術で作られていた。これでは書きにくいため、開発者は新機能をASP.NET Coreで作ることを決めた。
だが、そのシステムにオーナーシップを持つ人がいなかったため、会員管理とは無関係な機能がもりもり実装されていくことになったのだ。さらに、バージョンアップもできず、無理矢理バージョンアップしてリリースしたら、動かなくなったこともあった。
モノリスリニューアルに追い抜かれることもあった。検索処理をApache Solrを導入し、C#でAPI化したところ、モノリス側のリニューアルが進んだ結果、検索だけ別のAPI化になっている合理性がなくなったのだ。
検索APIに採用した技術スタックが相対的に古くなっていたこともあり、最終的に検索APIは廃止され、Goの実装に吸収されることになったという。
「カキヤスサ分割は短期的な生産性は上がるが、逆に新たな負債を追加してしまうこともある」と徳武氏は警鐘を鳴らす。カキヤスサ分割をモノリス脱却や負債の返却に対する有効なアプローチにするなら、次の2つのどちらかの問いに「Yes」で答えられる必要がある。
- ゼロから同じサービスを作った場合でもその境界で分割するか?
- その分割をテコにしてモノリスを置き換えることはできるか?
■考察2「プロダクトの課題を解決する分割は安定する」
2つ目の考察は、プロダクトの課題を解決する分割は安定するということ。レガシー脱却や開発生産性向上だけではなく、プロダクトの課題を解決するためのアプローチとしてサービス分割を採用すると、「安定する分割境界が見つかりやすく、スポンサーが付きやすい」と徳武氏は語る。
この事例として徳武氏が紹介したのが、認証基盤の話だ。一休.comでは会員認証処理がサービスごとに実装されていた。セキュリティを高めるために、ログインの統合を行うことにしたからである。
そのための認証基盤を、ASP.NET(C#)で構築。さらに2段階認証を導入するため、Goでリニューアルをすることになった。分割境界自体は揺らがないので、開発が進めば進むほど担うべき役割が明確になっている。
「プロダクトの課題やビジネスの課題を解決するための開発に乗っかる形で分割、リニューアルできないか目を光らせておくことが重要です」(徳武氏)
■考察3「同じ役割のAPIの乱立、API粒度のばらつき」
3つ目の考察は、同じ役割のAPIの乱立、APIの粒度のばらつきがあったこと。全社横断で見たとき、同じ役割のマイクロサービスが複数でき上がっていたという。しかも明らかに包含関係のあるドメインを扱う、別々のマイクロサービスが出来上がっていたのだ。
「こうしたことに対処するには、全体最適できるのは望ましいが、そのための体制が難しいと感じている」と徳武氏。
その具体的な事例として紹介したのが、カード決済マイクロサービスが3つもできたこと。できること、やりたいことは少しずつ異なるが、外部の決済サービスに対してリクエストを送る機能は同じである。
このようなことが起こった背景には、一休ではボトムアップで開発者が進めることが多く、横断的に管理する人がおらず、局所最適の弊害だ。
サービスの粒度のばらつきの事例としては、小さすぎるサービスができたこと。ビジネス的な課題解決のための開発を引き金にマイクロサービス化を進めると、サービスが担うビジネスドメインに包含関係にあるマイクロサービスができたのだ。
一休会員には会員ランクがある。その会員ランクの業務処理を担当する簡易的なAPIを、キャンペーンの制御のために開発したのだが、一方で認証基盤では一休会員関連の業務処理を集約することになった。
つまり、認証基盤に会員ランク業務は包含される形となる。一休ではマイクロサービスを細かく切っていく方針を採用していない。そのため、会員ランクAPIが独立する必然性がないという結論に至った。
これらのことにより、プロダクト単位で局所最適の弊害を避けるには、そのための体制を作り統制を取る必要がある。
一方で、独立したサービスを作るメリットは、そのような統制が開発の足かせになってしまうことを避けられること。「自律と統制のバランスを取る必要がある」と徳武氏は言う。
そこで一休では全社で使うプラットフォームをマイクロサービスとして作ったり、責任を持つチームを置く体制になった。現在、各プロダクト内では、細かくサービス分割をしないという方向になりつつある。
サービス分割は事前に計画しても、開発現場は常にさまざまな方向から動機付けされており、また開発方針を決めるパラメータは無数にあるため、計画通りに進めるのは難しい。だからこそ、分割のきっかけ、動機を逃さないことが大切である。
その際に気をつけたいのは、「カキヤスサ分割の罠」に注意すること。そして局所最適の弊害を避けるための体制を作り、統制できるようにすることだ。
これらの失敗を踏まえ、現在、徳武氏たちが取り組んでいるのが、全社横断の機能群をマイクロサービス化するという、一休プラットフォーム構想である。
新規サービスの立ち上げの際のエンジニアリング工数を削減し、より高速に事業を立ち上げられるようになる。さらに、マイクロサービス郡をレガシー脱却の武器として、活用できるようになるからだ。
現在は、一休ポイントの管理、決済、会員計情報の管理という3つのマイクロサービス化に取り組んでいる。
一休が決済プラットフォームを構築する理由
株式会社 一休
宿泊プロダクト開発部 バックエンド開発チーム 江口 潤氏
その新たなマイクロサービスの取り組み事例を紹介するべく、登壇したのが宿泊プロダクト開発部バックエンド開発チームの江口潤氏だ。江口氏は、主に宿泊予約サービスのバックエンド開発に従事し、並行して決済のマイクロサービス化にも取り組んでいる。
徳武氏の説明にもあったように、一休.comには、3つの類似の決済モジュールが乱立していた。決済モジュールといってもかなり小さく「APIラッパーに近いものだった」と江口氏。乱立の背景にあったのが、決済予定や決済履歴などの決済関連機能が各サービスに漏れ出していたため、モジュールに集約されていない状況になっていたのだ。
このような状況では、再利用するメリットが少ないため、新しくサービスを追加するたびに決済関連の機能を追加していたのだ。決済プラットフォームでは、再利用性を高めるため、決済予定や決済履歴など決済に関連する機能をすべて集約することとした。
「最終的には一休.comの全てのサービスにおいて、決済機能は決済プラットフォームを活用するという構造で考えています」(江口氏)
現在、昨年10月にリリースされた「ふるさと納税」をはじめ、今後新たに追加されるサービスを中心に、決済プラットフォームの適用を進めている。「宿泊予約やレストラン予約、スパ予約などの既存サービスはまだプラットフォームの導入ができていませんが、順次、移行していく予定です」(江口氏)
決済プラットフォームを活用している一休.comふるさと納税は、昨年10月にリリースした。ふるさと納税の締め切りは12月末。駆け込み需要で大晦日の夜にかなりアクセスが集中するという。
「一休の他サービスでも、特定のタイミングでアクセスが集中することがあります。そのため高アクセスに耐えられる技術を選ぶ必要がありました。特にDBに高負荷がかかるため、決済プラットフォームではAmazon Aurora Serverless v2を採用しました」(江口氏)
Auroraを採用するメリットは、アクセスが集中して高負荷になると柔軟にオートスケールされること。もちろん、アクセスが少なくなると、スケールインしてくれる。この対応で2022年の大晦日は何事もなく乗り切れたという。
決済プラットフォーム構築のドメイン境界をどこに引くか
決済プラットフォームの構築において、まず検討したのが、ドメイン境界をどこに引くかである。「迷ったことが2つあった」と江口氏は言う。一つは会計業務の扱いである。
つまり、一休サービスか、決済プラットフォームのどちらのサービスに持たせるのが得策か。会計は一見、決済プラットフォームの持ち物ではなさそうに見える。
会計業務がなぜ発生するのか。一休ではカード会社から一旦、一休に入金してもらい、各契約施設に手数料を差し引いて振り込むというお金の流れになっている。つまり一休にお金が入ってくるため、会計管理が必要になるわけだ。
会計管理には債務の増減という考え方がある。決済プラットフォーム起点ではカード会社の入金のタイミングで債務が増加し、一休サービスから契約施設に振り込みをするタイミングで債務が減少する。
「2つのサービスにまたがって債務の管理がされることになるわけです」(江口氏)
会計管理の扱いは、決済プラットフォームでも一休サービスのどちらでもいいと思いがちだが、一休サービスに持たせてしまうと、新しくサービスを追加するたびに、同様の実装が必要になる。そこで決済プラットフォームに集約することになった。
また、経理業務の観点からも検討した。経理業務では、会計データを会計システムに連携している。もし、会計管理機能がサービスごとに分かれていると、経理担当者も連携のたびに画面を切り替えるという手間が発生してしまう。
しかし、会計管理を決済プラットフォームに集約すれば、経理担当者の手間を軽減できる。これらのことから、会計管理を決済プラットフォームに集約することを選択したのである。
次に迷ったことは、どこまでを共通化すればよいのか。一休では予約した時点で決済が確定するわけではなく、予約時点では与信枠だけを確保し、後日、決済が日次バッチで動いて、決済を確定するという流れになっている。
この与信枠の確保が予約してから、実際の宿に泊まるまでの期間が長いと、与信枠が途中で開放され、与信枠を取り直すという処理を行っている。バッチで決済の与信枠を取るときに、それなりに失敗するケースが出てくる。
失敗したときは別の利用できるカードを再登録してもらう必要が出てくるので、お客さまにカード変更依頼メールを送ることになるという。
「このメールを送る処理は一休のサービス全体で発生するものになるので、これも決済プラットフォームにまとめたら良いのではという話が出ました」(江口氏)
メールには「決済がうまくいきませんでした。このURLからカードを再登録してください」という文言に加え、予約情報を出すことが重要になる。メールの信頼性を高めるためだ。
だが、予約情報は各サービス側が持っている。そこで決済プラットフォームからは決済に失敗した通知をサービス側に送るだけとし、メール送信は一休サービス側で行うことにした。
「ドメイン知識がない場合は、処理をサービス側に委譲した方がいいという結論となりました」(江口氏)
決済プラットフォームの整合性を担保するための取り組み
次に決済プラットフォーム構築で考えたことは、整合性を崩さないこと。現在の一休のシステム構成は以下図のようになっている。
サービスごとにDBが分割されており、APIによって決済プラットフォームのDBと連携している。DB間の整合性を保つために、「2つのことを行った」と山口氏は話す。
1つは何らかの理由で決済処理に失敗した場合は、すぐにプラットフォームに決済取り消しをリクエストして、整合性を保つという方法を採用している。
もう1つが決済プラットフォームに専用の整合性チャックのバッチをよういしていること。決済の履歴を適宜取得し、それと対になる一休サービスのトランザクションデータをチェックするのである。
もしなかった場合は不整合と判断して、バッチの中で決済取り消しをする。このような手段により整合性を担保しているのだ。決済プラットフォームは既存サービスにはまだ導入されていないが、今後導入を進めていく予定だ。
最後に江口氏は次のように語り、セッションを締めた。
「小さすぎるマイクロサービスは再利用性が低くなるので、できるだけ凝集度の高いマイクロサービスを作ること。次にドメインをまたぐ機能はプラットフォームにまとめること。だが、ドメイン知識がなければ処理はサービス側に委譲すること。第三に整合性を担保するためには、補償トランザクションが失敗する可能性も考慮することです」(江口氏)
多くの質問が寄せられたQ&Aタイム
Q&Aタイムには非常に多くの質問が寄せられた。その一部を紹介する。
Q.サービス粒度の包含関係や分割する必要性は、作る前にわかるべきか
徳武:ある程度そのビジネスドメインに親しんでいるエンジニアならわかるかもしれませんが、難しいケースも多々あります。例えば、包含される側のサービスがビジネス的な要求があって分割しなければいけないことがあった場合や、包含する側のサービスが著しくレガシーだと、カキヤスサ分割してしまうこともあります。
Q.分割しすぎであることを検知するために何をすべきか
徳武:一般的な分割の粒度はありません。開発者はどのくらいいるのか、ソフトウェア資産はどのくらいなのかなど、いろんなパラメータがあるからです。分割しすぎかどうかについては、その判断基準を見ておく必要があります。
ECサイトの場合、お客さまが触る部分については、お客さまの関心事でシステム分割していくと使い勝手が良くなるかもしれません。一休ではユーザーの関心の持ち方で分けていくような形で進めています。検索や欲しいものリスト、口コミなど細かくマイクロサービスを作っていくことは考えていません。
Q.整合性チェックバッチはどのくらいの頻度で流しているか
江口:5分に1回の頻度で実施しています。決済を先に実行し、その後に予約のトランザクションをコミットするという流れにしているので、決済だけ成功して予約データがないといったことを防ぐ目的で実装しています。
Q.整合性を担保する5分というのは、どのような観点から考えたのか
江口:5分という数字に意味があるわけではありません。できるだけ短い時間でバッチを流しています。
Q.バッチではなく、メッセージングサービスを使うことで、失敗リクエストの再送を担保する検討はあったのか
江口:そういう案も検討しました。決済プラットフォームにバッチを作った理由は、できるだけ一休サービス側に負担をかけたくなかったからです。
Q.フロントエンドとバックエンドは同じチームで開発するのか
徳武:フロント業務が中心の人、サーバサイド中心の人はいるが、両方触るケースが多いですね。