Lock in $30 Savings on PRO—Offer Ends Soon! ⏳

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

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

Daiki Miura

April 23, 2021
Tweet

More Decks by Daiki Miura

Other Decks in Technology

Transcript

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

    ▶ Twitter: @daikiii5555 ▶ 最近の趣味: 車輪の再発明         (自作Docker、自作OS、自作Cコンパイラ etc) ▶ PINTORという美術鑑賞アプリの会社で働いています ▶ この前のISUCONでRubyで決勝進出しました🎉
  2. Upgrowが解決しようとした課題 (Controller編) - 複雑化したController Filter (before_action) - 条件付きのbefore_action (:if, :unless)

    - Controllerのサブセットにのみ適用される before_action (:only, :except) - Fat Controller - どこに書いたらいいか明確ではないロジックが全て Controllerに書かれる - 複数のModelを触る場合の一連の手続き - メール送信 - 外部サービス呼び出し - ジョブをエンキュー - 上のようなロジックを ControllerではなくModelに書いてFat Modelになる場合も多い
  3. 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 問題を引き起こす
  4. そもそもRails Wayの何が問題なのか? - ControllerにもModelにも書けない (書きたくない) ロジックがある - 複数のModelを触る場合の一連の手続き - ある特定のケースでのみ行われる

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

    - View - HTTPレスポンスボディを組み立てる - Controllerの責務の一部を切り出したもの - Model - ControllerとViewが負っている責務以外の全ての責務 - ビジネスロジックの記述 - DBへの永続化 - ユーザーからの入力値のバリデーション - コールバック - etc
  6. Upgrowのアーキテクチャ (Model) Record Repository Action Input Model View Result Controller

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

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

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

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

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

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

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

    DB 責務: Actionの結果を表すオブジェクト - いくつかの決まったフィールドを持つ Struct - successかfailureのいずれか状態を表す - failureのときはerrorsフィールドに値が入る
  14. Upgrowと既存のアプローチとの比較 - 課題の分析 (なぜ課題が生じるか?) - Upgrow - ControllerにもModelにも書けない (書きたくない) ロジックがあるから

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

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

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

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

    フルスタックフレームワーク 基本的にはRails Wayに 沿って作ればいい (Rails is omakase) フレームワークは詳細 (詳細 には依存しない) システム全体の設計思想 全体として密結合な設計に し、規約で縛ることでコード 量を減らす 全体として疎結合な設計に するが、コード量は増える
  19. Railsの課題にどう立ち向かうのか? - 「これを使えば未来永劫問題なし」のような万能アーキテクチャはない - Rails Wayが価値を発揮する状況と他のアプローチが価値を発揮する状況は異なる - プロダクト (ビジネス) の複雑さの増大に従ってリアーキテクチャが必要

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