Slide 1

Slide 1 text

React→Hotwireへ 技術スタック変更した話 nao

Slide 2

Slide 2 text

自己紹介 nao (Fukuoka) ● エンジニア歴:2021/3/31〜 (4年目) ○ Railsは2年ぐらい ● main:株式会社ヤマデン ○ 営業支援部(DXチーム)エンジニア ● sub:合同会社プログラム・キッチン ○ ソリューションエンジニア ● x: (mmm_bbb72) ● hobby:チャリ🚲、登山、音楽

Slide 3

Slide 3 text

基幹システムのリプレイス

Slide 4

Slide 4 text

技術スタック(移行前) ● Ruby on Rails ● フロントエンドをReactで描画 ● サーバーとの通信はGraphQLを利用 ● オンプレからのデータ取得&Pushは node.js ○ odbc driver(AS400に接続) ○ node-mysql2

Slide 5

Slide 5 text

開発体制 ● 正社員エンジニア:1人 (私) ● 業務委託エンジニア:1人 ※うちは商社なので、エンジニアの部署も存在しません。

Slide 6

Slide 6 text

この開発体制で どう進めるのか?

Slide 7

Slide 7 text

● Data migrationの問題 ○ AS400→MySQLへの変換 ● 基幹システムの仕様把握(開発者はもうおらず ...) ○ システム把握量が膨大 ○ 物流システム、受発注システム、請求システム、etc.. ● 多様な技術スタック ○ Railsの他にReactやGraphQL等のモダン技術キャッチアップ 開発の課題

Slide 8

Slide 8 text

処理速度を重視 ● 素早く商品が検索できること ● 受注入力が一部自動化しつつ、処理件数を上げれること ● 電話対応しながら触れること リッチなUIを求められていない(ECサイトもBtoB向けのためシンプル) 見た目はどうでもいい感... 基幹システム開発だし... これ、Reactを使う必要ある?

Slide 9

Slide 9 text

Hotwireの導入調査 ● FetchAPIを利用 ● 非同期リクエストを飛ばし、レスポンスでHTMLを受取る ● 部分的なDOM更新 ○ HTMLの中の要素の中身を置き換え ○ 既存のと置き換え、をマージ ○ は既存のもの使用可能 ■ JavaScriptやCSSはそのまま利用 ■ JSやCSSの初期化処理がなくなりレスポンス向上

Slide 10

Slide 10 text

Turbo Rails top_page Fetch API Turbo Rails cart_page cart_page HTML HTML cart_page Turbo Drive

Slide 11

Slide 11 text

Turbo Rails View(HTML) Fetch API list turbo_frame id=’huga’ Turbo Frame HTML

Slide 12

Slide 12 text

Turbo Rails View(HTML) Fetch API create.turbo-stream.erb form id=”messages_455” submit Turbo Stream append
new_message
id=”messages_456” remove

Slide 13

Slide 13 text

リプレイス開発の途中 今からなら間に合う!

Slide 14

Slide 14 text

Hotwireの利点がうちの開発に適合してた ● ⭐シンプルな開発体験 (開発人数少ない ) ○ Railsとの親和性が高い ○ JavaScriptの記述が少ない ● 高速なユーザー体験 ○ ⭐軽量でレスポンスが速い (要件として求められている ) ● 保守性の向上 ○ フロントエンドが複雑になりにくい ○ ⭐ロジックをサーバーに集約できる (ロジック把握の属人化防止 ) Hotwireの移行決め手

Slide 15

Slide 15 text

元々私がフロントエンド(React)をメインで担当 移行結果 二人でRails開発に専念できるようになった! 🎉 Rails側に移動したので、ここからRailsをメインで触るようになった

Slide 16

Slide 16 text

なんとか移行完了...

Slide 17

Slide 17 text

どこが置き換わる or 更新されるのかを考えなければならない Hotwireの課題感(Turbo Stream等の複数変更)

Slide 18

Slide 18 text

sample:カートページ(数量変更) 削除 削除 削除 XXXX_10 2個 ¥200 + ー 合計 ¥2,500 YYY_50 1個 ¥300 + ー ABC_20 5個 ¥2000 + ー http://xxxxx.com クリック 購入へ進む 戻る

Slide 19

Slide 19 text

sample:カートページ(数量変更) 削除 削除 削除 XXXX_10 3個 ¥300 + ー 合計 ¥2,600 YYY_50 1個 ¥300 + ー ABC_20 5個 ¥2000 + ー http://xxxxx.com Replace Update 購入へ進む 戻る Update 個数に応じ単価も変動

Slide 20

Slide 20 text

update.turbo_stream.erb

Slide 21

Slide 21 text

sample:カートページ(商品削除) 削除 削除 削除 XXXX_10 2個 ¥200 + ー 合計 ¥2,500 YYY_50 1個 ¥300 + ー ABC_20 5個 ¥2000 + ー http://xxxxx.com クリック 購入へ進む 戻る

Slide 22

Slide 22 text

sample:カートページ(商品削除) 削除 削除 YYY_50 1個 ¥300 + ー ABC_20 5個 ¥2000 + ー http://xxxxx.com Remove 合計 ¥2,300 Update 購入へ進む 戻る Update

Slide 23

Slide 23 text

destroy.turbo_stream.erb

Slide 24

Slide 24 text

