kintone フロントエンドリアーキテクチャプロジェクトで大切にしていること

kintone フロントエンドリアーキテクチャプロジェクトリーダーの @koba04です。

昨年末から、kintone フロントエンドリアーキテクチャをプロジェクト(フロリア)として再構成してスタートさせました。フロリアという名前は社内での公募により決定しました。

今回はプロジェクトで目指していることについて紹介します。本プロジェクトの開始前に Cybozu Meetup で話したスライドや動画も公開されているのでよければ見てください。

speakerdeck.com

www.youtube.com

これまでの取り組みについては下記の記事にて紹介しています。

blog.cybozu.io

3 行まとめ

  • kintone のフロントエンドのリアーキテクチャをプロジェクトとして取り組んでいます
  • ただ、Closure Tools からの移行を行うだけではなく、自律的に活動できるような体制・アーキテクチャを目指してフロントエンドを分割します
  • オーナーシップを明確化し認知負荷を減らすことで、挑戦・失敗ができる環境作りを目指しています

フロリアのゴール

フロリアでは下記の 3 点をゴールとして掲げています。

  • 全てのページが React によって表示されている​
  • フロントエンドが技術的にもチーム的にも分割されている​
  • ユーザー体験に関する指標に対する計測が行われており、チームの関心ごとになっている

今回は「全てのページが React によって表示されている​」と「フロントエンドが技術的にもチーム的にも分割されている​」について詳しく紹介します。

全てのページが React によって表示されている​

これは文字通り、現在 Closure Tools によってページの表示が行われているものを React に置き換えることです。

現状

現状 kintone のフロントエンドは Closure Tools によって構築されており、現代のフロントエンドの実装とはかなり異なる形の構成・実装となっています。Closure Tools は Google が開発しているフロントエンド開発のためのツールセットで 10 年以上前から存在します。

developers.google.com

Closure Tools では、Closure Library によってアプリケーションを構築し、Closure Compiler によってバンドル・JSDoc による型チェック、Minify、チャンク分割を行い、Closure Templates によって HTML 文字列を安全に組み立てます。

Closure Tools はアプリケーションで使うライブラリやバンドル、型チェック全てを結合的に提供することで、安全さや高い圧縮性を実現しています。その反面、Closure Library だけを置き換えるといった個別の置き換えが難しいです。また npm パッケージの導入についても、Closure Compiler 互換な JSDoc を提供していないため気軽に取り入れることもできません。

その結果として Closure Tools からの段階的な移行が進まず、世の中との乖離によって学習コストの高さが目立つようになってきました。Closure Tools に関する情報が少ないだけでなく、最近では React や Vue を使ってアプリケーション開発の経験をベースにしている開発者が多いため、オンボーディングの観点でも大きなコストになっています。

React や Vue により一般的になった宣言的な UI コンポーネントを用いた開発は、開発速度やアプリケーションの品質にも大きく寄与します。Closure Library にもコンポーネントという概念はありますが、DOM のようなクラスベースによる Mutable な操作で UI を更新していく形になります。React や Vue とはパラダイムの違いが大きく、ただライブラリが違うという以上の影響があります。

今後

Closure Tools から置き換えることは、今後のフロントエンド開発を加速させていくためにも避けて通れないと考えました。置き換え先としてはいくつかの観点による比較から React を選定しました。なぜ React を選んだのかという点はここでは述べませんが、選んだ観点や理由については ADR(Architecture Decision Record) として残しています。ADR については後述します。

ゴールとしては全てのページが React によって”マウント”されていることをプロジェクトゴールの一つとして挙げています。一部の複雑で外部仕様にも強く依存するような Closure Library のコンポーネントについては移行が難しい可能性があります。そういったコンポーネントについては別途置き換えていくことを想定しています。実際には React への置き換え以外にも Closure Tools が担っている部分を TypeScript や webpack、Vite などで置き換えることも含まれます。webpack や Vite などのツールに関しては担当チームが自由に技術選択できる構成を取っています。チーム分割については後述します。

React に関しては、現状は全チームが使うということを前提にしています。この選択は技術的な独立性を下げる側面はありますが、コンポーネントの共有しやすさなどのメリットを獲得するために選択しています。もちろん将来的に React から別のライブラリに置き換えるような未来は想定しています。その際には分割した単位で置き換えに取り組むことが可能であるため移行は進めやすいと考えています。

kintone のアプリケーションでは開発用のページなども含むと 100 ページ以上あります。また、ユーザーが JavaScript によってアプリケーションを拡張できる仕組みも持っているため、技術的な移行の際に検討すべき点は多いです。そのため、規模や難易度を考慮すると長い道のりになることが想定されます。ですが、小さい単位でリリースを行いながら着実に置き換えを進めていきたいと考えています。

フロントエンドが技術的にもチーム的にも分割されている​

モノリスな構成からの脱却

