フレームワークの外で依存を扱うと、設計は一段深くなる

John Smith

Hatched by John Smith

Apr 23, 2026

1 min read

72%

0

依存は便利さではなく、設計の責任である

「便利だから使う」と「境界を守るために使う」は、同じ仕組みでも意味がまるで違います。依存性注入や delegate は、単なる省力化テクニックではありません。むしろ、オブジェクト同士がどこまで知り合ってよいかを決める、設計上の宣言です。

ここで面白いのは、FastAPI のような現代的なフレームワークが依存性注入を前提にしている一方で、Rails 以外の Ruby では delegate をあえて自作したくなる、という逆方向の欲求が生まれていることです。片方は「依存を明示しながら組み立てたい」。もう片方は「フレームワークの外でも、責務の伝達を自然に書きたい」。

この2つは別々の話に見えて、実は同じ問いに収束します。ソフトウェアの複雑さを、どこに閉じ込めるべきか。 その答えは、機能の多さではなく、依存の見え方にあります。


依存性注入と delegate は、どちらも「知ってよいこと」を制限する

設計の失敗は、しばしばロジックのミスではなく、結合のしすぎから起こります。クラスや関数が相手の内部を知りすぎると、変更は伝染し、テストは重くなり、再利用は難しくなります。そこで依存性注入が登場します。必要なものを外から渡すことで、実装は具体物ではなく 役割 に向き合えるようになります。

たとえば、注文処理がメール送信機能を直接生成してしまうと、注文の変更と通知の変更が絡み合います。ところが、メール送信サービスを外から渡せば、注文処理は「通知できる何か」があることだけを知っていれば済みます。これはコードの見た目を整えるためではなく、変更の波及範囲を制御するための技術です。

delegate も同じです。あるオブジェクトが持つ責務を、そのまま他のオブジェクトへ渡すとき、delegate は「この振る舞いは私のものだが、実装は君に任せる」と言います。これにより、呼び出し側は複雑な内部を覗かずに済み、インターフェースだけで会話できます。

依存性注入は「何を使うか」を外に出す技術であり、delegate は「誰が知っているか」を外に出す技術である。

この2つの違いは微妙ですが重要です。依存性注入は、主に組み立ての責任を整理します。delegate は、主に問い合わせの責任を整理します。前者は「誰が作るか」、後者は「誰が答えるか」を整える。どちらも、オブジェクトを自立させるために依存を外部化しています。


便利なフレームワークほど、依存の輪郭を見失いやすい

FastAPI の Dependency Injection が魅力的なのは、書くコードが短くなるからだけではありません。依存を宣言的に書けるため、アプリケーションの構造が見えやすくなるからです。エンドポイントの引数を見るだけで、その処理が何に頼っているか分かる。これは保守性の話であると同時に、認知負荷の削減でもあります。

しかし、便利な仕組みには副作用があります。DI がフレームワークに組み込まれていると、開発者は依存を管理しているつもりで、実はフレームワークの流儀に依存しているだけ、という状態になりやすいのです。見かけ上は疎結合でも、裏では特定の実行モデルやコンテナ構成に寄りかかっていることがあります。

このとき本当に必要なのは、DI を「使う」ことではなく、依存の境界を自分で説明できることです。ここが肝です。依存が宣言されていれば良いのではありません。依存が読者にとって意味のある単位で現れている必要があります。

たとえば、次の2つは同じように見えて、設計の質が違います。

  1. UserServiceDatabase, Clock, Mailer を受け取る。
  2. UserServiceRepository, NotificationPort, TimeSource を受け取る。

後者のほうが抽象度が高く、テストしやすいだけでなく、実装の変更に強い可能性があります。重要なのは「注入していること」ではなく、注入する単位が責務の境界と一致していることです。

delegate についても同じ罠があります。便利だからと何でも delegate してしまうと、どこで何が起きているのか見えなくなります。特に Rails 以外で delegate を使いたい場面では、ActiveSupport の大きな仕組みに頼らず、必要最小限のモジュールで実現したくなる。これは単なるミニマリズムではなく、暗黙性の削減です。

つまり、フレームワークは依存を隠すのではなく、依存を読める形に整えるために使うべきです。隠れた依存は、短期的には楽でも、長期的には変更のたびに利子を生みます。


自作の delegate が教えてくれる、抽象化の正しい作法

Rails の外で delegate を自作したくなる気持ちは、実はとても健全です。それは「もっと小さく、もっと明確に、必要な分だけ欲しい」という要求だからです。大きなライブラリを入れれば一発で解決する問題でも、実際にはプロジェクトの価値を守るために、依存を増やしすぎないほうが良いことがあります。

