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
今改めてServiceクラスについて考える 〜あるRails開発者の10年〜
Search
Tomohiro Hashidate
September 26, 2025
Technology
0
650
今改めてServiceクラスについて考える 〜あるRails開発者の10年〜
Kaigi on Rails 2025 登壇資料
Tomohiro Hashidate
September 26, 2025
Tweet
Share
More Decks by Tomohiro Hashidate
See All by Tomohiro Hashidate
rubygem開発で鍛える設計力
joker1007
3
1k
実践Kafka Streams 〜イベント駆動型アーキテクチャを添えて〜
joker1007
3
1.1k
本番のトラフィック量でHudiを検証して見えてきた課題
joker1007
2
1.1k
5分で分かった気になるDebezium
joker1007
1
130
Rustで作るtree-sitterパーサーのRubyバインディング
joker1007
5
1.4k
tree-sitter-rbsで作って学ぶRBSとパーサージェネレーター
joker1007
3
300
Kafka Streamsで作る10万rpsを支えるイベント駆動マイクロサービス
joker1007
7
4.8k
neovimで作る最新Ruby開発環境2023
joker1007
3
4.5k
ReproのImport/Exportを支えるサーバーレスアーキテクチャ
joker1007
1
1.3k
Other Decks in Technology
See All in Technology
AIで 浮いた時間で 何をする? #プロヒス2025
konifar
26
12k
施策が均質化する採用市場で何を捨て 何を大事にしていくかを考える
akyun
0
180
経営の意思決定を加速する 「事業KPIダッシュボード」構築の全貌
recruitengineers
PRO
3
160
2025 IEEE MSST: NFS: Genesis
pugs
0
110
テストコードすら書けなかったレガシーアプリがAIと上手に協働できるようになるまでの軌跡
dip_tech
PRO
0
230
【GPT-5本出版記念】npaka による AIの今とこれから と AI時代の生存戦略
npaka
2
370
Claude Code でアプリ開発をオートパイロットにするためのTips集 Zennの場合 / Claude Code Tips in Zenn
wadayusuke
8
4.5k
Bedrock で検索エージェントを再現しようとした話
ny7760
3
290
使いやすいプラットフォームの作り方 ー LINEヤフーのKubernetes基盤に学ぶ理論と実践
lycorptech_jp
PRO
2
270
品質保証に注目したAIプロダクト開発
sansantech
PRO
1
140
データ分析エージェント Socrates の育て方
na0
8
4k
2025/09/16 仕様駆動開発とAI-DLCが導くAI駆動開発の新フェーズ
masahiro_okamura
0
320
Featured
See All Featured
GraphQLの誤解/rethinking-graphql
sonatard
72
11k
Git: the NoSQL Database
bkeepers
PRO
431
66k
How To Stay Up To Date on Web Technology
chriscoyier
791
250k
Context Engineering - Making Every Token Count
addyosmani
3
100
Become a Pro
speakerdeck
PRO
29
5.5k
Distributed Sagas: A Protocol for Coordinating Microservices
caitiem20
333
22k
Speed Design
sergeychernyshev
32
1.1k
The Pragmatic Product Professional
lauravandoore
36
6.9k
How to Ace a Technical Interview
jacobian
280
23k
Sharpening the Axe: The Primacy of Toolmaking
bcantrill
45
2.5k
4 Signs Your Business is Dying
shpigford
185
22k
Why Our Code Smells
bkeepers
PRO
339
57k
Transcript
今改めてServiceクラスについて考える 〜あるRails開発者の10年〜 joker1007 (Repro株式会社) Kaigi on Rails 2025 1
自己紹介 Tomohiro Hashidate id: joker1007 Repro株式会社 チーフアーキテクト 元々はRailsエンジニアだったが、最近はJavaばかり書いている スティール・ボール・ランのアニメ化が楽しみで生きている パーフェクトRuby及びパーフェクトRuby
on Railsの著者の一人 2
Reproから来ました 日々大量のデータを扱い、デジタルマーケティングを支援してます。 3
4
パーフェクトRuby on Railsの第1版 2014/6/6 発売 11年前、ざっくり10年以上経った。 (思えば遠くに来たもんだ) 5
9章の6 サービスクラス ここを書いたのが俺。 6
そしてもう一つ 「俺が悪かった。素直に間違いを認めるから、もうサ ービスクラスとか作るのは止めてくれ」 at Qiita https://qiita.com/joker1007/items/25de535cd8bb2857a685 これを書いたのが2016年末。パRailsを書いてから2年半後ぐらい。 2年半で言ってること変わってんじゃん!と言いたくなるかもしれない。自分でもそう思う。 7
最初のパRailsから大体10年後のこと…… 8
2024/09/07 福岡Rubyist会議04 懇親会にて @junk0612 「サービスクラスってどう思います?」 @joker1007, @onk 「オッ!サービスクラスの話?」 みたいな感じになりまして、 ちょうど近くに座っていた
@snoozer05 さんとビアバーに流れて議論する機会があった。 これを機にちょっと話をしたいなーと思っていたら、この前の関西Ruby会議08でもサービス クラスへの言及があって、話すなら今だなと思った。 9
この時は、実際の仕事の現場では出来る限り使わない 方が良い、という意見で概ね合意があったと思う (それなりに飲んでたので記憶違いだったらごめんなさい。 訂正歓迎。 ) 10
何故使わない方がいいのか 「開発統制の困難さを上回るメリットが得られない」 もう少し具体的な話をする前に過去を振り返っておきたい。 ちなみに、ここから文字がめっちゃ多いです。 1日の最後に眠い話になるかもしれませんが、お付き合いください。 11
そもそもサービスクラスとは何だったのか 当時俺はどの様に書いていたのか 過去の文献や資料、そして自分が書いた文章を読み返 してみた 12
2011年4月 エリック・エヴァンスのドメイン駆動設計 発売 (原著は2003年発売) 日本でサービスクラスという名前が語られることが増えた背景に、DDD本の影響があったこと は無視できない。 この発表にも内容への言及がいくつか出てくるが、DDDという手法を採用する・しないに関わ らず、自分達が解決したい業務ドメインに対する理解を深めることは、どんなソフトウェアを 開発する上でも重要な営みだと考えているため。 Railsのバージョンとしては3.1〜3.2ぐらいの時期で、ActiveRecordが今も残る形に変更さ
れ、Sprocketsが入り、多くのスタートアップで使われる様になってきた時代。 ちょうど自分もRailsで仕事が出来る様になった頃で、個人的には一番熱かったかも。 13
Fat Modelに対する問題意識 Railsがビジネスの現場で使われることが多くなった結果、あちこちでFat Modelが問題として 顕在化し始めた。 ActiveRecordの特性上、システム自体が複雑になると色々な箇所から参照される基盤となる クラスに多くの処理が記述され、複雑度が爆発し依存関係がとっ散らかって管理しきれなくな る、という問題がしばしば発生する。 それに対処するために、モデルを整理するテクニックがあちこちで語られる様になりました。 Rails
fat modelでググると2010年頃からそういうテクニックについて語られている記事が増 えているのが分かります。 この頃からサービスクラスについての言及も多く出てきます。 14
DDD本におけるサービスとは (要約) ソフトウェアのビジネスドメインから生まれる概念の中で、エンティティや値オブジェクトに 責務を持たせるとそれらの定義を歪めたり不自然なオブジェクトが生まれる場合がある。そう いった時に振舞いに着目して何が実行できるかという観点から命名し責務を定義するもの。 そして、その命名と操作名はユビキタス言語に由来していなければならない。 ユビキタス言語についての簡単な説明: ビジネスドメインについて議論・設計を行う際に実際 の業務で表現される語彙とコード上の語彙を統一して、ドメイン理解とコード上の表現の相互 のやり取りの基盤となるもの。我々が何を作ろうとしているのかを突き詰めていく際のコミュ
ニケーションの基盤となるもの。 15
パーフェクトRailsにおけるサービスクラス (要約) コントローラーとモデルの中間に立つもので、コントローラーはHTTPリクエストをハンドリ ングするインターフェースであり、サービスはモデルが行う処理を取り纏めるインターフェー スである。 サービスクラスは処理そのものをカプセル化したものであり、かつ業務知識と実装のメンタル モデルを一致させることが重要な役割の一つである。 業務知識と照らし合わせて自然な形でアプリケーションを設計するための手段の一つ。 適切な名前付けが出来ないならサービスクラスを利用する対象として不適切な可能性がある。 16
意外とちゃんと書いてある (自分相手だと、いくらでも上から目線になれますね ) 乱用は危ないから考えて使おうね、とも書いてある。 実際書いた時もDDD本は大いに参考にしたし、先達の記事も一杯読んだ。 余談だが、パーフェクトRailsはこういう現場でコードを書いている目線からの知見を大事に したいと思って書いていた。 しかし……。 17
この時に分かってなかったこと 「乱用は危ないから考えてから使う」の度合いが人によってバラバラだというこ と ビジネスドメインの語彙に対する理解度が人によってバラバラだということ 単にややこしい処理に安易に名前付けて良い訳じゃないってこと ある機能の振舞いとActiveRecordのデータ操作を適切に分離することの難しさ 自社開発の会社で役職を得て人の開発を監督する責務を負う様になり、ちょっとづつ分かって きた。 18
チーム開発における共通認識の重要さ Railsを活用した事業が増え、2、3人のすぐに話が伝わるレベルでは収まらない開発が普通に なった。自分もゼロからのスタート時点で関わっていない仕事が増えた。こういう世界で継続 的に開発のペースを維持するためには、チーム全体の水準と共通認識が重要になる。 一貫性を伴ってコードを書いてないと、少し時間が経っただけで何がどうなってるかすぐ分か らなくなる。 人はすぐに物事を忘れるし、自分が書いたコードがいつの間にか謎の拡張をされていたりする のは日常茶飯事なので、大事なのは「ちゃんと想像が付く」こと。 19
想像が付くとはどういうことか 機能のエントリポイントから戻り値を返すところまでの全体像を把握しやすいこ と どのディレクトリに何が入っているか分かること クラス名・メソッド名で処理内容がイメージできて、副作用の有無が分かること ソースコードそのものに対しては「リーダブルコード」という良い本がある。 これを構造レベルで実践すると考えて欲しい。 20
サービスクラスの基準の作りにくさ 一つのモデルに収まらない複雑な処理を表す振舞いに考えて名前を付けろ、では現実的に基準 にならない。 想像が付く様にするためには、もっと明確で覚え易いルールが必要になる。 単純なルールを決めたとして、本当に常にそうあるべきか?仮にそうだとして、裏側に ActiveRecordのモデルが居る訳で、モデルに書く処理とサービスクラスに書く処理のメソッ ドレベルでの境界はどこにあるのか?その書き方の理由を説明が出来るか? 朝のキーノートでメール送信コードどこに書く?というテーマでmoroさんが考え方を示して くれましたが、そもそもコードの配置を適切に行う時点でかなり考える力が要る。 21
Railsのレイヤー分類はよく出来ている Railsの基本的なレイヤーはController, Model, Viewだけだ。Jobは非同期処理向けの Controllerと言える。 Controller: ユーザーからのリクエストをハンドリングし、モデルの処理結果を表 示するviewを選択する Model: RDB操作とビジネスロジック全部
View: 処理結果の表示 これをきっちり守るだけで大体どこに何があるか想像が付く。 人間が簡単に覚えて共通認識を作れるのはこの程度だという割り切りが見える。 こういう共通認識の力こそがフレームワークを活用する利点。 逆に言えば共通認識を破壊するとフレームワークの利点が失われる。 22
サービスクラスを使っても問題ないケース 一人、または少人数で開発していてすぐにコミュニケーションが取れる チームの大半が対象業務を十分に理解しており、読み易いコードを維持すること についての水準が高い こういう場合なら使っても問題ないし、見通しが良くなるケースもあると思う。個人開発なら 基本的に問題ない。 最低でも一人はここまで話した様なことが自分で説明できて、コードレビューに時間が割ける ことが必要。 今日のトークの入門FormObject冒頭でFormObject説明できる人~って聞いてて、あまり手が上がらなかったですね。 この説
明の難しさがチームで書くコードのクオリティを保つ難しさでもある。 23
じゃあ、Fat Modelはそのままで良いのか? もちろんそんな訳はない 24
Formクラスってどうなん? 25
現実的な折衷案としてのFormクラス ServiceクラスとFormクラスはActiveRecordの単体クラスの責務とするのが難しい問題の置 き所としては同じものだが、扱う文脈の限定度合いが違う。 Formクラスの方がより限定的な問題に対処するもので、それが名前にも表れている。 だからこそ、現実的な折衷案として機能する。 26
Formクラスの責務 (これも諸説あると思うが……) Webアプリケーションにおける重要な処理の大半は、HTMLフォームもしくはそれに準ずるイ ンターフェースによってまとめられたパラメーターのPOST/PUTによって発生する。 つまり、パラメーターハンドリングとトランザクションコントロールを扱うレイヤーがあれ ば、大半の複雑な問題を扱える、そういう割り切りの元に存在している。 昨今のWebアプリケーションは入力パラメーター自体がそもそも複雑なので、ここがなんとか なれば良い。 27
サービスクラスとの違い Formクラスは画面やユースケース(つまり大体アクション)と紐付いている ActiveModel, ActiveRecordのインターフェースと親和性が高い Formという名前から想像できる具体的なイメージがある 元々はビューに表示するformヘルパーとの相性も考慮されていたが、昨今はJSで 組み立てることが多いので、この点は重要ではなくなってきた。 単なる名前と印象の問題では?と思うかもしれないが、業務としてのソフトウェア開発におい てそれは物凄く重要である。チームメンバーが納得できる名前を付けられるかどうかがその後 の開発のしやすさに大きな影響を与える。
入門FormObjectで話をされていたのは機能的な分類だったが、自分は名前とそこから発生す る共通認識に注目している。 28
Formクラス、サービスクラス共通の難しさ 複数のattributeに関する操作やRDBに関する操作をまとめて、ActiveRecordのクラスのメソ ッドに定義したとする。 これを複数のForm、Serviceから再利用しようとすると、ActiveRecordのメソッド側から修 正したくなった時に困るし、Form側からの要請でメソッドを弄って大丈夫か怪しい、みたい なことになる。 結局ActiveRecordを上手く整理することから逃げられない。 ある意味ではRailsの大きなしがらみでもある。 JC版 ダイの大冒険22巻より
29
結局のところ、ServiceだのFormだのは余り重要では ない Railsの世界で言えば全部モデルの世界をどう整理するかという話で、わざわざ別のディレク トリ区切る程なのか? ちゃんとシンプルさを求めることから逃げてないか? 30
まずRDBと向き合う 参考: Identifying User Identity by moro dynamic! by moro
パーフェクトRails著者が解説するdeviseの現代的なユーザー認証のモデル構成に ついて by 俺 31
RDBの基本 自然に表現できて安全なデータ構造を見出すこと エンティティとイベントの区別 中間表現に適切な名前を付ける 正規化・SQLの表現テクニックを学ぶ 状態ではなく事実の有無を活用し、データベースの制約を活かす設計 テーブルやカラムにメンバーが自然に呼称できる適切な名前を付ける 32
まずActiveRecord、ActiveModel、SQLを使いこな す 外から呼ばれるメソッドか、Validationか、Callbackかを適切に選択する Validation, Callbackに分岐を発生させない テーブル設計と組み合わせて状態管理をシンプルに ユニーク制約に影響を与える削除フラグを止めるとか STI, polymorphicは難しい、気を付けろ ちゃんとscope作って名前を付ける
joins, includes, preloadの違いを理解し、EXPLAINはすぐ確認できる様に 33
我々が本当に整理したかったこと 34
重要なのはドメインコンテキストの理解 複雑なシステムを設計する時に重要なのはコンテキストマップを作ること。DDDにおける基本 にして奥義だと思う。 DDD本においては境界づけられたコンテキストと呼ばれる。 そして、その一連のコンテキストがシステム内部のどのコンポーネントに相当し、どうやって 他のコンテキストと相互作用するのかを考える。 コンテキストをコードを中で自然に表現できて、コンテキスト同士は境界を越えた先に影響を 与えない様にコントロールしたい。 35
境界づけられたコンテキストとは 同じユビキタス言語(語彙)が使える業務領域のこと。 例えば、Reproではスマホに対してプッシュメッセージを送ってマーケティングコンテンツを 配信することと、アプリケーション内のポップアップでマーケティングコンテンツを配信する ことは、同じ「コンテンツ配信」だが実態としては、前者は外部の配信APIへのリクエスト送 信、後者は自社のAPIに対するSDKからのリクエスト受信、とやりたいことが真逆だし、期待 する配信タイミングも目的も異なる。 同じ言葉を使っても、意味や期待する動きが異なるなら同じコンテキストとして扱うのが適切 ではない可能性がある。 36
依存関係を一方向に保つ 機能のワークフローを描く時、集約のルートとなる場所が明確になる様にデザインし、そこか ら依存関係の方向が逆戻りしない様に設計する。 Rubyの世界では古くはmoduleを利用した名前空間とディレクトリ構成だったが、現代にお ける現実的な選択肢はやはりモジュラーモノリスになるだろう。 37
モジュラーモノリスのやりたいこと 分散システムの複雑性から可能な限り距離を取りつつ、コンテキストの境界を明 確にし強制する 認知負荷を下げる 開発の統制を保つ 但しRubyには同一のコードベース内で可視性を強制する手段が無かった。 なのでShopifyはPackwerkを作った。Zeitwerkを利用して定数参照にフックをかける形でコ ード間の依存性を検証する。 38
現実的だが楽な道ではない 参考: A Packwerk Retrospective (2024) - Shopify Packwerkの依存性検出には限界があるし、コンポーネントの責務をどう分離するかの示唆を 与えてくれる訳ではない。
特にドメインで分離した結果、機能面での依存が不自然になるのは非常に難しい問題で、簡単 に結論が出せる様な問題ではない。 コンテキストマッピングもコンポーネントの分割も、一度で上手くいく様なものではないの で、継続的に何度も判断を下し続けてコードとメンタルモデルを相互に改良し続ける必要があ る。(こういう行いをドメインモデリングの文脈では蒸留と呼ぶ)。 39
Serviceクラスの復活 モジュラーモノリスによってコンポーネントの境界が明確になった時、Webというインターフ ェースに限定されないコンポーネントレベルでの公開エントリポイントが必要になる。 ここでServiceクラス(またはそれに類する何か)を利用するのは意味があると考えている。こ の時、ドキュメント化された呼び出し規約は絶対に必要になる。 マイクロサービスにおいてはリポジトリで管理されるスキーマなどがそれに当たる。モジュラ ーモノリスではメソッドの型になるだろう。 型検査をやるならこういったコンポーネントの境界を跨ぐ公開エントリポイントが最も重要度 が高い。 40
新しい世界 Ruby::Box (by @tagomoris) 今開発が進んでいるNamespace改めRuby::Boxによりもたらされる可視性のコントロールは Packwerkより遥かに強力。 Ruby::Boxではある名前空間のクラス定義構造は他のものと完全に独立している。定義自体が 無いものはどうやっても呼べない。 流石に早々に使える様にはならないと思うが、より自由なディレクトリ構成を持ちつつコンテ キスト境界を明示する一つの手段になるのではないかと期待している。
41
実は言いたかったこと チームで継続的に仕事として開発することの難しさ、そしてそれへの向き合い方。 Serviceクラス、Formクラス自体は合理的な場合もあるが上手く活用することは 難しい。特にチーム開発では。 もし使うならチームメンバー全員でどういうケースで使うのか、どうして必要な のかをちゃんと言語化し話し合う。 重要なのは、開発者にとって想像が付くこと、驚きが少ない開発。読めるコー ド、そして読める構造へ。 ServiceクラスやFormクラスはコード上の表現技法の一つに過ぎない。コンテキ ストマッピングをしっかり考えて、継続的に改善をし続けること。
42
正解は無い、一緒に考え続けましょう ソフトウェア設計は継続的な営み。 ITシステムの設計ってのは、ある瞬間に適切な選択をすることではなくて、継続的に開 発が滞りなく行えて新しい関係者が物事を理解しやすい状態を維持し、ビジネスが意図 した通りに発展できる状態を維持し続けることだと伝えていきたい。 — joker1007 (アルフォートおじさん) (@joker1007) June
18, 2025 43
We're hiring!! 一緒にソフトウェア設計を考えてくれる仲間を探しています。 よろしくお願いします。 44