Save 37% off PRO during our Black Friday Sale! »

Upgrow: Railsアプリの保守性を高めるためのShopifyのアプローチ / Upgrow

Upgrow: Railsアプリの保守性を高めるためのShopifyのアプローチ / Upgrow

E4159c65ac8decc882bfaf10088bd07e?s=128

Daiki Miura

April 23, 2021
Tweet

Transcript

  1. Upgrow: Railsアプリの保守性を高めるための Shopifyのアプローチ 2021-04-23 銀座Rails #32 Daiki Miura

  2. 自己紹介 ▶ 名前: 三浦 大輝 (Miura Daiki) ▶ GitHub: @daikimiura

    ▶ Twitter: @daikiii5555 ▶ 最近の趣味: 車輪の再発明         (自作Docker、自作OS、自作Cコンパイラ etc) ▶ PINTORという美術鑑賞アプリの会社で働いています ▶ この前のISUCONでRubyで決勝進出しました🎉
  3. 目的 1. Upgrowとはどんなアーキテクチャで、どんな課題をどう解決しようとしているのかを 説明する 2. Upgrowと既存のアプローチの共通点・相違点を明らかにする 3. 1., 2. を踏まえた上でどのようにアーキテクチャ選定を行えば良いかの方針をまと

    める
  4. 目次 1. Upgrowとは何か? a. 解決しようとした課題か? b. その課題をどう解決しようとしたのか? 2. 同じ課題に対する既存のアプローチとの共通点・相違点は何か? a.

    Railsでよく使われるアプローチとの違い b. Rails以外でよく使われるアーキテクチャとの違い 3. まとめ
  5. 注意 現在Upgrowの公式GitHubレポジトリ及び公式ドキュメントは非公開になっており見るこ とができません。 この発表は2021年2月25日時点での情報を元に作成しています。

  6. 1. Upgrowとは何か?    解決しようとした課題か?    その課題をどう解決しようとしたのか?

  7. ソフトウェアアーキテクチャのゴール 「そもそもなんのためにソフトウェアアーキテクチャを適用するのか?」 • 保守性の高いソフトウェアを構築するため ◦ ⇔ コードの変更・追加を素早く簡単に行える ソフトウェアを構築するため ▪ どこを変更

    / どこに追加 すればいいのかが明確 ▪ 変更・追加したことによる副作用が小さい ▪ 変更・追加する行数が少ない
  8. Upgrowが解決しようとした課題 - “Rails Way” で開発してるといくつかの課題が生じ、保守性が下がってしまった - “Rails Way” = Railsが提供しているコンポーネントのみで開発する手法

    => Upgrowという独自のアーキテクチャを導入して解決した shopifyでは
  9. Upgrowが解決しようとした課題 (Controller編) - 複雑化したController Filter (before_action) - 条件付きのbefore_action (:if, :unless)

    - Controllerのサブセットにのみ適用される before_action (:only, :except) - Fat Controller - どこに書いたらいいか明確ではないロジックが全て Controllerに書かれる - 複数のModelを触る場合の一連の手続き - メール送信 - 外部サービス呼び出し - ジョブをエンキュー - 上のようなロジックを ControllerではなくModelに書いてFat Modelになる場合も多い
  10. Upgrowが解決しようとした課題 (Model編) - Active RecordのCallback地獄 - 大量のcallback - 条件付きのcallback -

    親クラスから継承されたcallback - Fat Model - Modelに大量のメソッドが生える - どこに書いたらいいか分からないロジック - Validation - Callback - “神Model”、”神Class” - Active RecordのAssociation - 不必要にModel同士を密結合にしている - あるModelのメソッドが別のModelのインスタンスを触る余地を常に与えている - 原則、ModelのメソッドはそのModelに関する処理のみであるべき - associationは遅延ロードされる => N+1 問題を引き起こす
  11. そもそもRails Wayの何が問題なのか? - ControllerにもModelにも書けない (書きたくない) ロジックがある - Modelが責務を負いすぎている

  12. そもそもRails Wayの何が問題なのか? - ControllerにもModelにも書けない (書きたくない) ロジックがある - 複数のModelを触る場合の一連の手続き - ある特定のケースでのみ行われる

    Modelの処理 (特定の場合のみ行われるバリデーションなど ) - メール送信 - 外部サービス呼び出し - ジョブをエンキュー - => Clean Architectureでいうところのアプリケーションビジネスルール - ビジネスロジック (ルール) には2種類ある - エンタープライズビジネスルール - アプリケーションの都合ではないコアなルール - 特定のアプリケーション操作が変更しても影響を受けないロジック - アプリケーションビジネスルール - アプリケーション固有のルール - システムのユースケース
  13. そもそもRails Wayの何が問題なのか? - Modelが責務を負いすぎている - Controller - アプリ外部とのインターフェース - HTTPの仕様を扱う

    - View - HTTPレスポンスボディを組み立てる - Controllerの責務の一部を切り出したもの - Model - ControllerとViewが負っている責務以外の全ての責務 - ビジネスロジックの記述 - DBへの永続化 - ユーザーからの入力値のバリデーション - コールバック - etc
  14. 課題をどのように解決しようとしたか? - 「単一の責任を持つ小さなオブジェクト同士がメッセージを交換して処理を行う」よう にする - 単一責任原則 (SRP) に沿って 既存のコンポーネントを分解・新たなコンポーネントを追 加

    していく - ⇔ コンポーネントに変更を加える理由がただ 1つになるように 既存のコンポーネント を分解・新たなコンポーネントを追加 していく
  15. 課題をどのように解決しようとしたか? Record Repository Action Input Model View Result Controller DB

    View Controller DB Model Rails Way Upgrow
  16. Upgrowのアーキテクチャ (Model) Record Repository Action Input Model View Result Controller

    DB 責務: アプリのコアなビジネスロジックの記述 - エンタープライズビジネスルールの記述 - インスタンスはImmutable - Clean Architectureで言うところのEntity - DBとのやり取りやValidationなどの責務は負わない - ActiveRecord::Baseを継承しない
  17. Upgrowのアーキテクチャ (Model) Record Repository Action Input Model View Result Controller

    DB 責務: アプリのコアなビジネスロジックの記述
  18. Upgrowのアーキテクチャ (Record) Record Repository Action Input Model View Result Controller

    DB 責務: DBとのやりとり - データベースの値を Rubyでそのまま表現したもの - DBとのやりとり以外のロジックは一切持たない - Active Recordパターンから外れる - Row Data Gateway? - ActiveRecord::Baseを継承する
  19. Upgrowのアーキテクチャ (Record) Record Repository Action Input Model View Result Controller

    DB 責務: DBとのやりとり
  20. Upgrowのアーキテクチャ (Repository) Record Repository Action Input Model View Result Controller

    DB 責務: アプリ側から見た永続化レイヤ - 永続化技術 (DBとのやりとり) を隠蔽する - 複数の永続化技術へのやりとりを共通化する - immutableなオブジェクトを返す (Model) - Recordからの返り値 (ARインスタンス) を immutableな別のオブジェクトにマッピングする
  21. Upgrowのアーキテクチャ (Repository) Record Repository Action Input Model View Result Controller

    DB 責務: アプリ側から見た永続化レイヤ
  22. Upgrowのアーキテクチャ (Input) Record Repository Action Input Model View Result Controller

    DB 責務: ユーザーの入力を検証しオブジェクト化する - 入力のバリデーションを行う - ActiveModel::Modelをincludeすることで validationのための便利メソッドやviewとのシーム レスな統合が行える
  23. Upgrowのアーキテクチャ (Input) Record Repository Action Input Model View Result Controller

    DB 責務: ユーザーの入力を検証しオブジェクト化する - 入力のバリデーションを行う - ActiveModel::Modelをincludeすることで validationのための便利メソッドやviewとのシーム レスな統合が行える
  24. Upgrowのアーキテクチャ (Action) Record Repository Action Input Model View Result Controller

    DB 責務: アプリケーション固有のロジックを記述する - ユースケースを記述 - リクエストと1対1対応する - パブリックなメソッドはただ1つのみ (Action#perform) - Result (後述) を返す
  25. Upgrowのアーキテクチャ (Result) Record Repository Action Input Model View Result Controller

    DB 責務: Actionの結果を表すオブジェクト - いくつかの決まったフィールドを持つ Struct - successかfailureのいずれか状態を表す - failureのときはerrorsフィールドに値が入る
  26. Upgrowのアーキテクチャ (Result) Record Repository Action Input Model View Result Controller

    DB
  27. Upgrowのアーキテクチャ (Action)

  28. Upgrowのアーキテクチャ (Controller)

  29. Upgrowのアーキテクチャ Record Repository Action Input Model View Result Controller DB

  30. Upgrowのアーキテクチャ (Association)

  31. Upgrowのアーキテクチャ (Transaction)

  32. 2. 同じ課題に対する既存のアプローチ との共通点・相違点何か?    

  33. “同じ課題” - Controller - 複雑化したController Filter (before_action) - Fat Controller

    - Model - Active Recordのcallback地獄 - Fat Model
  34. 同じ課題に対する既存のアプローチ Rails Developers Meetup 2019 「Ruby on Railsの正体と向き合い方」 by @yasaichi

    さん
  35. 同じ課題に対する既存のアプローチ Rails Developers Meetup 2019 「Ruby on Railsの正体と向き合い方」 by @yasaichi

    さん
  36. Upgrowと既存のアプローチとの比較 - 課題の分析 (なぜ課題が生じるか?) - Upgrow - ControllerにもModelにも書けない (書きたくない) ロジックがあるから

    - Modelが責務を負いすぎているから - 既存のアプローチ - 特定のユースケースと密結合した処理を書く場所がないから
  37. 同じ課題に対する既存のアプローチとの比較 Record Repository Action Input Model View Result Controller DB

    Model View Controller DB アプリケーション ビジネスルールを記述するレイヤ (Service, Form, Interactor) 共通点: アプリケーションビジネスルールを記述するコンポーネントを追加したこと 相違点: Upgrowの方が既存のアプローチよりもコンポーネントの粒度が細かい 既存のアプローチ Upgrow
  38. UpgrowとClean Architecture (同心円状アーキテクチャ) との比較 - UpgrowのアーキテクチャはClean Architecture (の一例である同心円状アーキテクチャ ) と似

    ている Clean Architecture Upgrow Entities Model Use Cases Action Controllers Controller Gateways Repository DB Record
  39. UpgrowとClean Architecture (同心円状アーキテクチャ) との比較 - UpgrowのアーキテクチャはClean Architecture (の一例である同心円状アーキテクチャ ) と似

    ている - => Clean Architectureに従うメリットを享受できる - フレームワーク非依存 - システムをフレームワークの制約で縛るのではなく、フレームワークをツールとして利 用できる - データベース非依存 - DBを簡単に差し替え可能 - ビジネスルールはデータベースに束縛されない - 外部エージェント非依存 - ビジネスルールは外界のインターフェイスに束縛されない - REST API, GraphQL, gRPC - テスト可能 - UI, DB, Webサーバーなどの外部要素がなくてもビジネスロジックのテストを行える
  40. Railsの思想とClean Architectureの思想 (Upgrow) との比較 - Clean Architectureに従うメリットはRailsの思想と反している部分が多い - フレームワーク非依存 -

    システムをフレームワークの制約で縛るのではなく、フレームワークをツールとして利用でき る => Railsは「設定より規約」   システム全体として規約にのっとってプログラミングすることで、余計なプログラ   ミングや設定を省く 
 - データベース非依存 - ビジネスルールはデータベースに束縛されない => RailsはURLからDBまでを密結合にする - 外部エージェント非依存 - ビジネスルールは外界のインターフェイスに束縛されない => RailsはRESTなwebアプリケーションを作るためのフレームワーク
  41. Railsの思想とClean Architectureの思想 (Upgrow) との比較 Rails Clean Architecture (Upgrow) フレームワークに対する考え 方

    フルスタックフレームワーク 基本的にはRails Wayに 沿って作ればいい (Rails is omakase) フレームワークは詳細 (詳細 には依存しない) システム全体の設計思想 全体として密結合な設計に し、規約で縛ることでコード 量を減らす 全体として疎結合な設計に するが、コード量は増える
  42. Railsの思想とClean Architectureの思想 (Upgrow) との比較 このドキュメント(Upgrow) は私を当惑させる。私は Upgrowが 今の形のまま長く使われるようになるとは思わない。

  43. 3. まとめ

  44. Railsの課題にどう立ち向かうのか? - 「これを使えば未来永劫問題なし」のような万能アーキテクチャはない - Rails Wayが価値を発揮する状況と他のアプローチが価値を発揮する状況は異なる - プロダクト (ビジネス) の複雑さの増大に従ってリアーキテクチャが必要

    - 課題を感じた時に初めてRails Wayから外れる - Rails Wayで課題を感じていない場合はRails Wayに乗っかるのが最適 - まだビジネスがそこまで複雑化しておらずビジネス的な不確実性が大きいので、長期的なアプリの保守性の高さよりも 短期的なスピードが求められる - 課題を感じたらまずは課題を明確にする - なぜFat Modelなのか?なぜFat Controllerなのか? - プログラミング (設計) の原則に従って、その課題を解決するような新たな必要最小限のコンポーネントを追加する - SOLID原則 - オブジェクト指向設計の原則 - 「課題を感じる => コンポーネント追加 => 課題を感じる => コンポーネントを追加 => ....」を繰り返す - 新たに追加したコンポーネントをチーム全員が正しく使えるようにする - 設計を明文化して (ドキュメントに落として) 周知する - gemでインターフェースを縛る
  45. Thank you for listening!