こんにちは〜!
テクノロジー戦略本部 開発3部 部長のMJ(@mj3880)です。僕のことは入社エントリをどうぞ!
開発3部では、バイセルの買取業務全般に関する既存基幹システム「GYRO」の運用や、リユースプラットフォーム「Cosmos」における顧客管理システム「CRM」、出張訪問買取システム「Visit」、買取管理システム「Deal」の開発と運用を行っています。
僕はそれらの統括に加え、VisitとDealの開発チームのエンジニアリングマネージャー(EM)も兼任しております。
GYROとCRMは現在リリース済みで運用中ですが、VisitとDealは2024年内のリリースに向けて鋭意開発中です。
今回は、まだ道半ばではありますが、VisitとDealの発足からアーキテクチャや技術選定(主にバックエンド)の推進にあたり、EMとして携わったこと・注視したことなどを、リリース前の振り返りも兼ねてお話ししたいと思います。
入社と開発3部発足と部長就任と新規開発のEMと
僕は2023年3月にバイセルに入社しましたが、その1ヶ月後に開発3部が発足し、部長を任命されました。加えて、Visitの開発もEMとして牽引していくことが求められていました。これらの重大なミッションのスーパーコンボを受け、やる気と不安でいっぱいでした。
Visitとは
「Visit」は、リユースプラットフォーム「Cosmos」における出張訪問買取システムです。ユーザーはバイセルグループの出張訪問査定員の方々を想定しています。
主な機能や特徴は以下です。
- 操作に迷わないUI/UXとスマホ⇔タブレット間のシームレスな連携
- 情報入力の自動化・効率化
- 査定時のミスや差し戻しを削減
- データドリブン経営のためのデータ取得強化
これらを搭載し、最終的には出張訪問買取の粗利向上に繋がることを目標に立ち上がったプロダクトです。
「なぜ必要か(Why)、何が必要か(What)」を知る
データドリブン経営やバイセルグループの拡大に向けて、Cosmosの拡張が重要であり、その一翼をVisitが担うことになるのは入社前から認識していました。そのため、入社後もステークホルダーとのディスカッションの中で開発の目的が揺らぐことはありませんでした。
しかし、ステークホルダーはマネジメントレイヤーの方がほとんどでした。
前述の通り、Visitのユーザーは出張訪問査定員の方々です。彼らになぜVisitが必要なのか、そして彼らは業務を遂行する上でどんなペインを抱えていて、それを解決するには何が必要かをしっかりと見極めることが重要と考え、行動することにしました。
百聞は一見にしかずなのです
現場の方にインタビューをすることも考えましたが、それでは想像の域を脱することができません。本当の現場を知るには現場を見るしかないのです。
僕は出張査定の現場に同行させてもらうことを決意し、複数件のお客様のご自宅に伺い、査定員の方の後ろで見学をさせていただきました。僕はお客様と世間話をすることぐらいしかできませんでしたが、その後のフォローコール(訪問後、お客様に査定の不備等なかったかを確認するための連絡)ではお客様から良い評価をいただけました。
現場を知ってから考えが変わったことは複数ありました。例えば「査定や決裁の待ち時間は削減すべき」と考えていましたが、実際はその待ち時間を利用してお客様との信頼値を上げるコミュニケーションを取り、追加の買取商材を聞き出せたりしており、待ち時間も有効活用できるのだという発見がありました。
こうして、まずは徹底的にドメイン理解に努め、プロダクトの方向性を見定めることに注力したからこそ、今も目指すべきゴールがブレてないんだなと感じています。
焦らず、落ち着いて、理想のアーキテクチャを描く
業務について理解を深めたあと、Visitやその周辺のアーキテクチャ構想フェーズに入ります。
業務ドメインに加え、リユースプラットフォームCosmosの中でVisitが担う役割や機能を整理すべく、ユースケースやそれに紐づくエンティティやドメインの洗い出しを行いました。
例えば、Visitは出張訪問買取サービスですが、買取で必要なドメインとして案件管理や査定、契約といったものがあります。これらは出張訪問だけではなく、店舗買取や宅配買取といった他の買取チャネルでも共通で利用するドメインです。そのため、買取チャネル全体に関わるドメインを扱うサービス(Deal)と、出張訪問特有のドメインを扱うサービス(Visit)とで分ける方針を取りました。(=VisitチームでDealとVisitという2つのサービスを同時開発することに…!)
Dealには案件管理、査定、契約といった概念が違う複数のドメインが含まれますが、業務特性上、一緒に扱われることが多く切り離しづらいため、サービスベースアーキテクチャを念頭に置いた設計を選択し、この形になりました。
スケジュールと理想のトレードオフで良くない判断をする
しかし、困りました。理想像を追い求めましたが、これを実現しようとすると当初予定していたリリース時期には到底間に合わないことがわかりました。DealとVisitという2つのサービスを同時開発する方針を検討してしまいましたから…。
一時、デリバリを最優先で考え、アーキテクチャを妥協して進めようとしたこともありました。
しかし、CTOのキュンさんに相談したところ、「アーキテクチャの理想に強い意志を持ち、どうしたら実現できるかを考えて行動すべきではないか」という助言をもらいました。
スケジュールのプレッシャーに飲まれ、妥協だらけで技術負債も大きく抱えそうなアーキテクチャで楽に進めようとしてしまったのは、反省しかありません。
とはいえ、良くない判断をして方針を見直した結果、改めて議論したアーキテクチャが最適であると再認識しました。スケジュールもスコープ分けをすれば担保できそうなことがわかり、焦らず落ち着いて、理想に対して強い意志を持ち、考え抜く大切さを学べたなと感じています。
Visitを取り巻くアーキテクチャ概観図
ここまでの議論に加えて、出張訪問査定では音声録音や精密な写真撮影といったブラウザでは実現しきれない要件も含まれていたため、Webviewメインのモバイルアプリも作る方針となり、アーキテクチャの概観としては以下のようになりました。
既存基幹システム連携という鬼門
既存の基幹システムであるGYROは、Cosmosにリプレイスしていくことが決まっていたため、当初はGYROとの連携は考えていませんでした。
しかし以前、GYROの更に前のシステムからGYROに移行した際、トラブルが多く発生したためVisitも慎重に導入を進めたいという事業部の要望があり、様々な課題を検討する必要がでてきました。
まず、Visit導入当初では、バイセルの出張訪問査定員は全員GYROを使っている状態です。そこから徐々にVisit利用者を増やしていくので、VisitとGYROが並行で利用されることになります。(GYROにはVisit同様、査定用のモバイルアプリがあります)
その場合、Visitで作成したデータと、GYROで作成されたデータの所在がバラバラになり、データ集計が煩雑になり、不整合も生まれやすくなります。
加えて、GYROには査定員以外のユーザー(特定商材の専門査定部隊や契約管理チームなど)もいるため、Visitで作成したデータがGYRO上で見られないと業務に支障が出ます。
これらの課題解決のためには、Visitで作成されたデータをGYROにも連携して、GYROではVisitのデータも見れる状態を作ることが必要でした。
分散トランザクションの考慮
GYROはそのアーキテクチャの都合上、データ連携にはAPI利用が不可欠だったため、分散トランザクションについて考慮しなければなりませんでした。データ作成・更新時の基本方針は以下としました。
- Write時はまずVisit,Deal側でトランザクションを開始して書き込み実行
- その後GYROにデータ更新のリクエストを投げる
- 成功ならVisit,Deal側でトランザクションをコミットし、失敗ならロールバックをする
また、GYROには様々なUIがあり、それぞれ色々な事業部が利用しています。よって、Visitで作成・更新したデータがGYRO上で書き換えられるというケースもあります。
そのため、初回のスコープでは、データ取得時はGYROのデータを正とする方針を取りました。他ユーザーによるデータの書き換えを踏まえると、最新の状態に保たれているのは常にGYROだからです。Visit,DealとGYROのデータ差分を埋める必要があるシーンでは、必ず書き込みの処理が発生する設計になっているので、その際にVisitとDealのデータを最新状態に更新すれば差分が解消されるため問題ありません。
連携はいつ無くせるのか?
初回スコープと書きましたが、いずれGYROはリプレースされ、完全にCosmosに置き換わる想定です。そのため初期からGYROとの連携を解除することを念頭に設計する必要がありました。
簡単にフェーズ分けすると、大きく三段階に分けられました。
- 第1段階: 出張訪問査定員以外はGYROを操作しデータ更新を行っている
- 第2段階: 全てのユーザーがGYROを閲覧目的のみで利用している
- 最終段階: すべてのユーザーがGYROを利用しなくなっている
現在第1段階ですが、Cosmosの開発が進み、各事業部の方々がGYROでデータを操作しなくなれば第2段階になり、GYROとの連携解除の未来が見えてきます。
とはいえ、それは長い道のりです。GYROの全ての業務ドメインとユーザーをカバーするために必要なサービスや機能の数がかなり多いため、このロードマップの達成は数年かかりそうです。
バックエンドの大きめな技術選定あれこれ
ここまでチーム内外での熱い議論を経て、ドメインを整理し、適切なサービス分割のアーキクチャの方向性が定まり、さらには基幹システムとの連携方針や未来戦略が定まりました。こうしてようやく各レイヤーの技術選定に駒を進めます。各検討項目で一つ記事が書けそうなのですが、今回は主にバックエンド寄りで大きな意思決定をしたもののみに絞り、その概要をお伝えします。
GraphQLを見送ってREST APIを採用した理由
まず、データ通信の方式については、社内ではHasura(GraphQL)が主流になりつつありましたが、敢えてREST APIを採用しました。大きな理由は以下です。
- エンドポイントが明示的なため、APMとの親和性が高く、パフォーマンス劣化への検知や改善活動が行いやすい
- エンドポイント毎にバリデーション追加等、様々なセキュリティ脆弱性を加味した実装がしやすい
- フロントエンドエンジニアがバックエンドの品質を注視せずとも開発が進められる
フロントエンド、バックエンドの観点に加え、チーム体制や状況を踏まえた上で、比較検討して結論にたどり着きました。また、後述のCloud Spannerに対応したGraphQLサーバーで安定的に利用できそうなライブラリが当初見当たらなかったのも採用を見送った理由の一つです。
REST APIを選択した結果、やはりフロントエンドとバックエンドのつなぎ込みの部分ではバックエンドの実装を待たねばいけないため、開発開始当初はテンポが落ちていました。
しかし、バックエンドとフロントエンドで議論して先にOpenAPIのスキーマを定め、以降は作業を分離し、フロントエンドではmockを用意して仮実装するなどの工夫で並行して開発が進めることができています。
改めて振り返ると、やはりフロントエンド側はバックエンドのパフォーマンスなどを意識しないで済むことが逆に生産性向上に繋がったと感じます。
GraphQLを採用検討するにあたり、下記の記事も参考にさせていただきました。 GraphQLを導入する時に考えておいたほうが良いこと
バイセルで初めてのCloud Spanner採用とその理由
リユースプラットフォーム「Cosmos」には認証認可の基盤があり、そこを起点として各サービスのエンドポイントに繋がります。基本的な構成(LBやCloud Run、Cloud Functionsなど)は他のプロダクトに倣っていましたが、RDBに関してはCloud SQLがバイセルでは主流だったところ、DealではCloud Spannerを採用することにしました。他の選択肢としてはAlloyDBなどもありましたが、Cloud Spanner採用の理由は以下です。
- メンテナンスとスケーリングがほぼ不要(これが大きい)
- 年間で合計5分の停止時間はあるので、それを踏まえたリトライ設計は必要
- データベース別の負荷の偏りを気にしてインスタンスを分ける必要性がない
- 開発環境の料金はCloud SQLが最安だが、本番環境での利用でQPSが1万以下なら、0.1ノード単位(=100 processing unit)で調整できるのでコスパがいい
- 社内での利用実績がなかったため、チャレンジをしたい気持ちも正直あった
注意点としては、Cloud Spannerは通常のRDBとは違い、コネクションを張るのではなくAPI経由でのリクエストになります。そのため、レイテンシが若干高まる可能性があります。ただし数ミリ〜数十ミリ秒程度とのことです。
また、Dealのサーバーサイド言語はGolangを採用していますが、開発開始時点(2023年5月頃)ではCloud Spannerに安定的に対応しているORMパッケージもなかったため、Google公式のCloud Spannerへのシンプルな読み書き用クライアントパッケージを利用することにしました。公式なこともあり、クセもなく使いやすかったのでその点も嬉しかったです。
ちなみに、Visitでは永続的なデータ保存はDealに任せ、揮発性の高いデータのみを扱う設計になったため、RDBは使用せず、Firestoreを採用しました。
Googleから提供されているCloud Spannerについて詳細説明がされているスライドを導入時に参考にさせていただきました。 https://lp.cloudplatformonline.com/rs/808-GJW-314/images/Database_OnAir_q2_0409_Session.pdf
オニオンアーキテクチャの採用とその理由
ソフトウェアアーキテクチャは、VisitもDealもオニオンアーキテクチャを採用しました。
各レイヤー(DBとのデータマッピングを行う層やビジネスロジックを書く層など)の依存関係をコントロールして技術負債をなるべく生み出さない実装ができるようにしたかったため、クリーンアーキテクチャを採用しようと検討していましたが、厳密なクリーンアーキテクチャはメンバーの習熟度の影響をもろに受けますし、複雑性も高いため長い時間軸で見ると維持をするのが難しいと考え、よりシンプルなオニオンアーキテクチャを採用しました。
また、DI(Dependency Injection - 依存性の注入)によりテスト環境の構築がしやすいことや、前述の基幹システムであるGYROを連携解除するフェーズが来た際の対応のしやすさも念頭においていたため、採用する運びになりました。
クリーンアーキテクチャ、オニオンアーキテクチャの選定については以下の記事を参考にさせていただきました。 クリーンアーキわからんかった人のためのオニオンアーキテクチャ
まとめ
Visitを取り巻くアーキテクチャや技術選定についてお話しさせていただきました。理想を追い求めた結果、開発難易度も工数も上がってしまいましたが、ここまで実装方針に大きなブレが生じることなく進められてきたので、理想のアーキテクチャを妥協せず意志を持って考え抜くことを諦めないで良かったなと感じています。
そして!もうすぐ我が子たち(Visit,Deal)がリリースできそうな兆しが見えてきています!
初回スコープの機能開発はほぼ終わり、QAや負荷試験、現場トライアルなどのフェーズに入ったので、いよいよです。
引き続き、EMとして、部長として、より良いサービス提供がきるように試行錯誤をして推進していきたいと思います。
最後まで読んでいただき、ありがとうございます。こちらの入社エントリも読んでいただけると嬉しいです。バイセルではエンジニアリングマネージャー(EM)を募集しています。興味がある方はぜひご連絡ください!