一枚のJSONが、チームに言葉を届ける

朝礼アプリをマルチテナント化した記録。複雑さを削ぎ落とすたびに、何が本質かが見えてくる。

この記事で言いたいこと:マルチテナントの設計は、最終的に「一枚のJSONファイルをどこに置くか」という問いに収束した。テナントごとにリポジトリを作る設計から、一つのリポジトリにディレクトリを切るだけの設計へ。複雑さを削ぎ落とすほど、本質が現れる。

1. 朝礼を、チームに届けたかった

個人用の朝礼アプリがある。毎朝、登録した言葉を一つ選んで復唱する。声に出し、記録し、積み重ねていく。シンプルな仕組みだが、続けると確かに変化が起きる。

これを組織に届けられないか、という声があった。社訓を、理念を、創業者の言葉を——毎朝、全社員のスマートフォンに届ける。朝礼の代わりに、あるいは朝礼の補完として。

仕組みとしては単純に見えた。管理者が言葉を登録する。社員はアプリを開く。それだけだ。だが「テナントごとに異なる言葉を届ける」という要件は、思いのほか設計の問いを立てることになった。

2. 最初の設計——テナントごとにリポジトリを作る

最初に思いついた設計は、テナントごとにリポジトリを作ることだった。

組織Aが申し込む → リポジトリを自動作成 → GitHub Pagesで公開
組織BのURLにアクセスすると、組織B専用の設定で動く

実装した。決済が完了すると、Webhookが走り、GitHub Actionsがリポジトリを作成し、Pages設定を有効化し、初期設定ファイルを書き込む。数分後には専用URLが動く。

動いた。だが、問題がすぐに見えた。

アプリのコードを更新するたびに、全テナントのリポジトリに変更が伝わらない。テナントが増えるほど、管理するリポジトリが増える。100テナントになれば100リポジトリ。1000になれば1000。コードと設定が混在した、コピーの山が積み上がっていく。

3. 問い——何がテナントごとに違うのか

立ち止まって考えた。テナントごとに本当に違うものは何か。

アプリのコードは、すべてのテナントで同じだ。UIも、動作ロジックも、通知の仕組みも。違うのは、アプリ名と、配信する言葉と、いくつかの設定値だけだ。

それをまとめると、一枚のJSONファイルになる。

app_name: "朝礼 — ○○株式会社"
preset_words: [{ title: "社是", text: "誠実に、大胆に、素早く。" }]
lock_preset: true

これだけだ。テナントとは、この一枚のJSONファイルのことだった。

4. 新しい設計——一つのリポジトリに、ディレクトリを切るだけ

設計を変えた。

テナントの設定を一元管理するpublicリポジトリを一つ作る。テナントが増えるたびに、そのリポジトリにディレクトリを追加し、tenant.jsonを置くだけ。アプリ本体は変わらない。

tenants/
 abc12345/tenant.json ← 組織A
 xyz67890/tenant.json ← 組織B
 ...

アプリは起動時に、URLパラメータからテナントIDを読み取り、対応するtenant.jsonを取得する。それだけで、そのテナント専用の朝礼アプリになる。

アプリのコードを更新すれば、全テナントに即座に反映される。リポジトリは増えない。設定と実装が完全に分離した。

5. 削除・更新・請求——すべてがファイル操作に収束する

この設計の美しさは、テナントに関するあらゆる操作がファイル操作に収束することだ。

テナントを作るとは、tenant.jsonを追加することだ。設定を更新するとは、そのファイルを書き換えることだ。テナントを削除するとは、そのファイルを消すことだ。

更新のキューも、同じリポジトリの別ディレクトリに積まれる。管理者が「保存して配信する」を押すと、Cloudflare WorkerがJSONファイルをキューに書き込む。1分おきに処理が走り、tenant.jsonに反映される。700の組織が同時に設定を保存しても、Workerはキューに積むだけで即座に応答を返す。詰まらない。

複雑に見えた仕組みが、ファイルの読み書きに帰結した。

6. 請求書払いという問い

カード決済は自動化できた。だが、大きな組織ほどカード決済を使えないことがある。稟議、購買部門、請求書払い——そのような文化の中で動く人たちが、確実にいる。

最初は「お問い合わせください」で済ませようとした。だが、それでは「銀行振込で申し込んだのに、どこに進めばいいかわからない」という状況が生まれる。申し込んだ事実と、テナントの準備と、入金確認が、バラバラに浮遊する。

解決策は、申込み時点でorder_codeを発行することだった。カード決済でも請求書払いでも、最初にorder_codeが生まれる。そのコードが、申込みと、設定と、入金確認と、テナントの起動を、すべてつなぐ。処理のタイミングが違うだけで、流れは同じだ。

7. 設計の純度が、運用のシンプルさになる

マルチテナントの設計を通じて、改めて感じたことがある。複雑さは、設計の選択の積み重ねから生まれる。

最初の選択が「テナントごとにリポジトリを作る」だったとき、その下流にあらゆる複雑さが連鎖した。コードの重複、更新の困難、管理の煩雑さ。一つの選択が、何十もの問題を作り出した。

設計を変えて「一枚のJSONだけが違う」にしたとき、複雑さの連鎖が止まった。何が変わるのか、何が変わらないのかが明確になった。変わらないものはアプリ本体に置き、変わるものだけをJSONに切り出した。

設計の純度が高いとき、コードは短くなり、説明も短くなり、運用も軽くなる。朝礼アプリの設定がたった数行のJSONで表現できるとき、それは設計の純度が高い証拠だ。

テナントとは、一枚のJSONファイルのことだった。コードは変わらない。言葉だけが、組織ごとに違う。

トキストレージは、声・画像・テキストを1000年保管する「存在証明の民主化」を目指すプロジェクトです。

トキストレージを知る すべてのエッセイを読む