Upgrade to Pro — share decks privately, control downloads, hide ads and more …

ホットペッパービューティーにおけるモバイルアプリ向けAPIのBFF/Backend分割 / J...

Recruit
December 13, 2022

ホットペッパービューティーにおけるモバイルアプリ向けAPIのBFF/Backend分割 / JJUG2019Spring

Recruit

December 13, 2022
Tweet

More Decks by Recruit

Other Decks in Technology

Transcript

  1. ⾃⼰紹介 • Nawate Ippei • リクルートライフスタイル • 2016/04に新卒⼊社 • ⼊社してからずっとホットペッパービューティー

    • 仕事内容 • 2016/04 ~ 2018/01 Androidエンジニア • 2018/01 ~ サーバサイドエンジニア https://twitter.com/sakuna63 https://github.com/sakuna63
  2. 今⽇のお話について • モバイルアプリ向けAPIを2つのAPI層(BFF層/Backend層)に分割した取り組み 
 についてお話しします • 現在進⾏系で分割中です 👷 🚧(約100本中、28本のAPIが新構成に移⾏済み) •

    新構成のAPIを利⽤したアプリがつい先⽇(5/13)リリースされました • なので保守・運⽤の経験に基づいて語れることはほぼありません 🙇 • ࠓ೔͸෼ׂ։͔࢝ΒϦϦʔε·Ͱͷؒʹಘͨ஌ݟΛڞ༗͠·͢
  3. アジェンダ 1. BFFとは何か? 2. BFF/Backend分割の⽬的 3. BFF/Backend分割の⽅針 4. BFF/Backendの設計・実装 •

    API設計 • 技術選定 • ソフトウェア設計・実装 5. BFF/Backend構成の開発体制 6. まとめ
  4. アジェンダ 1. BFFとは何か? 2. BFF/Backend分割の⽬的 3. BFF/Backend分割の⽅針 4. BFF/Backendの設計・実装 •

    API設計 • 技術選定 • ソフトウェア設計・実装 5. BFF/Backend構成の開発体制 6. まとめ
  5. BFFとは何か? • BFF = Backends For Frontends • マイクロサービスアーキテクチャの1つ •

    FrontendとAPI Serviceの間に位置するBackendサーバ • 個々のサービスが提供するAPIを集約し、Frontendに特化したレスポンスを返す API Service API Service API Service JSON HTML CSS, JS BFF for Web BFF for App Search 
 Engine Mobile App Browser
  6. アジェンダ 1. BFFとは何か? 2. BFF/Backend分割の⽬的 3. BFF/Backend分割の⽅針 4. BFF/Backendの設計・実装 •

    API設計 • 技術選定 • ソフトウェア設計・実装 5. BFF/Backend構成の開発体制 6. まとめ
  7. 課題に対する打ち⼿ BFF層 Backend層 モバイルアプリ向けAPI 
 (モノリシック) サーバサイドチーム アプリチーム サーバサイドチーム 分割

    APIのBFF/Backend分割を実施し 
 アプリチームのリソースでAPIを開発・リリースできる体制を整えることで ボトルネック(API開発スピード)の改善を狙う
  8. アジェンダ 1. BFFとは何か? 2. BFF/Backend分割の⽬的 3. BFF/Backend分割の⽅針 4. BFF/Backendの設計・実装 •

    API設計 • 技術選定 • ソフトウェア設計・実装 5. BFF/Backend構成の開発体制 6. まとめ
  9. このように分割することに BFF層 Backend層 表⽰要件に関わる処理 業務ロジック アプリ特有の処理 データソース(DB等) 
 のRead/Write REST

    API • BFFにアプリ特有の処理や表⽰要件に関わる処理を持たせる • BackendはREST APIを提供、BFFは複数のBackend APIを呼び出し結果を集約
  10. 処理分担のポイント • BFFはアプリチームで⾼速に開発できるようにしたい • 開発(変更)の早さ ≒ 意思決定の早さ(特に⼤きな組織では) • すばやく意思決定するには 


    「他チーム・他システムへの依存・影響を⼩さくする」のが⼤事 アプリチームで変更の意思決定が完結する処理をBFFに持たせる
  11. BFF/Backend分割イメージ - Before モバイルアプリ向けAPI 
 (モノリシック) サーバサイドチーム アプリチーム 表⽰要件に関わる処理 •

    プレゼンテーションロジック • 表⽰要件に合わせたデータ集約 業務ロジック データソース(DB, 検索エンジン) 
 のRead/Write アプリ特有の処理 • アクセストークンの管理など
  12. BFF/Backend分割イメージ - After サーバサイドチーム アプリチーム BFF層 Backend層 REST API 表⽰要件に関わる処理

    • プレゼンテーションロジック • 表⽰要件に合わせたデータ集約 業務ロジック データソース(DB, 検索エンジン) 
 のRead/Write アプリ特有の処理 • アクセストークンの管理など
  13. 分割⽅針の振り返り 処理分担の基準について • 基本的に、この基準で迷いなく開発できている 👍 • ⼀点悩んだところがあったので次のスライドで紹介 • BFFの開発スピードが早くなるかはこれから検証 🧐(保守・運⽤が始まったばかりなので)

    BackendのAPI粒度(REST)について • 参照系のBackend API(GET)の四分の⼀(24本中6本)がBFF API間で再利⽤できている 👍 • 逆に更新系の Backend API(POST, PUT, DELETE)はBFF APIと1:1で対応することがほとんど
  14. 分割⽅針の振り返り 処理分担の基準について • 基本的に、この基準で迷いなく開発できている 👍 • ⼀点悩んだところがあったので次のスライドで紹介 • BFFの開発スピードが早くなるかはこれから検証 🧐(保守・運⽤が始まったばかりなので)

    BackendのAPI粒度(REST)について • 参照系のBackend API(GET)の四分の⼀(24本中6本)がBFF API間で再利⽤できている 👍 • 逆に更新系の Backend API(POST, PUT, DELETE)はBFF APIと1:1で対応することがほとんど
  15. アジェンダ 1. BFFとは何か? 2. BFF/Backend分割の⽬的 3. BFF/Backend分割の⽅針 4. BFF/Backendの設計・実装 •

    API設計 • 技術選定 • ソフトウェア設計・実装 5. BFF/Backend構成の開発体制 6. まとめ
  16. アジェンダ 1. BFFとは何か? 2. BFF/Backend分割の⽬的 3. BFF/Backend分割の⽅針 4. BFF/Backendの設計・実装 •

    API設計 • 技術選定 • ソフトウェア設計・実装 5. BFF/Backend構成の開発体制 6. まとめ
  17. BFFのフレームワーク選定 • ⾔語はKotlinでフレームワークは実質Spring⼀択で考えた • ⾔語にこだわらなければnode.jsやGolangも候補だった • Springは5.0からKotlinを正式にサポート 🙆 • Spring

    WebFluxならNon-Blocking I/Oのサポートも 🙆 • Spring WebFluxはReactorの学習コストが懸念だが、 
 AndroidチームがRxJavaを利⽤していたので⼤丈夫そう 🙆 Spring WebFluxを採⽤!
  18. BFFの技術選定の振り返り • 「ほんとにアプリチームでAPI開発できるのか?」という懸念はあったが、 
 問題なく開発できそうなことがわかってきている • ほとんどKotlinとReactorの知識だけで書ける • Springは下回りさえ整えてしまえば、API実装で意識することはほぼない •

    特にAndroidエンジニアにとってはかなりハードルが低そう • iOSエンジニアについては要検証... • 実際Androidエンジニアに実装してもらったが 
 「これならいけそう」という感想を得ている 👏
  19. BFFの技術選定の振り返り • Kotlin x Springは違和感なく開発できている 😄 • 「KotlinだからSpringが活かせない」ということは全くない • 「SpringだからKotlinが活かせない」とういことも全くない

    • 個⼈的に気に⼊ってるポイント • SpringはNonnull/Nullableがしっかりアノテートされているので、 
 KotlinのNull安全性が活きる 💪 • Spring公式提供の拡張関数群も充実 • 今後は、spring-fu/kofuのようなDSLにも期待
  20. Backendの技術選定 • あまりこった検討はなし • Java • 要員確保の観点 • 組織的にもJavaエンジニアが多い 🙆

    • Spring Boot + Sprint WebMVC • 社内事例のあった技術スタックを素直に採⽤
  21. アジェンダ 1. BFFとは何か? 2. BFF/Backend分割の⽬的 3. BFF/Backend分割の⽅針 4. BFF/Backendの設計・実装 •

    API設計 • 技術選定 • ソフトウェア設計・実装 5. BFF/Backend構成の開発体制 6. まとめ
  22. Backendのアプリケーション層実装例 @Usecase @AllArgsConstructor public class ReservationUsecase { /** * 予約一覧を返却する

    */ @Nonnull public GetReservationResponse getReservations(...) { //... // GetReservationResponse = レスポンス構造を表すレスポンスDTO(View) // Usecase(アプリケーション層)の時点でViewのインスタンスを返却する return GetReservationResponse.builder() .totalCount(count) .reservations(reservationDtos) .build(); } • アプリケーション層の時点でView(Json)と対応するDTOを返す
  23. Backendのアプリケーション層実装例 • ControllerはUsecaseの返り値をResponseEntityにラップして返すだけ @RestController @AllArgsConstructor public class ReservationController { private

    final ReservationUsecase usecase; /** * 予約一覧を返却する */ @GetMapping public ResponseEntity<GetReservationResponse> getReservations(...) { final var body = usecase.getReservations(); return ResponseEntity.ok() .body(body); }
  24. アプリケーション層 ドメイン層 BFFのアーキテクチャ Repository Repository Repository Service Service Backend API

    リソース リソース リソース リソース リソース リソース 集約結果 集約結果
  25. BFFのドメイン層実装例 • RepositoryはただAPIを呼んで返すだけ • JSON→Kotlin(DTO)間の変換はすべてJacksonが担う @Repository class HairSalonRepository(private val api:

    HairSalonApi) { @Cacheable(cacheNames = ["publishing"], key = "'/hair-salons/' + #id") fun getById(id: String): Mono<HairSalon> = api.getHairSalon(id) }
  26. BFFのアプリケーション層実装例 fun getMessageBox(userId: String, salonId: String): Mono<HairMessageBox> = clientSalonRepository.getById(salonId) .flatMap

    { clientSalon -> salonRepository.getById(salonId) .map { it.toDto() } // サロンが掲載中なら掲載情報を使って返却する // 非掲載(掲載情報の取得に失敗した)ならサロンボードの登録情報を使って返却する .onErrorResume { Mono.just(clientSalon.toDto()) } } // メッセージの検索と、未読数の取得を行う .flatMap { messageRepository.getMessagesBySalonId(userId, salonId) } .map { (salon, response) -> HairMessageBox( total = response.total, salon = salon, messages = response.messages.map { it.toDto() } ) } • Serviceの実装例、複数のAPI(Repository)を呼び出して結果を集約していく
  27. アジェンダ 1. BFFとは何か? 2. BFF/Backend分割の⽬的 3. BFF/Backend分割の⽅針 4. BFF/Backendの設計・実装 •

    API設計 • 技術選定 • ソフトウェア設計・実装 5. BFF/Backend構成の開発体制 6. まとめ
  28. • こういった前提を踏まえると 
 「参照のためのDBリソースを増やす = リードレプリカのスケールアウト」 
 がBFF/Backend構成のキモなのではないか?と思える。 • ちなみに、ホットペッパービューティーではOracle

    DBを利⽤しているため 
 スケールアウトが難しい 😅(※ インスタンス課⾦のため) • Backendレスポンスをキャッシュするなど、別の負荷対策が重要になってくる 余談:BFF/Backend構成はリードレプリカのスケールアウトがキモ?
  29. アジェンダ 1. BFFとは何か? 2. BFF/Backend分割の⽬的 3. BFF/Backend分割の⽅針 4. BFF/Backendの設計・実装 •

    API設計 • 技術選定 • ソフトウェア設計・実装 5. BFF/Backend構成の開発体制 6. まとめ