PROMARI JOURNAL

DIP――ブラウザの機能を、ポートの向こうへ置く

作成: / 公開: / 内容更新:

執筆:tamito0201 / 掲載・運営:プロマリ

丸い共有ボタンの裏側を掘る連載の第8回。SOLID の5つ目の原則 DIP を、「コピー」のボタン1つから確かめます。コピーを頼むコードはブラウザを知らず、約束(ポート)だけを知る。実行時の呼び出しは外へ、import は内へ。import の向きを数え、DI・DI コンテナ・ファクトリの役目を読み、わざと境界を破って何が止めるかまで、図16点で読み解…

PASS IT ON

ひとつの発見を、次の会話へ。

何にでもポートを挟まない

ここまで、コピーの道具をポートの向こうへ置くと、テストで代役を差し込め、ブラウザの事情が1つのファイルに閉じ込められることを見てきました。では、コードの中の具体的な道具には、すべて約束を挟むべきなのでしょうか。答えは「いいえ」です。このページでは、DIP の限界と、使いどころを考えます。

変わりにくい具象には、頼ってよい

実は、方針のコードも、具体的な道具にたくさん頼っています。ClipboardGateway の write が返す Promise、URL を表す string、ボタンの並びを表す Array。どれも JavaScript の言語に最初から用意された、具体的な部品です。けれど、これらにまで「文字列を扱う約束」を挟む人はいません。内側の型検査も、ブラウザの型は外していますが、言語の標準(ES2022)の型はそのまま使えるようにしています。

挟まない理由は、変わりにくいからです。Martin は、DIP のコラムと同じ連載の「Stability(安定)」の回で、安定依存の原則を示しました。原文は「THE DEPENDENCIES BETWEEN PACKAGES IN A DESIGN SHOULD BE IN THE DIRECTION OF THE STABILITY OF THE PACKAGES.(設計の中のパッケージどうしの依存は、安定している方向へ向けるべきだ)」です。変わりやすいものに、変わりにくいものを頼らせてはいけない。裏返せば、自分より変わりにくいものに頼るのは、安心してよいということです。

図6.1-1 変わりにくい具象には、頼ってよい
図6.1-1 変わりにくい具象には、頼ってよい。考え方(Martin の安定依存の原則から)。言語の標準のように変わりにくい具象は、そのまま使ってかまいません。ポートを挟むのは、変わりやすい道具だけです。図6.1-1 変わりにくい具象には、頼ってよい。考え方(Martin の安定依存の原則から)。言語の標準のように変わりにくい具象は、そのまま使ってかまいません。ポートを挟むのは、変わりやすい道具だけです。

この物差しで、このプラグインの道具を並べてみます。Promise や string のような言語の標準は、いちばん変わりにくいので、どの層からも直接使います。DI コンテナの InversifyJS は外部のライブラリで、版が上がれば書き方が変わるかもしれないので、組み立て役の中だけに閉じ込めています。そして navigator.clipboard は、ブラウザの種類・権限・テストの環境で振る舞いが変わる、いちばん変わりやすい道具なので、ポートの向こうへ置いています。ポートを挟むのは、変わりやすい道具だけです。

挟みすぎると、何が起きるか

反対に、何にでも約束を挟むと、別の困りごとが生まれます。1つ目は、読む場所が増えることです。「コピーの道具は何か」を知るのに、約束のファイル、実装のファイル、組み立て役のファイルの3つを開くことになります。1つの約束に1つの実装しかなく、差し替える予定も無いなら、その行き来は手間だけが残ります。

2つ目は、約束が道具の写しになることです。2ページ目で見たように、道具の手順をそのまま並べた約束は、名前がインターフェースになっただけで、方針のコードは相変わらず道具の都合を知っています。約束を挟む手間を払ったのに、差し替えも試しやすさも手に入りません。3つ目は、DI を使っただけで安心してしまうことです。コンストラクタで受け取る型が具体的な道具のままなら、依存の向きは何も変わっていません。引数で渡すことと、よい境界になっていることは別です。

このプラグインでも、表示を受け持つ presentation は、スクロールに合わせて共有欄を出し入れするために、ブラウザの window や document を直接使っています。これは表示の層そのものの仕事で、方針の判断を含まないので、約束を挟まずに済ませています。依存の境界のテストも、presentation に対しては navigator や fetch のような外との通信の道具だけを禁じ、表示のための window や document は許しています。どこまでを約束の向こうへ置くかは、層ごとに選んでいるのです。