sample:カートページ(商品全削除) 削除 削除 削除 XXXX_10 2個 ¥200 + ー 合計 ¥2,500 YYY_50 1個 ¥300 + ー ABC_20 5個 ¥2000 + ー http://xxxxx.com クリック 購入へ進む 戻る

Slide 25

Slide 25 text

sample:カートページ(商品全削除) http://xxxxx.com カートの中身は空です。 Remove 購入へ進む 戻る 合計 ¥0 Update 非表示にしたい Update ↑表示したい

Slide 26

Slide 26 text

Turbo Streamを使いすぎると どこを変更しているか把握が難しい

Slide 27

Slide 27 text

カートテーブル全体をラップして 更新する方法

Slide 28

Slide 28 text

sample:カートページ 削除 削除 削除 XXXX_10 2個 ¥200 + ー 合計 ¥2,500 YYY_50 1個 ¥300 + ー ABC_20 5個 ¥2000 + ー http://xxxxx.com 購入へ進む 戻る turbo_frame_tag

Slide 29

Slide 29 text

See, your life gets more complex whenever you add partial updates to the mix. Now you have to care about screen regions, the elements they contain, and how interactions affect them. Good abstractions help, but you can’t shake the additional complexity off. You are just in a more complex realm. This is why we say that Turbo is progressive: go with the happy Turbo Drive path by default — and deviate from it when you need higher fidelity for specific screens or interactions. ↓ 画面の一部だけを更新する仕組み(部分更新)を取り入れると、システム全体がより複雑になります。画面の領域や、その中に 含まれる要素、さらにそれらに対するインタラクションがどのように影響を及ぼすかまで気にしなければならなくなるからです。優 れた抽象化(設計)でこれを助けることはできますが、この追加された複雑さを完全に取り除くことはできません。それは単に、よ り複雑な領域に足を踏み入れるということなのです。 だからこそ、私たちは Turboを「漸進的( progressive)」だと表現しています。基本的には、 Turbo Driveによる「ハッピーなパス (理想的なルート)」をデフォルトとして使用し、特定の画面やインタラクションにおいてより高精度な動作が必要な場合にのみ、 そのデフォルトから逸脱するべきだと考えています。 とある記事の紹介 参考:https://dev.37signals.com/a-happier-happy-path-in-turbo-with-morphing/

Slide 30

Slide 30 text

画面の一部だけを更新する仕組み(部分更新)を取り入れると、システム全体がより複雑になります。 画面の領域や、その中に含まれる要素、さらにそれらに対するインタラクションがどのように影響を及 ぼすかまで気にしなければならなくなるからです。 優れた抽象化(設計)でこれを助けることはできますが、この追加された複雑さを完全に取り除くことは できません。 それは単に、より複雑な領域に足を踏み入れるということなのです。 だからこそ、私たちは Turboを「漸進的( progressive)」だと表現しています。 基本的には、 Turbo Driveによる「ハッピーなパス(理想的なルート)」をデフォルトとして使用し、特定の 画面やインタラクションにおいてより高精度な動作が必要な場合にのみ、そのデフォルトから逸脱する べきだと考えています。

Slide 31

Slide 31 text

開発者の幸福度と応答性 参考:https://dev.37signals.com/a-happier-happy-path-in-turbo-with-morphing/ できるだけ Turbo Streamを使わないほうがハッピーになる 😀

Slide 32

Slide 32 text

ユーザーアクションにより多くの部分が変更される場合 部分更新では処理が複雑になりすぎる。 ページ全体を再レンダリングしたほうがシンプルなのでは? 要はカートの中身をリフレッシュすればいい (Turbo Driveに立ち返ってみる ) でもスクロール位置は保持したい (DefaultのTurbo Driveでは無理)

Slide 33

Slide 33 text

Turboには「Morphing」 というのがある

Slide 34

Slide 34 text

https://turbo.hotwired.dev/handbook/page_refreshes#morphing Turbo Driveの部分適用版。 morphを指定すると、ページのリフレッシュが起こった際、変更があったDOMのみ更新し、そ の他の部分はそのままにする。(デフォルトはreplace) preserve を指定すると、ページのリフレッシュが起こってもスクロール位置は保持してく れる。 morphingについて 今回の利用目的はこちら!

Slide 35

Slide 35 text

やることは3つ ● layoutsファイルに以下を追加 ○ <%= yield :head %> ● controllerはredirectさせる ● erbファイルに以下を追加 ○ <% turbo_refreshes_with %>

Slide 36

Slide 36 text

提案PR出して、置き換え完了🎉 ファイル数が削減され、シンプルな動きで実現可能。 カートが空になった場合の状態も正常挙動にできた。 今の所目立った不具合はない。

Slide 37

Slide 37 text

● Hotwireを使うことでAction Cableの実装もシンプルに ○ turbo_stream_fromを使えば、「Turbo::StreamsChannel」を利用できる ○ あとは更新や削除時にブロードキャストすれば Turbo Streamを受け取れる ● Solid Queueを使ってredisやresqueを削除(現在対応中) ● ECS→EC2への移行検討 ○ Kamalの利用検討 その他(今日話せないこと)

Slide 38

Slide 38 text

総括 ● 開発人数が少ないPJTではRailsの開発に注力できるので開発効率アップ ● TurboStreamなど複数部分の更新は把握が難しい ● (正直)Hotwireのベストプラクティスは分からない... ○ Hotwireを使ってもシンプルに実装する というのを心がけたい ○ Turbo StreamやStimulus利用を避けれないか考える