楽楽勤怠の開発体制

西角知佳氏(以下、西角):「はじめてのフロントエンド・バックエンド分離」ということで西角がお話しいたします。よろしくお願いします。

まず簡単に自己紹介から。西角知佳と言います。2015年にラクスに新卒で入社しまして、入社以来「楽楽精算」という商材の運用保守や機能の開発を担当していました。去年(2019年)から新サービスの「楽楽勤怠」の機能開発を担当しています。

そんな楽楽勤怠ですが、まもなくのリリースに向けて鋭意開発中です。今回は、そんな楽楽勤怠の開発における話をしたいと思います。

楽楽勤怠の開発体制からですが、フロントエンドにSPA、Single-page Applicationを採用しています。この図のように、フロントエンドとバックエンドでチームを分けて開発しています。バックエンドでWeb APIを提供しているので、フロントエンドはそのAPIを呼び出して画面の描画を行う作りになっています。ちなみに、私はバックエンドエンジニアです。

ここで、ラクスにおけるSPAの事情をお話ししたいのですが、SPAを採用しているプロダクトは、社内ではまだ少ない状態なんですね。その中でも、フロントエンドエンジニアとバックエンドエンジニアのチームを分けて開発しているプロダクトは、楽楽勤怠が初めてという社内で知見がまだ少ない状態でした。私にとっても、初めての経験でした。

ここで、SPAにおけるWeb APIとは何かということを考えてみたいのですが、フロントエンドとバックエンドのインターフェースであると同時に、やはりボトルネックにもなりやすいところだと思います。ですので、スムーズなAPI開発が、開発全体のスピードにも影響すると考えています。つまり、開発における重要なファクターを握っているんじゃないかと私は考えています。

今回はそんなWeb API開発に焦点を当てて、フロントエンドとバックエンドを分離した開発の知見が少ない中で、どのようにAPI開発を進めているのか、あとはどのような試行錯誤を経て現在の進め方へ至ったのかをお話しできればと思います。

Web API開発の進め方

ということで、さっそくWeb API開発の進め方の紹介に移ります。API開発をするにあたって、チームで決めたことが大きく3つあります。「どのようにAPIを定義するか」「定義したAPI仕様をどのように表現するか」「それらをどのようなフローで回していくか」の3点です。

まず1つ目の「どのようにAPIを定義するか」ですが、こちらは基本的にRESTの原則に従ったRESTful APIとしています。その中でも曖昧な部分もあると思うので、その部分はプロダクト独自のAPIの標準仕様を策定しています。

具体的にはURIの命名規則や、こういうエラーが起こったときにこういうステータスコードで返すみたいなルール、またリクエスト・レスポンスのJSONフォーマットについて、例えば配列の中身がない場合に空配列を返すのかnullを返すのかといった、細かいルールを決めて、プロダクト全体のAPI設計を統一しています。

この策定は、オライリーから出ている『Web API:The Good Parts』という書籍がすごく参考になりまして、それを基に策定しました。API設計の考え方を学ぶのにもオススメの書籍です。

次に、どのようにAPI仕様を表現するかですが、こちらはOpenAPI SpecificationでAPI仕様を記述しています。もしかしたら、Swaggerと言ったほうがわかりやすい方もいるかもしれません。これがテキストベースのYAMLファイルで記述するようになっているので、ソースコードと同一リポジトリで管理しています。ブランチ管理もできるので、API仕様とコードが乖離しにくいような作りになっているんじゃないかと思います。

Swagger UIというツールを使えばドキュメント化もできるので、基本的に開発者が見るAPI仕様は、これでドキュメント化されたものを見ています。

具体的にはこんな感じのドキュメントが生成されて、この色が付いている1行が1エンドポイントになります。この行をクリックしたら詳細が見れる、という感じになっています。

これは、APIクライアントとしてリクエスト送信もできるので、ちょっと試しにこのAPIにリクエストを送りたいときにもサクッと送れるので、すごく便利ですね。

3つ目です。どのようなフローで開発するかですが、開発フローはけっこう試行錯誤を繰り返してきたので、その変遷はこのあと紹介したいと思います。重視した点としては、双方のチームがスムーズに開発を進められることを意識してフローの策定を行いました。

開発フローの変遷

というところで、開発フローの変遷の話にいきたいと思います。これが初期の開発フローの図です。バックエンドチームとフロントエンドチームがいて、バックエンドチームがまず実装の中でAPI設計を同時に行うフローになっていました。

ですので、バックエンドチームの実装が終わったあとに、API仕様がフロントエンドチームに共有されて、フロントエンドチームはその仕様を基に実装やAPIのつなぎ込みを行っている感じでした。比較的シンプルなフローですね。

楽楽勤怠は、ユーザーストーリーのような単位でこのフローを回して開発を続けていまして、メンバーが少ないうちはこれでもよしなに回ってはいましたが、やっぱりチームが大きくなったり、開発のメンバーが増えていったり、あとはフローをどんどん回していくにつれて、いまいちな点が見つかってきました。

まず1つ目が、API設計をバックエンドチームだけで決めているので、フロントエンドチームの意見が取り入れられないような仕様になってしまう問題がありました。ですので、バックエンドチームとしてもフロントエンドにとって実装しやすいAPI仕様になっているのか、不安を抱えながら仕様策定を行っている状況でした。