ポートを挟むかの目安

では、ある道具を直接使うか、ポートの向こうへ置くかは、どう決めればよいでしょうか。この記事では、次の3つの問いを順にたどることを提案します。

図6.3-1 ポートを挟むかの目安
図6.3-1 ポートを挟むかの目安。判断の目安(提案)。変わりやすく、試すのに邪魔で、細部を知る必要のない道具だけを、ポートの向こうへ置きます。図6.3-1 ポートを挟むかの目安。判断の目安(提案)。変わりやすく、試すのに邪魔で、細部を知る必要のない道具だけを、ポートの向こうへ置きます。

1つ目は、動く場所によって、その道具の中身が変わるかです。クリップボードは、ブラウザでは本物、テストでは存在せず、サーバーで動かすならまた別になります。2つ目は、その道具が無いと、方針のコードを試せないかです。クリップボードが無い Node では、直接呼ぶ形のユースケースは成功の場合を試せませんでした。3つ目は、方針の側が、その道具の細かい手順まで知る必要があるかです。ユースケースが知りたいのは「書けたか、書けなかったか」だけで、navigator.clipboard?.writeText の確かめ方までは要りません。

3つとも「はい」なら、ポートを挟む候補です。「いいえ」ばかりなら、直接使ってかまいません。コピーの道具は3つとも「はい」で、言語の標準は3つとも「いいえ」でした。約束を挟むのは、変わりやすく、試すのに邪魔な道具だけにしました。何にでも挟むのではなく、挟む場所を選ぶこと。これが、DIP を実際の設計に使うということです。

DIP によくある3つの誤解

DIP を読むときに陥りやすい誤解を、3つ並べておきます。

DIP によくある誤解と、この記事での答え
よくある誤解この記事での答え
DI(依存性の注入)を使えば、DIP になるDI は渡し方の技法。受け取る型が使う側の決めた約束になっていて、初めて import の向きが逆になる
具体的な道具には、すべてインターフェースを挟む言語の標準のような変わりにくい具象には直接頼ってよい。挟むのは、変わりやすく試すのに邪魔な道具だけ
「逆転」は、処理の流れが逆になること処理(呼び出し)は今までどおり外へ進む。逆になるのは import の向き

3つの誤解に共通しているのは、DIP を「インターフェースを作ること」や「引数で渡すこと」という書き方として読んでいることです。DIP が求めているのは、書き方ではなく、コードがどちらを知っているかという向きです。向きは、この記事でしたように、import を数えれば確かめられます。

手を動かして確かめる

今回の練習は、手元のコードで、方針のコードが具体的な道具を直接知っている場所を数えることです。たとえば、次のようなコマンドで、new で道具を作っている行や、fetch・localStorage のようなブラウザの機能を直接呼んでいる行を探せます。

Shell方針のコードが道具を直接知っている行を探す(探す語と場所は、手元に合わせて変える)
$ grep -rnE "new |fetch\(|localStorage" src/
図8-1 この回の宿題:方針から道具への矢印を数える
図8-1 この回の宿題:方針から道具への矢印を数える。確かめ方の提案。変わりやすい道具を直接知っている行が、ポートを挟む候補です。まずは1つだけ、約束を書いてみます。図8-1 この回の宿題:方針から道具への矢印を数える。確かめ方の提案。変わりやすい道具を直接知っている行が、ポートを挟む候補です。まずは1つだけ、約束を書いてみます。

見つかった行を、「変わりにくい道具(言語の標準など)」と「変わりやすい道具(ブラウザの機能・外の通信・保存先など)」の2つに分けます。変わりやすいほうの中から1つ選び、方針の側が「何をしてほしいか」だけを書いた約束(インターフェース)を1つ書いてみてください。約束に書くメソッドが、道具の手順の写しになっていないかも、あわせて見てみましょう。

このプラグインで同じように数えたときは、方針のコード(domain と application)から infrastructure への import は0件で、ブラウザの実装を名指ししているのは組み立て役の ShareContainer.ts だけでした。宿題:方針のコードから道具への矢印を数え、1つに約束を書いてみる。数えた結果は、このラベルからコメントに残せます。

