PROMARI JOURNAL

共有先が増えても触らないコードを決める

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

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

丸い共有ボタンの裏側を掘る連載の第2回。共有先が増えても書き換えないコードを先に決め、共有先の違いを「名札カード」と呼ぶデータファイル(TOML)へ寄せた設計を、架空の共有先を1つ足した差分の実測と図12点でたどります。

PASS IT ON

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

もし、分岐で書いていたら

分けた形の良さは、分けなかった形と並べると見えてきます。仮に、共有先ごとの違いを条件分岐で書いていたとしましょう。「xならこのURL、lineならこのURL、hatenaなら……」と、共有先の数だけ段が続く書き方です。ここでは「分岐のはしご」と呼ぶことにします。

図2-1 もし、分岐で書いていたら
図2-1 もし、分岐で書いていたら。説明例。分岐のはしごは、追加のたびに既存の段の隣を書き換えます。図2-1 もし、分岐で書いていたら。説明例。分岐のはしごは、追加のたびに既存の段の隣を書き換えます。

分岐のはしごで共有先を1つ足すと、いま動いている段のすぐ隣に、新しい段を差し込むことになります。足すたびに、既存のコードを開いて書き換える。閉じておきたかった場所に、毎回手が入る形です。しかも同じ分岐は、URLを作る場所だけでなく、色やアイコンを選ぶ場所、クリックしたときの動きを決める場所にも現れがちです。1つの追加が、何か所もの分岐に散らばっていきます。

今回の実装には、共有先の名前で処理を切り替える分岐が1つもありません。代わりに、共有先ごとの違いを、すべてデータの側へ寄せました。次は、その寄せた先をのぞいてみます。

共有先は、名札カードで宣言する

共有先は、それぞれが destinations/ フォルダにあるデータファイル1つで表されています。形式は、設定ファイルなどでよく使われる「項目名 = 値」を並べる TOML です。1つのファイルに共有先1つ分の名前・行き先・色などが並ぶ様子が、名前や所属を書いた名札に似ているので、この記事ではこのファイルを「名札カード」と呼びます。Xの名札カードを、アイコンのSVGだけ省いてそのまま載せます。

TOMLdestinations/x.toml(アイコンの中身を省略)
# X(旧Twitter)のポスト画面。
key = 'x'
label = 'ポスト'
brand_color = '#000000'
action = 'open'
endpoint = 'https://twitter.com/intent/tweet'
icon = '<svg …>…</svg>'

[params]
url = 'url'
text = 'text'
hashtags = 'hashtagsCsv'
via = 'via'

並んでいるのは、名前(key)、画面の文言(label)、共有画面のエンドポイント、渡す項目の対応表(params)、アイコン、ブランドの色、クリックでの動作(action)の7つです。どの項目も値を書いてあるだけで、計算も判断もありません。「どうやって作るか」ではなく「何であるか」を書くこの書き方を、宣言的と呼びます。

図3-1 共有先は、名札カードで宣言する
図3-1 共有先は、名札カードで宣言する。現在のしくみ。共有先はURLを組み立てません。どこへ何を渡すかを宣言するだけです。図3-1 共有先は、名札カードで宣言する。現在のしくみ。共有先はURLを組み立てません。どこへ何を渡すかを宣言するだけです。

[params] の読み方も押さえておきましょう。左辺がSNSの受け取るクエリパラメータの名前、右辺が記事から渡す情報の名前です。Xなら、記事のURLを url に、紹介文を text に、タグを hashtags に、アカウント名を via に入れて渡します。名札カードに書いてあるのはこの対応表だけで、URLを組み立てる処理はどこにもありません。共有先は、どこへ何を渡すかを宣言するだけなのです。

URLを組み立てるのは、1か所だけ

では、誰がURLを組み立てているのでしょうか。ブラウザ側の ShareDestination という部品です。名札カードから読み取った宛先と対応表を持ち、記事の情報を受け取って、共有画面のURLを1本作ります。中に持つ値だけで意味が決まる値オブジェクトとして作られていて、作ったあとは中身が書き換わりません。

図3.1-1 URLを組み立てるのは、1か所だけ
図3.1-1 URLを組み立てるのは、1か所だけ。現在のしくみ。違いはデータの側にあり、組み立てる仕組みは1つです。だから共有先が増えても触りません。図3.1-1 URLを組み立てるのは、1か所だけ。現在のしくみ。違いはデータの側にあり、組み立てる仕組みは1つです。だから共有先が増えても触りません。
TypeScriptweb/src/domain/model/ShareDestination.ts(shareUrl の部分)
shareUrl(request: ShareRequest): string {
  if (!this.#endpoint) return request.url;
  const pairs = Object.entries(this.#params)
    .map(([name, field]) => [name, FIELD[field](request)] as const)
    .filter(([, value]) => value !== '')
    .map(([name, value]) => `${UriEncoder.encode(name)}=${UriEncoder.encode(value)}`);
  return this.#endpoint + (pairs.length ? `?${pairs.join('&')}` : '');
}

見てほしいのは、この組み立ての中に「xなら」「hatenaなら」が一度も出てこないことです。対応表を上から順に読み、空の値は送らず、RFC 3986の符号化で安全な文字に置き換えてから「&」でつなぐ。XでもはてなブックマークでもLinkedInでも、同じ手順が同じように働きます。違いはデータの側にあり、組み立てる仕組みは1つ。だから共有先が増えても、このファイルは触りません。

共有先のデータは、ブラウザ用の束に焼き込まれる

ここで、ひとつ不思議に思われたかもしれません。名札カードはデータファイルで、URLを組み立てるのはブラウザで動くTypeScriptです。ブラウザは、どうやって名札カードの中身を知るのでしょうか。

答えは、ビルドの時点での受け渡しです。生成器(tools/config.py)が名札カードのファイルを読み込んでカタログにまとめ、ブラウザで動くコードと一緒に、1つのバンドルへ焼き込みます。

図3.2-1 共有先のデータは、ブラウザ用の束に焼き込まれる
図3.2-1 共有先のデータは、ブラウザ用の束に焼き込まれる。現在のしくみ。データファイルは設計図で、本番で動くのは生成された束です。図3.2-1 共有先のデータは、ブラウザ用の束に焼き込まれる。現在のしくみ。データファイルは設計図で、本番で動くのは生成された束です。

実行時のWordPressは、名札カードを読み込みません。このプラグインのWordPress側が受け持つのは、生成した設定を読み、共有欄の要素を出力することです。名札カードは、ブラウザ用の束を作るための設計図です。設計図は1か所にだけ書き、束は機械が作る。この分担のおかげで、共有先の行き先やロゴを手で2回書く場面がありません。

COMMENTS
コメント…

このページ(2ページ目)の感想・質問・設計へのコメント

PASS IT ON

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

この記事を書いた人

Takaomi Murasaki

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

公開記事 4 件

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

FROM INSIGHT TO IMPACT

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

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

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

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

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

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

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

ご相談後の流れ

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