二つ目のゴールとして、フロントエンドを分割することを挙げています。現状の kintone のフロントエンドは 1 つのモノリスなアプリケーションになっています。モノリスであることによるメリットももちろんありますが、影響範囲が把握しづらく広範囲に及ぶというデメリットが目立っています。これは Closure Tools からの移行を難しくしている要因の一つでもあります。

なぜこれをゴールに設定しているのかというと、たとえ Closure Tools から React に置き換えたとしても、置き換えた結果がモノリスな React アプリケーションになっている場合には、一部の問題しか解決出来ていないからです。Closure Tools を置き換えることによって改善される部分も多くありますが、置き換えた結果がモノリスな React アプリケーションになっていると依然として変更のしづらさは残ると考えています。今回のような大掛かりなリアーキテクチャを今後再び行うことは避けるべきで、そのためにも変化しやすい構成になっていることは重要です。

アーキテクチャとチーム(組織)体制をセットで考える

変化しやすくするためには分割することで影響範囲を明確化し、分割したアプリケーション単位でオーナーシップを明確にすることが重要だと考えています。アプリケーションを分割をする際には、アプリケーションのアーキテクチャを変えるだけでは不十分で組織やチームの体制も一緒に変える必要があると判断しました。これについては最近日本語版が出版された「チームトポロジー」という本でも紹介されています。下記はその一部です。

  • 逆コンウェイ戦略: 「チーム構造と組織構造を変化させることで、望ましいアーキテクチャを実現する」
  • システムのアーキテクチャと組織のアーキテクチャが対立する場合、組織のアーキテクチャが勝つ」(ルース・マラン)

フロリアのチーム体制についても分割したチームがオーナーシップを持てるようなゴールの設定やメンバー構成を意識しています。「チームトポロジー」を読むことで、自分達の目指している方向ややり方に対して自信を持つことができました。

チーム単位の Monorepo によりオーナーシップを明確に

フロリアでは、職種に関わらずチームがゴールを達成するために必要な人がチームに参加するクロスファンクショナルな形を採用しています。加えてチームをクロスファンクショナルなメンバーで構成するだけでなく、アプリケーションの構成においてもチーム単位の Monorepo を構成するなどチームの独立性を意識した構成になっています。下記はそのイメージです。

frontend/
    {teamA}/
        applications/
            {applicationA}/
        packages/
            {packageA}/
    {teamB}/
        applications/
            {applicationB}/
        packages/
            {packageB}/
            {packageC}/

チーム単位の Monorepo という少し特殊な構成を採用している理由はチーム間の結合度をより下げるためです。チーム単位で分けることで依存パッケージなどの重複が発生しますが、Monorepo 内でのパッケージの管理方法やビルド、テストなどを各チームがオーナーシップを持って自由に決めることができます。またアプリケーションやパッケージに対するオーナーシップも明確になります。

この構成の場合、チームを超えてパッケージを利用する場合には、パッケージレジストリにパブリッシュしたものを使う必要があります。これにより、パッケージ作成者はパッケージの更新時に他のチームのアプリケーションで壊れていないかを都度確認する必要はなく、セマンティックバージョニングに従ってリリースすればよくなります。パッケージの利用者は都合のいいタイミングでパッケージの更新を自身のアプリケーションに適用できます。この構成の場合、アプリケーション全体で実装の不一致が発生することが想定されますが、チームが独立して活動できることの方がアーキテクチャの優先事項であると判断しています。もちろん、ユーザーの体験に大きな影響を与える変更などについては、メジャーバージョンアップとしてリリースし適用のタイミングを合わせるなどの手段を取ることもあります。

失敗をリカバリ可能にして挑戦を増やす

チームやアプリケーションの単位を明確にして分割していくことで、影響範囲を明確化して挑戦しやすくなることを狙っています。そのためには、挑戦が失敗した場合にも影響範囲が限定的でありリカバリ可能な状態であることが重要だと考えています。一般的に、何かに挑戦した結果の失敗というのは見えやすいものです。一方、何もしない結果として失敗しているという状態もあると考えており、この場合は学びを多く得ることはできません。このような挑戦しないことによる失敗が発生しない状態を作りたいと考えています。

チームやアプリケーションの構成は変化することを前提に設計する

チームやアプリケーションの単位は変化していくことを前提に考えています。適切なチームやアプリケーションの単位は、チームの状態やプロダクトの方向性やステージによって変わります。その時々でベストな構成を選択できるよう、チームやアプリケーションの形が柔軟に変化できる状態を目指しています。チーム単位の Monorepo 構成はその取り組みの一つですが、それだけでなくアプリケーションの構成とチーム構成を疎結合にするための仕組みづくりも必要であると考えています。

ADR(Architecture Decision Record) により決断を記録し共有する

各チームが自律的に取り組むことで「決断する」の数は増えていきます。フロリアでは決断を ADR として記録すること推奨しています。ADR に決断を残すことでチームでの合意形成がスムーズになるだけでなく、将来振り返った時に決断に対する「Why」や検討事項を確認できます。さらに、他のチームが意思決定する際にも参考にできます。