DIP の原典は、Robert C. Martin のThe Dependency Inversion Principle(C++ Report、1996年6月)です。安定依存の原則は、同じ連載のStabilityの回で読めます(どちらも Web Archive に残る写し)。ポートという呼び名は、Alistair Cockburn のHexagonal Architecture(Ports and Adapters)から、DI という呼び名は、Martin Fowler のInversion of Control Containers and the Dependency Injection pattern(2004)から借りています。組み立て役(Composition Root)は、Mark Seemann のComposition Rootが詳しいです。

まとめと、次回

第8回のまとめです。DIP は、方針のコードも、道具のコードも、あいだに置いた約束のほうを向く、という原則でした。コピーのボタンでいえば、コピーを頼むユースケースは ClipboardGateway という約束だけを知り、ブラウザの実装も、テストの代役も、その約束に合わせて作られています。実行時の呼び出しは外へ進み、import は内へ向かう。この向きのずれが「逆転」でした。

この回で確かめたこと
問い答え確かめ方
方針のコードは、ブラウザを知っているか知らない。約束(ポート)だけを知るdomain・application から infrastructure への import 0件
具体的な道具は、どこで選ぶか組み立て役(ShareContainer)の1か所BrowserClipboard の名指しは ShareContainer.ts だけ
境界が崩れたら、誰が気づくか内側の型検査・依存の境界のテスト・代役を渡すテスト実験1で4件、実験2で5件が落ちた(63件中)
何にでも約束を挟むか挟まない。変わりやすく試すのに邪魔な道具だけ3つの問いを順にたどる

約束は使う側が書き、道具のほうが約束に合わせる。そして、約束を挟むのは、変わりやすい道具だけにします。前回の ISP で約束を小さく分けたから、DIP の差し込み口に、代役でも本物でも気軽に差し替えられる。第2章で読んできた5つの原則は、どれも「変わるものと変わらないものの境目に、どう線を引くか」を、別々の角度から言っていたのだと思います。

ここで大事なのは、今回の実験で加えた変更を、プラグインの本体には入れていないことです。境界を破った2つの書き換えは、見張りが止めることを確かめるためだけのもので、一時作業コピーごと片付けました。

次回は連載2.6「共有先の登録――明示登録から自動探索へ」です。今回は、組み立て役が「どの約束に、どの実装を結ぶか」を1か所で選んでいることを見ました。次回は、その選び方のもう1つの面、共有先の名札カードを、手で一覧に書いて登録するのか、置いておけば自動で見つけてもらうのか、を考えます。一緒に見ていきましょう。それでは、次回もお楽しみに!

連載の全体像は本編の連載目次へ。前回の2.4「ISP――コピーの代役に、いいねの保存まで求めない」、前々回の2.3「LSP――Copyに「開くURL」を要求すると何が壊れるか」とあわせて読むと、コピーの差し込み口が、どんな原則の上に立っているかが一続きで見えてきます。

プロマリでは、Webサイトの制作に加えて、共有や計測のような「裏側のしくみ」の設計・導入、それらを運用できる人材の育成に取り組んでいます。テストしやすく、差し替えやすい作りをチームでそろえたい方も、お気軽にご相談ください。

Web制作・計測・人材育成のご相談はこちら

COMMENTS
コメント…

この記事全体の感想・質問・設計へのコメント

PASS IT ON

この気づきを、誰かにも。

この記事を書いた人

Takaomi Murasaki

Promari SNS Shareの開発を通して、Webの仕組みや設計の選び方を紹介しています。動く実装と、その判断に至るまでを記事に残しています。

公開記事 10 件

プロフィールを見る
THANK YOU FOR READING.すべての記事へ ↗

FROM INSIGHT TO IMPACT

「できたらいいな」を、
動く仕組みに。

記事で見つけたヒントを、あなたの事業へ。
新しいサービスも、手間のかかる業務も。
いまの課題から、つくるべきものを一緒に考えます。

開発・AI・研修の実績を見る

まだ、仕様書はいりません。

「何から始める?」から、ご一緒に。

課題がまとまっていなくても大丈夫。テーマを選ぶと相談文をご用意します。
連絡先の必須入力は、お名前とメールアドレスだけ。

まずは課題の整理から相談する

ご相談後の流れ

  1. 01 内容を確認
  2. 02 メールでご連絡
  3. 03 課題・進め方をご相談
送信だけで契約やお申込みが確定することはありません。