Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
ApplicationController の継承を分割してエラーを減らした話/dividin...
Search
Masatoshi Moritsuka
March 01, 2024
Technology
420
1
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
ApplicationController の継承を分割してエラーを減らした話/dividing-application-controller
Masatoshi Moritsuka
March 01, 2024
More Decks by Masatoshi Moritsuka
See All by Masatoshi Moritsuka
拙者、『型は欲しいが型は書きたくない』者たちとの和睦を結び、るびぃにおける型の領地安堵を実現せんと欲す者也 #sekigahara01/sekigahara01
sanfrecce_osaka
4
2.5k
Rails の CLI ツールの書き方/writing-rails-cli-tool
sanfrecce_osaka
0
67
Time.zone.parse('dark')/time-zone-parse-dark
sanfrecce_osaka
0
120
外部APIが絡むテストをちょっといい感じに書く/a-little-nice-writing-external-api-testing
sanfrecce_osaka
0
38
gem_rbs_collection へのコントリビュートから始める Ruby の型の世界/contributing-gem-rbs-collection
sanfrecce_osaka
0
620
Rails と人魚の話/rails-and-mermaid
sanfrecce_osaka
0
490
パターンマッチ使ってるかい?(kyobashi.rb)/use-ruby-s-pattern-matching-on-kyobashi-rb
sanfrecce_osaka
0
280
Input object ではじめる入力値検証/input-value-validation-using-input-object
sanfrecce_osaka
0
610
実例で学ぶRailsアプリケーションデバッグ入門 〜ログインできちゃってました編〜/rails-application-debug-introduction
sanfrecce_osaka
2
930
Other Decks in Technology
See All in Technology
【CEDEC2026】『GRANBLUE FANTASY: Relink - Endless Ragnarok』のバトル制作事例 ~最高のキャラゲーを目指して~
cygames
PRO
0
230
Flutterをカメラで動かしたかった話
sony
1
130
攻撃と防御で学ぶAI時代のプロダクトセキュリティ演習
recruitengineers
PRO
7
2.2k
LLM・AIエージェントシステムベストプラクティス
shibuiwilliam
2
450
SmartHR Engineering Team Deck
smarthr
1
1.2k
Sansan Engineering Unit 紹介資料
sansan33
PRO
1
4.9k
AWS ネットワーク構築でハマった(ハマりかけた) 5選とそこから得た教訓
nagisa53
4
200
FPGAが実現する遠方宇宙の高空間分解能天体撮影 -大型地上望遠鏡の視力を補正する「補償光学」とは?-
komei_mt
0
180
Goでデータパイプラインを作ろう
sansantech
PRO
0
360
JavaScript 研修 (2026)
recruitengineers
PRO
1
460
ボトムアップ文化が強い組織で セキュリティをどう根付かせていくかの現在進行形の話 / Making Security Stick in a Bottom-Up Organization
yamaguchitk333
0
200
強化学習「理論」入門
enakai00
3
3.6k
Featured
See All Featured
End of SEO as We Know It (SMX Advanced Version)
ipullrank
3
4.3k
From Legacy to Launchpad: Building Startup-Ready Communities
dugsong
0
290
Facilitating Awesome Meetings
lara
57
7.1k
Faster Mobile Websites
deanohume
310
32k
AI Search: Implications for SEO and How to Move Forward - #ShenzhenSEOConference
aleyda
1
1.3k
Max Prin - Stacking Signals: How International SEO Comes Together (And Falls Apart)
techseoconnect
PRO
0
360
We Have a Design System, Now What?
morganepeng
55
8.3k
Primal Persuasion: How to Engage the Brain for Learning That Lasts
tmiket
0
400
Leo the Paperboy
mayatellez
8
2.1k
Getting science done with accelerated Python computing platforms
jacobtomlinson
2
410
Improving Core Web Vitals using Speculation Rules API
sergeychernyshev
21
1.6k
How to make the Groovebox
asonas
2
2.3k
Transcript
ApplicationController の 継承を分割して エラーを減らした話 森塚 真年(@sanfrecce_osaka) 2024/03/01 Wantedly x Qiita
Meetup #2 Ruby を用いたプロダクト開発 #wantedly_qiita_meetup
自己紹介 森塚 真年 GitHub: @sanfrecce-osaka Twitter(X): @sanfrecce_osaka Qiita: @sanfrecce_osaka from:
大阪府枚方市 趣味: コミュニティ・勉強会 株式会社エンペイ Ruby3.2/Rails7.0 Node.js v18/Vue.js 3.3/Vuetify 3.4
前提 : この発表での ApplicationController について 具象コントローラの基底クラスを ApplicationController と呼称 呼び分けが面倒なので便宜上 基底クラスを継承した基底クラスも
API の基底ク ラスも ApplicationController 長いので AC という略称も使います
本編
こんなことありませんか?
例 : mastdon(v4.2.8)
class ApplicationController < ActionController::Base # 略 # if/unless/only/except オプション 😇
before_action :store_referrer, except: :raise_not_found, if: :devise_controller? before_action :require_functional!, if: :user_signed_in? before_action :set_cache_control_defaults # skip_before_action 😇 skip_before_action :verify_authenticity_token, only: :raise_not_found # 略 end
# AC を継承した AC 😇 class Disputes::BaseController < ApplicationController #
略 # 継承先の AC で skip_before_action 😇 skip_before_action :require_functional! before_action :set_body_classes before_action :authenticate_user! before_action :set_cache_headers # 略 end
# AC を継承した AC を継承した AC 😇 class Settings::ApplicationsController <
Settings::BaseControl before_action :set_application, only: [:show, :update, :destroy, :regenerate] before_action :prepare_scopes, only: [:create, :update] # 略 end
module Settings module TwoFactorAuthentication class ConfirmationsController < BaseController # 略
# 具象コントローラで skip_before_action 😇 skip_before_action :require_functional! before_action :require_challenge! before_action :ensure_otp_secret # 略 end end end
つらい 😹 AC が関心を持ちすぎている 異なるユースケースの関心が含まれている リスコフの置換原則が守られていない 結果、色んなところで分岐が発生する
enpay でも頻発 障害 バグ 修正漏れ
どのように解決するのか 責務ごとに境界を区切る 境界ごとに AC を作成する AC の直接の親クラスは ActionController::Base API の場合は
ActionController::API 境界ごとにインターフェース( パス) を統一する
module や namespace で 境界を区切る resources :payments, module: :payments do
resources :histories end namespece: :account do resources :settings end
境界ごとに ApplicationController を作成
module Payments class ApplicationController < ActionController::Base before_action :validate_params before_action :authenticate_account!
before_action :authorize! # 略
private # 共通の振る舞いがない場合は空実装 def validate_params end def authorize! # 具象コントローラで共通の処理
end end end
module Accounts class ApplicationController < ActionController::Base # 重複は許容。共通化したい場合は module や
concern に切り出す before_action :validate_params before_action :authenticate_account! before_action :authorize! # 略
private # 共通の振る舞いがない場合は空実装 def validate_params end def authorize! # 具象コントローラで共通の処理
end end end
ユースケースごとに異なる場合 module Payments class HistoriesController < ApplicatonController # 略 private
# 共通の振る舞いがない場合はオーバーライド def validate_params # 具象コントローラ個別の処理 end def authorize! super # 共通処理を呼び出す # 具象コントローラ個別の処理 end
境界を切る基準 URL( パス) payments/{payment_id} URL( パス) をインターフェースと捉える 認証 認可 ロギング
キャッシュ エラー監視 layout の指定 etc
境界を分けていけば どの選択肢も選べそう! そのまま モジュラモノリス Rails エンジン
やってみて
うまくいったこと コードを読み書きする際に考えることが減った 疎結合 分岐減 単純な構成なので理解しやすい Sentry の通知や障害も減った AC で責務ごとの規約・関心を表現できる
一方
やりたかったこと id の部分のインターフェースを統一させたい module( ディレクトリ) ごとに AC を作りたい
# こう書けるとスッキリするが /payments/{payment_id} ではなく # /payments/{id} になる resources :payments, module:
:payments do # ここは /payments/{payment_id} resources :histories end
# param を使って指定すると /payments/{payment_id} になるが resources :payments, param: :payment_id, module:
:payments do # ここが /payments/{payment_payment_id} になってしまう resources :histories end
# これでもいけるけど # module: :payments を 2回 書くのも嫌なので・・・ resources :payments,
param: :payment_id, module: :payments resources :payments, only: [], module: :payments do resources :histories end
scope module: :payments do # 最終的にこんな形に resources :payments, param: :payment_id
resources :payments, only: [] do resources :histories end end
ご清聴 ありがとうございました
None