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