ここで見えてくるのは、抽象化には2種類あるということです。ひとつは機能の抽象化。もうひとつは関係の抽象化です。delegate は後者に属します。つまり、複数のオブジェクトの関係を、ひとつの意図のように見せるための仕組みです。

たとえば、UserProfileaddress.city を毎回掘り下げて参照しているとします。このとき delegate で city を直接呼べるようにすると、呼び出し側の意図は明確になります。呼び出し側は「住所の構造」を知る必要がなくなり、「都市が欲しい」という欲求だけを表現できます。

これはコードの短縮以上の意味を持ちます。読み手の思考を、構造から意図へ移すからです。良い抽象化は、情報を隠すのではなく、読むべき階層を下げるのです。

ただし、delegate も DI も、やりすぎると逆効果です。抽象化は「見えなくする」ことと表裏一体なので、抽象が多すぎると実体への到達が難しくなります。そこで必要になるのが、抽象化の粒度を判断する基準です。

良い抽象化は、変更しやすさを増やしながら、理解しやすさを失わない。悪い抽象化は、そのどちらかを犠牲にする。

つまり、delegate も DI も、単に「間に挟む」技術ではありません。間に挟むことで、どの情報を上位に残し、どの情報を下位へ沈めるかを決める技術です。


本当に設計しているのはコードではなく、会話の仕方である

ここまで見てきた2つの技法をつなぐと、ひとつの大きな見方が浮かび上がります。私たちが設計しているのは、クラスや関数の配置ではなく、コンポーネント同士の会話プロトコルです。

DI は、会話の前提条件を外に出します。「私はこれらの役割を受け取って働く」。delegate は、会話の委譲先を明確にします。「この問い合わせは、別の責務に任せる」。どちらも、相手の内部を知らなくても協力できるようにするための仕組みです。

この視点に立つと、設計判断の基準も変わります。よくある問いは「どのパターンを使うべきか」ですが、本当に重要なのは「この会話は、どのくらい長持ちさせたいか」です。会話が短命なら直書きでも十分です。会話が複数箇所に広がり、変更が頻繁なら、依存の境界を明示したほうがいい。

具体例を挙げましょう。小さなスクリプトなら、あるオブジェクトが別のオブジェクトのメソッドをそのまま呼び出すだけで十分です。けれど、複数の画面、ジョブ、API が同じ概念を扱い始めると、同じ呼び出しが散らばり、責務の定義が曖昧になります。この段階で delegate や DI を導入すると、各層の会話が整理されます。

重要なのは、これらを「重いアーキテクチャ」として捉えないことです。むしろ、会話の質を守るための軽量な秩序として見るべきです。秩序がないと、コードは書けても、変更時に誰が何を知っているべきかが崩れていきます。


Key Takeaways

  • 依存性注入は便利機能ではなく、境界設計の手段として使う。何を注入するかは、そのまま責務の線引きになる。
  • delegate は単なる省略記法ではなく、関係の抽象化である。呼び出し側が知るべき情報を減らし、意図を前面に出す。
  • フレームワークに頼る前に、依存の単位を説明できるか確認する。説明できない依存は、将来の変更コストになりやすい。
  • 抽象化は、見えなくすることではなく、見る階層を変えること。実装ではなく意図が読める形に整える。
  • 小さなプロジェクトほど、最小限の自作抽象が効くことがある。大きなライブラリを入れる前に、必要な会話だけを表現できるか考える。

まとめ: 良い設計とは、依存を減らすことではなく、依存を読めるようにすること

依存性注入も delegate も、最終的には同じ目的に向かっています。コードの相互理解を簡単にし、変更の影響範囲を狭くし、責務の境界をはっきりさせることです。違いは、どの場面でその境界を表現するかにあります。

だから、設計の成熟とは「フレームワークを多く知っていること」ではありません。依存を隠すことなく、依存を自然に見せる技術を選べることです。フレームワークの内側でも外側でも、その原則は変わりません。

そして最も大事なのは、依存を減らすことが目的ではないという点です。目的は、依存のあり方を明晰にすることです。明晰さがあれば、コードは短くても長く保ちます。曖昧さがあれば、コードはどれだけ整って見えても、変更のたびに崩れていきます。

結局のところ、良い設計とは「何を知っていてよいか」を決める営みです。DI も delegate も、そのための言語にすぎません。けれど、適切に使えばその言語は、コードを単なる手続きから、意図が流通する構造へと変えてくれます。

Sources

← Back to Library

Hatch New Ideas with Glasp AI 🐣

Glasp AI allows you to hatch new ideas based on your curated content. Let's curate and create with Glasp AI :)

Start Hatching 🐣