参考までに、Mira というチームで記録されている ADR は下記の通りです。

  • Miraのテスト基本方針
  • フロントエンドの設計方針
  • Miraのアクセシビリティ対応方針
  • フロントエンドビルドでのチャンク分割方針
  • Miraの開発フロー
  • Miraの静的解析とテストのCI方針
  • Miraの非同期通信周りの設計
  • Jestのtransformerの方針
  • テストコードにおけるdata-testidの付与ポリシー
  • CSSのクラス名の命名規則

ADR のテンプレートは下記です。

# Title

## Status
Draft (📝) / Proposed (🙋‍♀️) / Rejected (🙅) / Accepted (👍) / Deprecated (💥)

## Decision
決めたことについて書く

## Context
Decision に至る背景情報など

## Consideration
比較・検討した内容について書く

## References
参考リンクなど

ADR は比較的気軽に書けるドキュメントであり、フロリアだけでなく Cybozu の他のチームでも採用が進んでいます。

オーナーシップのない共通化は避け、重複を許容する

チーム単位でアプリケーションを分割することで、アプリケーション全体で保証すべき一貫性の確保やチーム間での重複実装といった懸念を感じる方もいるのではないでしょうか。フロリアでは、たとえ同じような実装だとしてもチーム間で不必要な共通化はしないという方針を取っています。例えば、HTTP Request を扱うモジュールはどのアプリケーションでも必要になるため共通化したくなりますが、どのチームがオーナーシップを持つべきかは明確ではありません。モジュールを共有することで変更する際に考慮すべきことやステークホルダーが増え、結果として変更に対するコストが高くなります。このようなモジュールは各チームで必要になりますが、必ず同一の実装になっている必要はありません。各チームがそれぞれで実装してチームに閉じてメンテナンスすることで結果としてコストは低くなると考えています。プログラムを書くときはどうしても DRY (Don’t Repeat Yourself) にしたくなりますが、本当に共通化する必要があるのかをじっくり考えることが重要だと考えています。オーナーシップのない共通化は避けることを前提として、その上で共通化すべきものについてはオーナーシップを明確にした上で共通化します。

横断の関心事に対してはチームを作りオーナーシップを割り当てる

アプリケーション全体で保証すべき一貫性に関しては、そのためのチームを作る場合もあります。例えばユーザー体験に関してはアプリケーション全体を通して統一的な体験を提供すべきです。フロリアではユーザー体験に対してオーナーシップを持つチームが存在します。このチームが kintone というプロダクトのユーザー体験に責任を持ち、各チームが統一的な体験を提供できるための手段を提供します。

こういった横断的なチームを作ることで陥るアンチパターンとしては、アプリケーションを開発しているチームとの連携がうまくいかず、結局使われないものを作ってしまうパターンが挙げられます。そうならないよう、現状は各アプリケーションのチームにもメンバーとして参加してもらうことでチーム間でコラボレーションできるような体制を作っています。

チーム間のコラボレーションを活発にしてサイロ化を防止する

チームを分割した結果として、各チームでサイロ化が発生する可能性があります。前述した通り、分割することでチームそれぞれがオーナーシップを持てる体制を目指しますが、それはチームの中で完結して問題を解決しなければならないという意味ではありません。必要に応じて他チームの助けを借りたり他のチームの学びを活用することで、チームの成果を最大化できます。チーム間のコラボレーションを如何に発生させるかはこのプロジェクトの成功に関わる重要な点であると考えています。まずは、各チームがコントリビューションを受け入れられる体制を作ることや、コラボレーションする場を作る必要があります。この点についてはスクラムマスターの @ama_ch に全体のスクラムマスターとして入ってもらい取り組んでいます。

現在は 4 チーム に分かれてそれぞれのチームがゴールを持って活動しています。まだまだスタートしたばかりではありますが、各チームがそれぞれこれまでになかった様々な挑戦に取り組み学びが共有されています。今後、各チームから様々なアウトプットが出てくると思いますので期待していてください!

このようなコラボレーションに対してオープンでいる姿勢はプロジェクト内に留まらず、社内や社外にも広げていきたいと考えています。その結果社内・社外に関わらず多くの人を巻き込んでプロジェクトを成功させたいと考えています。

最後に

本記事では、kintone のフロントエンドを刷新していく上で大事にしている点について紹介しました。リアーキテクチャが完了するまでには長い時間を必要です。しかしながらプロセスをインクリメンタルにしていくことでプロジェクトの成果を持って開発できる状態を目指していきます。また、リアーキテクチャによって実現するアプリケーションの分割やチーム体制については、フロントエンドだけにとどまらずフロントエンドをきっかけにアプリケーション全体に広げていきたいと考えています。

現在、フロリアとしては下記のポジションの募集を行なっていますのでご応募お待ちしています!

cybozu.co.jp

cybozu.co.jp