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.6k
Rails の CLI ツールの書き方/writing-rails-cli-tool
sanfrecce_osaka
0
86
Time.zone.parse('dark')/time-zone-parse-dark
sanfrecce_osaka
0
140
外部APIが絡むテストをちょっといい感じに書く/a-little-nice-writing-external-api-testing
sanfrecce_osaka
0
38
gem_rbs_collection へのコントリビュートから始める Ruby の型の世界/contributing-gem-rbs-collection
sanfrecce_osaka
0
630
Rails と人魚の話/rails-and-mermaid
sanfrecce_osaka
0
490
パターンマッチ使ってるかい?(kyobashi.rb)/use-ruby-s-pattern-matching-on-kyobashi-rb
sanfrecce_osaka
0
300
Input object ではじめる入力値検証/input-value-validation-using-input-object
sanfrecce_osaka
0
620
実例で学ぶRailsアプリケーションデバッグ入門 〜ログインできちゃってました編〜/rails-application-debug-introduction
sanfrecce_osaka
2
940
Other Decks in Technology
See All in Technology
みてねにおけるAI-DLC導入活動とAIドリブン開発の現在地/JAWS-UG AI-DLC #2
isaoshimizu
2
330
PM領域でのAI Agentの活用
lycorptech_jp
PRO
0
220
Introduction to Sansan Meishi Maker Development Engineer
sansan33
PRO
0
470
はじめてのDatabricks:技術者向けワークショップ / beginner-workshop
databricksjapan
PRO
0
110
魔法少女がClaude Codeを監視する / ccportal-deck
noriyukitakei
3
120
人気商品が「ちゃんと買える」をつくる ー ECの負荷改善
ykagano
1
190
Introduction to Bill One Development Engineer
sansan33
PRO
0
490
20260903 Tokyo Jazug Night #62 | Azure エンジニアよ、 その環境は本当にセキュアか?
olivia_0707
1
470
サーバー常駐型の 簡易障害調査AI エージェントを作ってみた話
masayoshi
1
520
Beyond the Hype: Practical AI for Your Oracle Database with MCP
thatjeffsmith
1
160
AI駆動開発を組織で促すために
lycorptech_jp
PRO
7
9.2k
いま、生成AIにKaggleをどこまで 任せられるか — ROGIIコンペでの進め方とTips
k951286
1
1.1k
Featured
See All Featured
HDC tutorial
michielstock
2
840
Are puppies a ranking factor?
jonoalderson
2
3.9k
職位にかかわらず全員がリーダーシップを発揮するチーム作り / Building a team where everyone can demonstrate leadership regardless of position
madoxten
69
64k
The Illustrated Children's Guide to Kubernetes
chrisshort
51
53k
<Decoding/> the Language of Devs - We Love SEO 2024
nikkihalliwell
1
310
How Fast Is Fast Enough? [PerfNow 2025]
tammyeverts
3
850
Collaborative Software Design: How to facilitate domain modelling decisions
baasie
1
300
Product Roadmaps are Hard
iamctodd
55
13k
B2B Lead Gen: Tactics, Traps & Triumph
marketingsoph
0
230
Why Our Code Smells
bkeepers
PRO
340
58k
Designing Experiences People Love
moore
143
24k
GraphQLの誤解/rethinking-graphql
sonatard
75
12k
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