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
81
Time.zone.parse('dark')/time-zone-parse-dark
sanfrecce_osaka
0
130
外部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
ブラウザで変わるID連携(OAuth/OIDC Numa (Immersion) Workshop 2026)
oidfj
PRO
0
350
アクセスキー流出時の対応で再認識した攻撃側と防御側の非対称性について
kazzpapa3
1
440
Oracle AI Databaseデータベース・サービス: BaseDB/ExaDB-Dの可用性
oracle4engineer
PRO
1
960
[ホンマでっか SRE] あなたはなぜ SRE に?
_awache
0
360
All About Sansan – for New Global Engineers
sansan33
PRO
1
1.5k
[RSJ26] Flow as Flow: Modeling Robot Velocity Fields as Probability Velocity Fields
keio_smilab
PRO
0
110
Distributed Transactions Under Fire: Building a Zero-Oversell Flash Sale Platform with Amazon Aurora DSQL
yama3133
1
110
AI駆動開発をチームに根付かせる - 「1行も書かない」チームがHarnessを育てた1年 -
kenichirokimura
6
3.3k
Data Hubグループ 紹介資料
sansan33
PRO
0
3.2k
LLMアプリ、 雰囲気で運用してませんか? 〜LLMOpsの現在地〜
taka_aki
1
560
Microsoft MVP プログラムを紹介するから目指す人増えてくれ
tsubakimoto_s
0
140
全社に広がるMCPサーバーを、 どう安全に管理するか MCPass開発の舞台裏
mtpooh
3
180
Featured
See All Featured
Paper Plane (Part 1)
katiecoart
PRO
1
10k
Principles of Awesome APIs and How to Build Them.
keavy
128
18k
Designing for Timeless Needs
cassininazir
1
460
Rebuilding a faster, lazier Slack
samanthasiow
85
9.6k
How to Grow Your eCommerce with AI & Automation
katarinadahlin
PRO
1
250
Believing is Seeing
oripsolob
1
200
AI in Enterprises - Java and Open Source to the Rescue
ivargrimstad
0
1.4k
It's Worth the Effort
3n
188
29k
How STYLIGHT went responsive
nonsquared
100
6.2k
Collaborative Software Design: How to facilitate domain modelling decisions
baasie
1
290
JAMstack: Web Apps at Ludicrous Speed - All Things Open 2022
reverentgeek
1
580
How to Get Subject Matter Experts Bought In and Actively Contributing to SEO & PR Initiatives.
livdayseo
0
180
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