フロントエンドの複雑さに耐えるため実践したこと / readyfor-nextjs-first
by
Takepepe
×
Copy
Open
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
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 ご清聴ありがとうございました!