2つ目が、フロントエンドチームにAPI仕様が共有されるのが、バックエンドチームの実装が終わったあとなので、それまで作業の詳細が見えない問題がありました。ですので、それまで詳細な見積もりが行えなかったりする問題がありました。

そういう課題を抱えていたので、振り返りをしてフローを改善していきまして、現在の開発フローは、こんな感じの図になっています。

大きく変えた点としては、APIの仕様は開発フローの最初に決めるようにしました。これはバックエンドチームが先にたたき台を作ります。それを基にバックエンドチームとフロントエンドチーム全員によるAPIレビューを行います。

レビューが終わった段階で、API仕様は完成しているので、仮にバックエンドチームの実装が終わっていなかったとしても、フロントエンドがモックを作って先行実装が可能になっています。これで、これまでの課題は解決されました。

めでたしめでたし、ということでまとめに入りそうな雰囲気なんですけど、ではなく、もうちょっとだけ続きます。

現在の開発フローにも課題がありまして、具体的にはAPI設計のフェーズを開発フローの最初に移動したことで、APIの不確実性が高まったという問題がありました。というのも、バックエンドチームが実装する中で、不備や新たに見えてくるものもあると思うんですね。

例えば、そのAPIで発生するエラーコードのパターンが追加になったり、API自体が追加で必要だったり、実装してみて初めてわかることもそこそこあると思います。ただ、API設計は決まってAPI仕様としても一応完成はしているので、APIの仕様変更となってしまうと、フロントエンドチームが先行実装していた場合に手戻りになってしまう問題がありました。

ですので、これを踏まえた今後の展望として、バックエンドチームの実装を待って今まで並行実装をやっていたところを、直列で実装したほうが実はトータルの効率がいいんじゃないかという仮説があるので、次の機能の開発で試してみようとしているところです。

これを見て、「本当に?」と思う方がいると思うので、軽く補足です。これというのは、すべての機能で適用するわけではなくて、単純な機能では今まで通りの進め方で進めて、手戻りのインパクトが大きい、APIのレスポンスを基にフロントエンドで何か処理をするような機能で試してみようとしています。

あとは、チームの状況も関係していて、フロントエンドチームのほうがバックエンドよりもリソースが少ない状況にあるので、なるべくフロントエンドの手戻りを減らしたいという思いもあります。また、まずは試してみることが大事だなと思っていて、やってみることでわかることもあるということで、試行錯誤してみることが大事なんじゃないかなと思っています。

これらを踏まえて、まとめになります。文章にすると陳腐な感じになってしまうのですが、チームで重視することや、チームの状況を踏まえて開発のフローの進め方を考えることが大事だなと思っています。あとは、改善点を明らかにするためにも、まずはやってみること。そこでやってみて、初めて気付くこともあると思うので、試行錯誤は大切だなと思っています。

まだまだ楽楽勤怠チームとしても試行錯誤を続けている段階だと思うので、これからもチームにあった進め方をバランスを見つけながら模索していきたいなと考えています。以上になります。ご清聴ありがとうございました。

質疑応答

司会者:ありがとうございました。まさに今開発している新規サービスからの発表でした。質問にいきたいと思いますが、「Swaggerですが、認証が必要なAPIがある場合にはどうやってSwagger UIを試しましたか?」。

西角:認証もSwagger UI上でできますね。トークンを入力して、認証といったことも、Swagger UIの機能でできるようになっているので、認証ありのAPIに対してもリクエストを送ることができます。すごく便利ですね。

司会者:それって1回認証ありきでないと動かないAPIでしょうか。

西角:はい。

司会者:それはどういうような動かし方になるんですか。

西角:トークンで認証するようなものだと、まずはトークンを発行するエンドポイントを叩いて、そこで得たトークンを、Swagger UI上の認証情報を設定するフィールドに設定すれば、以降はそれがリクエストヘッダーに付いた状態でリクエストが送れます。

司会者:なるほど。他にもう1個質問があって、「OpenAPI Generatorのようなコード生成機能は使用されていたのでしょうか?」という質問が来ています。

西角:そうですね。そこはまだやっていないです。モックをフル活用するなら、コード生成機能を使ってモックを作ってしまうのもありなのかなと思うのですが、今は、フロントエンドが先行実装できたらやるというような流れになっているので、あまり検討もできていないです。

司会者:わりとドキュメンテーションやテスタビリティみたいなところが、今使っている主なモチベーションですかね。

西角:そうですね。主にドキュメンテーションですね。

司会者:あともう1個きています。「REST APIでリソースが存在しない箇所やリソースを跨ぐ必要が出てきた場合は、どうルール化をしていますか?」という質問なんですが。

西角:「リソースが存在しない場合」。何かGETでID指定したときに、そのIDが存在しないですよということでしょうか?

司会者:REST APIでどうやって整合性を取っていくのかとか、そういうことかな?

西角:それで言うと、基本的には一覧画面などの複数のリソースを跨いで表示する画面は1画面1エンドポイントにはなっていますね。ただ処理に時間がかかるエンドポイントで整合性を気にしなくていいものであれば別エンドポイントに分けたうえでフロントエンドでは並行で呼び出してもらって時間短縮する、というのはやっていますね。回答になっているでしょうか?

司会者:そうですね。ありがとうございました。