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
410
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
59
Time.zone.parse('dark')/time-zone-parse-dark
sanfrecce_osaka
0
120
外部APIが絡むテストをちょっといい感じに書く/a-little-nice-writing-external-api-testing
sanfrecce_osaka
0
34
gem_rbs_collection へのコントリビュートから始める Ruby の型の世界/contributing-gem-rbs-collection
sanfrecce_osaka
0
610
Rails と人魚の話/rails-and-mermaid
sanfrecce_osaka
0
480
パターンマッチ使ってるかい?(kyobashi.rb)/use-ruby-s-pattern-matching-on-kyobashi-rb
sanfrecce_osaka
0
270
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
Oracle Exadata Database Service on Cloud@Customer X11M (ExaDB-C@C) サービス概要
oracle4engineer
PRO
2
8.4k
2年前に削除したPHPクラスが、 ある日突然決済をエラーにした
ykagano
1
310
Terraform共通モジュールをチーム横断で“変えられる”運用へ ― リリースと適用の分離
kekke_n
1
3.4k
インフラと開発の垣根を超えていき!〜元AWSインフラエンジニアがAWS開発で奮闘している話〜
hatahata021
3
300
Kaggleで成長するために意識したこと
prgckwb
2
420
Compose 新機能総まとめ / What's New in Jetpack Compose
yanzm
0
320
CDKで書くECSのベストプラクティス、 改めて考え直す2026 #cdkconf2026
makies
3
820
AICoEでAIネイティブ組織への進化
yukiogawa
0
200
AmplifyHostingConstructからSSRフレームワークのためのホスティング設計を考察する/amplify-hosting-construct
fossamagna
1
240
SRE依存からの脱却 運用を開 発チームへ移す、 フルサイ クル開 発体制の実践
joooee0000
0
3.2k
【Claude Code】鹿野さんに聞く 私の推しの並行開発環境 大公開 / claude-code-parallel-2026-07-15
tonkotsuboy_com
12
8.7k
オブザーバビリティ、本当に活用できてる? 〜API連携×生成AIで成熟度を自動評価〜
dmmsre
1
3.6k
Featured
See All Featured
Information Architects: The Missing Link in Design Systems
soysaucechin
0
1k
CoffeeScript is Beautiful & I Never Want to Write Plain JavaScript Again
sstephenson
162
16k
brightonSEO & MeasureFest 2025 - Christian Goodrich - Winning strategies for Black Friday CRO & PPC
cargoodrich
3
750
Conquering PDFs: document understanding beyond plain text
inesmontani
PRO
4
2.9k
エンジニアに許された特別な時間の終わり
watany
108
250k
Design of three-dimensional binary manipulators for pick-and-place task avoiding obstacles (IECON2024)
konakalab
0
490
Marketing to machines
jonoalderson
1
5.6k
Hiding What from Whom? A Critical Review of the History of Programming languages for Music
tomoyanonymous
3
970
Optimizing for Happiness
mojombo
378
71k
The B2B funnel & how to create a winning content strategy
katarinadahlin
PRO
1
420
Navigating Team Friction
lara
192
16k
Building an army of robots
kneath
306
46k
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