Slide 1

Slide 1 text

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


Slide 2

Slide 2 text

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


Slide 3

Slide 3 text

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


Slide 4

Slide 4 text

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


Slide 5

Slide 5 text

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


Slide 6

Slide 6 text

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


Slide 7

Slide 7 text

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


Slide 8

Slide 8 text

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


Slide 9

Slide 9 text

Redux の参照秩序


Slide 10

Slide 10 text

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


Slide 11

Slide 11 text

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


Slide 12

Slide 12 text

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


Slide 13

Slide 13 text

参照秩序 のガイドライン ファイル構成は
 Re-ducks パターンとしました。
 関心範囲を境界とし、
 Module 毎で管理。
 ├── redux
 ├── A
 ├── B
 ├── C
 ├── D
 ├── index.ts
 ├── reducers.ts
 └── store.ts
 #readyfor_meetup


Slide 14

Slide 14 text

参照秩序 のガイドライン ├── redux
 ├── A
 ├── B
 ├── C
 ├── D
 ├── index.ts
 ├── reducers.ts
 └── store.ts
 ファイル構成は
 Re-ducks パターンとしました。
 関心範囲を境界とし、
 Module 毎で管理。
 Module #readyfor_meetup


Slide 15

Slide 15 text

参照秩序 のガイドライン ├── 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


Slide 16

Slide 16 text

参照秩序 のガイドライン ├── redux
 ├── A
 ├── B
 ├── C
 ├── D
 ├── index.ts
 ├── reducers.ts
 └── store.ts
 この Module 同士は、
 参照関係をもつことができます。
 #readyfor_meetup


Slide 17

Slide 17 text

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


Slide 18

Slide 18 text

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


Slide 19

Slide 19 text

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


Slide 20

Slide 20 text

Module prefix ?

Slide 21

Slide 21 text

参照秩序 のガイドライン
 Api** App** UI** Page** Module prefix を4つに区分。
 prefix により、参照権限が異なります。


Slide 22

Slide 22 text

参照秩序 のガイドライン(prefix: Api***) Api** App** UI** Page** API レスポンスを「取得・保持」するのみ。
 API path と1対1で設け「別 Module は参照しない」


Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

このガイドラインの目的は、
 参照権限を限定することで、
 責務の所在を明確にすることです。
 
 参照秩序 のガイドライン
 #readyfor_meetup


Slide 28

Slide 28 text

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


Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

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


Slide 31

Slide 31 text

Cypress 実践 BDD


Slide 32

Slide 32 text

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


Slide 33

Slide 33 text

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


Slide 34

Slide 34 text

Cypress で実践する BDD
 実践にあたり採用したのが Cypress です。
 Cypress は動作が軽快で BDD に適した
 テストフレームワークです。
 
 #readyfor_meetup


Slide 35

Slide 35 text

Cypress で実践する BDD
 Redux と Cypress は相性がよく、
 Action dispatch で特定条件の再現が可能です。
 こういった場面で、Redux の特性が活きてきます。
 #readyfor_meetup


Slide 36

Slide 36 text

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


Slide 37

Slide 37 text

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


Slide 38

Slide 38 text

No content

Slide 39

Slide 39 text

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


Slide 40

Slide 40 text

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


Slide 41

Slide 41 text

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


Slide 42

Slide 42 text

Cypress で実践する BDD
 チケットを発行し、チケット番号とともに
 要求仕様の「空テスト」をコミット。 
 describe("「支払い方法」そのものが表示されない条件 ", () => { xit("最終確認ページから「リターンを変更する」ために戻ってきた場合 ", () => { // TODO: #567 }); });

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

Cypress で実践する BDD
 ライブラリ側もテストツール群も、
 得意・不得意(オーバースペック)があります。 
 ライブラリ選定の際には「テスト観点」も含め、
 バランスをとりながら選ぶ必要があります。
 
 #readyfor_meetup


Slide 45

Slide 45 text

振り返りと 今後


Slide 46

Slide 46 text

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


Slide 47

Slide 47 text

制御・非制御 Component の I/F
 たとえば、非制御 Component + Form
 前提の props 設計が紛れていた事。
 Form 都合の props を剥がすリファクタリングに
 想定以上の工数がかかりました。
 
 #readyfor_meetup


Slide 48

Slide 48 text

制御・非制御 Component の I/F
 また、制御 Component と比べると、
 非制御 Component は Redux と
 相性がよくありません。
 #readyfor_meetup


Slide 49

Slide 49 text

制御・非制御 Component の I/F
 React on Rails で利用していた箇所としては、
 非制御 Component がベストだった背景がそこに。
 移行に必要なコストに違いないものです。
 
 #readyfor_meetup


Slide 50

Slide 50 text

制御・非制御 Component の I/F
 READYFOR Elements の様な横断的な
 デザインシステムを構築するのなら、
 制御・非制御 Component を選択できる様な
 I/F を初期設計から盛り込むべきです。
 
 #readyfor_meetup


Slide 51

Slide 51 text

これからも Redux を使いつづけるか?
 useContextSelector など、
 Redux(useSelector) の代替となる様な
 React 公式 API の検討も始まっており、
 状態管理ライブラリ選定はやはり悩みどころです。
 #readyfor_meetup


Slide 52

Slide 52 text

これからも Redux を使いつづけるか?
 これからも Redux を使いつづけるか?
 というと「都度検討」だと思います。
 readyfor は機能単位で Next.js を分割しているため、
 状態管理も機能要件にあわせて選択できます。
 
 #readyfor_meetup


Slide 53

Slide 53 text

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


Slide 54

Slide 54 text

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


Slide 55

Slide 55 text

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


Slide 56

Slide 56 text

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


Slide 57

Slide 57 text

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