Gaudiy Tech Blog

Gaudiyの技術、開発組織、カルチャーについてお伝えするブログです

UXの向上と開発の生産性を両立するスキーマ駆動開発

こんにちは!ファンと共に時代を進める、Web3スタートアップのGaudiyでエンジニアをしている勝又(@winor30)です。

Gaudiyのプロダクト開発において、最も大切にしていることの1つに、ファンダムな最高のユーザー体験を提供することがあります。

今回は、このファンダムなユーザー体験を提供しながら、プロダクトの成長速度を落とさないために取り組んでいる、UX中心のスキーマ駆動開発についてまとめてみます。

具体的な開発フローや工夫している点についてまとめたので、同じような課題を抱えるチームのご参考になれば嬉しいです!

1. 取り組みの背景

UXに直結するエンジニアリングの領域は、ユーザーが実際に触るフロントエンドが中心になります。そのため、よいユーザー体験を提供するには、フロントエンド開発にしっかりとリソースを割ける状態を作ることが大事だと考えています。

また、プロダクトが順調に成長するためには、よい体験を実現しながらも、バックエンドの領域に近いビジネスロジックを正しく実行したり、機能の拡張性や汎用性を担保することも必要です。

それらの観点から、第一にユーザー体験を、第二にプロダクトの成長速度を落とさないという目的から、スキーマ駆動開発を中心とした開発フローを取り入れました。

2. Gaudiyのアーキテクチャ構成

Gaudiyが提供するプロダクトのアーキテクチャは、下記のようなFE → BFF → BEのような構成になっています。

エンジニアによる実装範囲は特に明確になっていませんが、基本的にFEをフロントエンドエンジニアが、BEをバックエンドエンジニアが開発しています。BFFに関しては、集約や整形ぐらいのロジックしかないため、ケースバイケースで開発をしています。

https://cdn-ak.f.st-hatena.com/images/fotolife/g/gaudiy/20220310/20220310234806.png

3. UX中心のスキーマ駆動開発の概要

基本的には、下記4つの開発アクティビティを大切にしています。

  1. すべてのインターフェースをスキーマ駆動で開発する
  2. スキーマのズレを検知できるようにする
  3. システム全体の結合は開発初期の段階で行う
  4. FEのブロックにならないようにモックを返す

この意図としては、「FEのブロックにならないようにモックを返す」を基本方針とすることで、UXの向上や検証のために必要なフロントエンド開発に、最大限のリソースを割くことができる状態を作るためです。

また、開発初期の段階でBEやBFFをFEときちんと結合させ、実際に結合させた状態を先に作るようにしています。これによって、実際の挙動に限りなく近い状態にすることができ、UXの細かい部分をいつでも確認することができます。

このような状態を維持しながらBEの開発も行えるように、スキーマ駆動開発と継続的なズレの検知により、BEとFEが自律的に開発できる状態を作っていくことが大切だと考えています。

3-1. すべてのインターフェースをスキーマ駆動で開発

Gaudiyでは現在FE → BFFはGraphQLを、BFF → BEはRESTを利用してアプリケーション間の通信を行っています。

そのため、GraphQLではシンプルにGraphQLスキーマを、RESTではOpenAPIを利用してスキーマ駆動開発を行っています。

各スキーマについてはFE、BEのエンジニアが議論しながら作成し、それをPRに上げるところから開発を始めています。これによって、それぞれのエンジニアが自律的に開発でき、開発の生産性をかなり高めることができています

実際に行う際はLive Shareを利用して、同時編集でGraphQLのスキーマを作成することがおすすめです。

3-2. スキーマのズレを検知できるようにする

GraphQLやRESTは、必ずサーバー側とクライアント側が存在します。

そのため、いくらスキーマを利用していても、サーバーとクライアント側で異なるバージョンのスキーマが利用されてしまうことは起こり得ます。これが生じると結局バグが発生してしまうため、プルリクエスト時のCIなどでズレを検知できるようにすることが大切です。

Gaudiyではアプリケーションのコードをmonorepoに配置しており、クライアント側とサーバー側のコードがすべて同じRepositoryに存在しています。

そのため、GitHub Actionでスキーマに変更があったのか? また変更があった場合、生成されたコードでビルドは可能か? をチェックしており、それによってインターフェースのズレを検知できるようにしています。

下記はGithub Actionのサンプルになります。

name: Check OpenAPI Schema for bff

on:
  pull_request:
    types: [opened, synchronize]
    paths:
      # bffのコードが変更されたケースとbeのopenapi.yamlのスキーマが更新されたタイミングで発火する
      - 'projects/bff/**'
      - 'projects/be/openapi.yaml'

