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

フロントエンドの複雑さに耐えるため実践したこと / readyfor-nextjs-first

5d9cd19df0e91caac118b793b4f803d5?s=47 Takepepe
February 05, 2021

フロントエンドの複雑さに耐えるため実践したこと / readyfor-nextjs-first

【READYFOR】実践!フロントエンド分離戦略::発表資料
https://readyfor.connpass.com/event/198730/

5d9cd19df0e91caac118b793b4f803d5?s=128

Takepepe

February 05, 2021
Tweet

Transcript

  1. READYFOR x Next.js フロントエンドの複雑さに耐えるため実践したこと
 @ Takepepe


  2. READYFOR フロントエンド分離のゴール
 ▪ BE/FE の責務をわけ、疎結合とすること
 ▪ 時代に即したベストプラクティスを追求できること
 
 #readyfor_meetup


  3. READYFOR フロントエンド分離のゴール これまでモノリスだった RoR の
 アプリケーションサーバーは APIサーバーへ。
 View はJS(TS)だけで完結する姿へ…
 


    #readyfor_meetup

  4. READYFOR フロントエンド分離のゴール Next.js はこういった背景のプロダクトで選ばれる、
 筆頭の選択肢となりました。
 静的キャッシュ恩恵・ゼロコンフィグ、
 SSR ハイブリッドなど、要件に合わせやすいです。
 #readyfor_meetup


  5. 参画の経緯と当時の状況
 READYFOR からお誘いをうけ
 委託参画したのが昨年4月。
 この10ヶ月間で2つの Next.js 案件
 立ち上げをお手伝いしました。
 
 #readyfor_meetup


  6. 参画の経緯と当時の状況
 すでに分離戦略が敷かれていたため、
 わたしの担当は明確でした。
 デザインシステム(READYFOR Elements)を利用
 して、Next.js App を構築することです。
 #readyfor_meetup


  7. 参画の経緯と当時の状況
 ▪ RoR アプリケーション一部に、react_on_rails
 ▪ READYFOR Elements が仕上がっていた
 ▪ Next.js

    製の PoC があった 
 #readyfor_meetup

  8. 参画の経緯と当時の状況 Next.js / SPA 化にともない、フロントエンドが抱える
 表示ロジックが複雑になることが想定されました。
 その複雑さを乗り越える設計・実装を
 どの様に行ったのかを、本日お話します。
 
 #readyfor_meetup


  9. Redux の参照秩序


  10. はじめに手掛けた「実行者SPA」は、
 細かな UI の部分再描画が頻出する要件でした。
 状態管理ライブラリに Redux を選定しました。
 Redux を選んだ理由
 #readyfor_meetup


  11. Redux を選んだ理由
 ▪ 算出値のメモ化と、部分再描画に優れている
 ▪ devtools が他ソリューションと比較し充実している
 ▪ integration test

    が実施しやすい
 #readyfor_meetup

  12. Redux は「秩序が生まれやすい」と定評がありますが、
 使い方次第では「無秩序」になりやすいものです。
 採用の際には、ガイドラインが必要です。
 Redux の懸念点
 #readyfor_meetup


  13. 参照秩序 のガイドライン ファイル構成は
 Re-ducks パターンとしました。
 関心範囲を境界とし、
 Module 毎で管理。
 ├── redux


    ├── A
 ├── B
 ├── C
 ├── D
 ├── index.ts
 ├── reducers.ts
 └── store.ts
 #readyfor_meetup

  14. 参照秩序 のガイドライン ├── redux
 ├── A
 ├── B
 ├── C


    ├── D
 ├── index.ts
 ├── reducers.ts
 └── store.ts
 ファイル構成は
 Re-ducks パターンとしました。
 関心範囲を境界とし、
 Module 毎で管理。
 Module #readyfor_meetup

  15. 参照秩序 のガイドライン ├── redux
 ├── A
 ├── B
 ├── C


    ├── D
 ├── index.ts
 ├── reducers.ts
 └── store.ts
 Module の内訳は以下のとおり。
 State を軸とした定義を保有します。
 state.ts / reducer.ts / actions.ts
 selectors.ts / thunks.ts
 #readyfor_meetup

  16. 参照秩序 のガイドライン ├── redux
 ├── A
 ├── B
 ├── C


    ├── D
 ├── index.ts
 ├── reducers.ts
 └── store.ts
 この Module 同士は、
 参照関係をもつことができます。
 #readyfor_meetup

  17. 参照秩序 のガイドライン ├── redux
 ├── A
 ├── B
 ├── C


    ├── D
 ├── index.ts
 ├── reducers.ts
 └── store.ts
 例えば「A」で発行された
 「A」のための Action を、
 「C・B」でも購読する、
 という参照関係です。
 #readyfor_meetup

  18. 参照秩序 のガイドライン ├── redux
 ├── A
 ├── B
 ├── C


    ├── D
 ├── index.ts
 ├── reducers.ts
 └── store.ts
 この参照秩序が保たれなければ、
 スパゲティコードが生まれます。
 Reducer と Selector で、
 この参照捻れは発生し得ます。
 #readyfor_meetup

  19. 参照秩序 のガイドライン 参照秩序は公式ガイドの
 Basic State Shape を参考に、
 「Module prefix」で明文化しました。
 https://redux.js.org/recipes/structuring-reducers/basic-reducer-structure#basic-state-shape


    #readyfor_meetup

  20. Module prefix ?

  21. 参照秩序 のガイドライン
 Api** App** UI** Page** Module prefix を4つに区分。
 prefix

    により、参照権限が異なります。

  22. 参照秩序 のガイドライン(prefix: Api***) Api** App** UI** Page** API レスポンスを「取得・保持」するのみ。
 API

    path と1対1で設け「別 Module は参照しない」

  23. 参照秩序 のガイドライン(prefix: App***)
 App** UI** Page** アプリケーションで横断的に利用する値を管理。
 また「API・App」Module しか参照できない。
 Api**

  24. 参照秩序 のガイドライン(prefix: UI***)
 UI** Page** Global UI に関連する状態しか持たない。
 また「API・App・UI」しか参照できない。
 Api**

    App**
  25. 参照秩序 のガイドライン(prefix: Page***)
 Page** 全ての値と Action を参照できる。
 Page Component と1対1。


    Api** App** UI**
  26. 参照秩序 のガイドライン(prefix: Page***)
 「下流 Module が上流 Module を参照する」という、
 シンプルな命名規則による参照秩序を設けました。
 Api**

    App** UI** Page** 上流 下流
  27. このガイドラインの目的は、
 参照権限を限定することで、
 責務の所在を明確にすることです。
 
 参照秩序 のガイドライン
 #readyfor_meetup


  28. 責務の所在地が明確であれば、
 設計の属人化が最小限になります。
 そして、必要な責務のみが下流に降りてきます。
 参照秩序 のガイドライン
 #readyfor_meetup


  29. 参照秩序 のガイドライン
 Page** この関係とすることで、特定ページ専用の「Page**」Module は、
 特定ページの処理のみに、専念することができます。
 Api** App** UI**

  30. 参照秩序 のガイドライン
 ▪ 実装中に異常な参照捻れに気付ける
 ▪ コンテキスト理解が容易くレビュー負荷も下がる
 
 命名規則だけで、開発体験はよりよくなる!
 #readyfor_meetup


  31. Cypress 実践 BDD


  32. Cypress で実践する BDD
 表示分岐ロジックが複雑になることが想定されたため、
 integration テストは不可欠としていました。
 そのため、後発の Prj は立ち上げ初期から
 BDD

    を採用しました。
 #readyfor_meetup

  33. Cypress で実践する BDD
 これから作成しようとするプログラムに
 期待される「振る舞い」や「制約条件」、
 つまり「要求仕様」に近い形で、
 自然言語を併記しながらテストコードを記述する。
 引用:https://ja.wikipedia.org/wiki/ビヘイビア駆動開発 #readyfor_meetup


  34. Cypress で実践する BDD
 実践にあたり採用したのが Cypress です。
 Cypress は動作が軽快で BDD に適した


    テストフレームワークです。
 
 #readyfor_meetup

  35. Cypress で実践する BDD
 Redux と Cypress は相性がよく、
 Action dispatch で特定条件の再現が可能です。


    こういった場面で、Redux の特性が活きてきます。
 #readyfor_meetup

  36. Cypress で実践する BDD
 混み入った分岐条件でのみ現れる画面も、
 どの「状態・Action」があれば再現できるのか?
 が、テストコードに落ちてきます。
 #readyfor_meetup


  37. Cypress で実践する BDD
 特定制約時を再現するの事が容易なため、
 テストファイルを細分化できます。
 表示を即座に確認できるため、
 ドキュメントとしても役立ちます。
 #readyfor_meetup


  38. None
  39. Cypress で実践する BDD
 また、OpenAPI 定義によるMock サーバーを、
 Cypress integration テストにも
 活用しました。


    #readyfor_meetup

  40. Cypress で実践する BDD
 「テストを書きやすい」環境は、
 ライブラリ・ツール選択で明らかに
 差が出てきます。
 #readyfor_meetup


  41. Cypress で実践する BDD
 また、ワークフローの工夫も大切です。 
 タスク分解の段階で「書きやすさ」は変わります。
 タスクを細分化し、十分小さくすることです。
 #readyfor_meetup


  42. Cypress で実践する BDD
 チケットを発行し、チケット番号とともに
 要求仕様の「空テスト」をコミット。 
 describe("「支払い方法」そのものが表示されない条件 ", () =>

    { xit("最終確認ページから「リターンを変更する」ために戻ってきた場合 ", () => { // TODO: #567 }); });
  43. Cypress で実践する BDD
 PR とともに実装内容がテストに記されているため、
 PR 概要を詳に書かずとも概要が伝わる状態が理想です。
 
 describe("「支払い方法」そのものが表示されない条件 ",

    () => { - xit("最終確認ページから「リターンを変更する」ために戻ってきた場合 ", () => { + it("最終確認ページから「リターンを変更する」ために戻ってきた場合 ", () => { }); });
  44. Cypress で実践する BDD
 ライブラリ側もテストツール群も、
 得意・不得意(オーバースペック)があります。 
 ライブラリ選定の際には「テスト観点」も含め、
 バランスをとりながら選ぶ必要があります。
 
 #readyfor_meetup


  45. 振り返りと 今後


  46. 制御・非制御 Component の I/F
 READYFOR Elements の再利用は
 とても上手くいきました。
 しかし、一部検討余地がありました。
 


    
 #readyfor_meetup

  47. 制御・非制御 Component の I/F
 たとえば、非制御 Component + Form
 前提の props

    設計が紛れていた事。
 Form 都合の props を剥がすリファクタリングに
 想定以上の工数がかかりました。
 
 #readyfor_meetup

  48. 制御・非制御 Component の I/F
 また、制御 Component と比べると、
 非制御 Component は

    Redux と
 相性がよくありません。
 #readyfor_meetup

  49. 制御・非制御 Component の I/F
 React on Rails で利用していた箇所としては、
 非制御 Component

    がベストだった背景がそこに。
 移行に必要なコストに違いないものです。
 
 #readyfor_meetup

  50. 制御・非制御 Component の I/F
 READYFOR Elements の様な横断的な
 デザインシステムを構築するのなら、
 制御・非制御 Component

    を選択できる様な
 I/F を初期設計から盛り込むべきです。
 
 #readyfor_meetup

  51. これからも Redux を使いつづけるか?
 useContextSelector など、
 Redux(useSelector) の代替となる様な
 React 公式 API

    の検討も始まっており、
 状態管理ライブラリ選定はやはり悩みどころです。
 #readyfor_meetup

  52. これからも Redux を使いつづけるか?
 これからも Redux を使いつづけるか?
 というと「都度検討」だと思います。
 readyfor は機能単位で Next.js

    を分割しているため、
 状態管理も機能要件にあわせて選択できます。
 
 #readyfor_meetup

  53. これからも Redux を使いつづけるか?
 Redux のテスト優位性をあげたとおり、
 フロントに複雑な表示ロジックを置く場合は、
 導入メリットが多いです。
 
 #readyfor_meetup


  54. これからも Redux を使いつづけるか?
 表示ロジックがフロントに寄る場合は採用、
 表示ロジックがサーバーに寄る場合は不採用、
 という切り分けをすると思います。
 #readyfor_meetup


  55. READYFOR の
 フロントエンドの分離戦略は
 スタートを切ったばかりです。
 #readyfor_meetup


  56. またいつか、
 この続きを共有できる事を
 楽しみにしています。
 #readyfor_meetup


  57. READYFOR x Next.js ご清聴ありがとうございました!