defaults:
  run:
    working-directory: ./projects/bff

jobs:
  check-openapi-schema:
    name: check code
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-node@v2
...
      # openapi.yamlからコードを生成し、その後のstepでbuildをし、成功するかチェック
      - name: Generate API Client by OpenAPI Schema
        run: npx @openapitools/openapi-generator-cli generate -i ../../projects/be/openapi.yaml -g typescript-axios  -o ./src/datasources/be/openapi/
      - name: Check build
        run: npm run build

3-3. システム全体の結合は開発初期の段階で行う

開発初期の段階でFE、BFF、BEが通信を行っている状態をつくることによって、開発初期段階でモックを使っていたとしてもリアルに近いプロダクトをチームに提供でき、UXを高めることに繋げられます。

Gaudiyは、体験の検証やQAを頻繁にする組織なので、最新の状態で触ることのできる環境を常に用意することは非常に大事です。そのため、結合自体も早めに実施するということをやっています。

体験の検証やQAについては以下のblogに詳しく書いてあるので、ぜひ読んでいただければと思います。

techblog.gaudiy.com

また、経験上の話をすれば、開発終盤になってFEとBFFや、BFFとBEのI/Fが合わないみたいなケースがかなり多いと思っています。そのため、先にこのリスクを潰しつつ継続的に維持した方が、大きくズレる前に修正もしやすく、トータルのコストが低くなると考えています。

3-4. FEのブロックにならないようにモックを返す

開発初期でシステムの全体の結合を作るには、BEがデータを返却する必要があります。BEでデータを返却できるような状態を作るには、DBとの接続や別サービスとの接続、BEのビジネスロジックの実装などかなり工数がかかります。

しかし、BEの開発をFEに待ってもらうと、それこそUXの最大化に繋がりません。そのため、どういうデータを返すべきかをFEエンジニアと相談しながら、モックデータを返却するような形で、BEの初期は開発するべきだと考えています。

こうすることで、FEが自律的に開発することができ、よりUXの最大化に繋げやすくなります。

また、モックが返却するデータは、FEエンジニアや他のロールの人に相談しながら実際に利用するようなデータを作ることによって、初期段階でもプロダクトを触ったときの体験を少しでもリアルなものに近づけることができます。

4. 開発フローの具体的な流れ

前述の4つの開発アクティビティをベースにしつつ、基本的には下記のようなフローで案件の開発を行っています。

今回の記事を読んで興味がある方は、具体的にどのように開発しているかの解像度を上げるためにも、ぜひ読んでいただければと思います。

4-1. 開発の基本フロー

1:開発案件のストーリーマップとDDDを終わらせる

完璧なものを作る必要はありませんが、全体像やどういう方針で開発するかの認識を揃えておくことで、その後の開発の認識ズレがなく開発しやすいです。

2:FE・BEエンジニアでGraphQLのスキーマを設計し、プルリクエストを上げる

Live Shareなどを使って同時編集でスキーマを作成できるようにするといい感じです。

3:GraphQLスキーマからモックレスポンスを返すような実装をし、最速でFEと結合できる状態を作る

4:FEはGraphQLスキーマからQueryを生成したりして、実際に結合できる状態を作る

このとき、仮にBFFと結合できない場合は、MSW(Mock Service Worker)を利用するとBFFを待たずにlocal開発ができます。(ただし、開発・本番などのリモート環境では確認できません。)

5. BEはOpenAPIを設計し、OpenAPIに沿ったモックレスポンスを返すBEアプリケーションを開発する

6. OpenAPIスキーマからクライアントを自動生成させ、すべてモックレスポンスの状態でBFF ← → BE間を結合させる

7. BEを開発していく

4-2. 継続的に行っていくこと

また、継続的に行っていくこととして、実装するなかでスキーマが変わるときは、必ず担当の別領域の(BE → FE、FE → BE)エンジニアに報告するようにしています。

レビュアーにもアサインするとベターです。

5. さいごに

今回の記事では、Gaudiyで実践している「UX中心のスキーマ駆動開発フロー」についてまとめてみました。

この開発フローを取り入れてから、UXを高めるための開発にリソースを割けるようになり、開発チーム全体の生産性もかなり上がったなと感じています。よければぜひご参考になさってみてください。

もっと詳しく知りたい方がいたら、ぜひMeetyで気軽にご連絡いただけたらと思います!他社さんの取り組みもお伺いしてみたいです。

meety.net