「教室を増やしたいんですが、システムも新しく作らないといけませんか」
入退室管理システムのオーナーから連絡が来たのは、最初のシステムを稼働させてしばらく後のことだ。
習い事教室の入退室管理システムを作った。子どもが教室に入ったとき、出たときに、QRコードを読み込むと保護者のLINEに通知が届く。一つの教室では問題なく動いていた。
問題は、教室が増えるときだ。
最初の設計は「一つの教室」を前提にしていた。スプレッドシートのシート構成、GASの処理ロジック、LINEの通知文、どこにも複数教室という概念がなかった。
教室ごとに別のシステムを作ることはできる。GASのプロジェクトをコピーして設定を書き換えれば動く。しかしそれをやると、管理するコードが教室の数だけ増える。バグを直すたびに全教室のコードを修正しなければならない。
一つのシステムで複数の教室を管理する、いわゆるマルチテナント対応にすることにした。
設計を見直した。
各教室を識別するのに「テナントID」を使うことにした。実装上はLINE公式アカウントのチャンネルIDがそのままテナントIDになる。教室ごとにLINE公式アカウントを持っているので、Webhookを受け取った時点でどの教室からの通知かが分かる。
スプレッドシートには「教室マスタ」シートを追加した。テナントID、教室名、LINE設定、通知先などを一行に収める。GASはWebhookを受け取るたびにこのシートを参照して、該当教室の設定を取り出す。
function getTenantConfig(channelId) {
const sheet = ss.getSheetByName('教室マスタ');
const rows = sheet.getDataRange().getValues();
for (let i = 1; i < rows.length; i++) {
if (String(rows[i][0]) === channelId) {
return {
name: rows[i][1],
lineToken: rows[i][2],
notifyUids: String(rows[i][3]).split(','),
};
}
}
return null;
}
入退室の記録も教室ごとに分ける必要がある。一つのシートに全教室の記録を混在させると、集計もレポートも複雑になる。
解決策はシート名にテナントIDを含めることだ。「記録_教室A」「記録_教室B」という形で、教室ごとに別のシートを動的に作る。
function getOrCreateSheet(sheetName) {
let sheet = ss.getSheetByName(sheetName);
if (!sheet) {
sheet = ss.insertSheet(sheetName);
sheet.appendRow(['日時', '氏名', '種別', '通知先']);
}
return sheet;
}
教室が増えてもスプレッドシートのシートが増えるだけで、GASのコードは変わらない。
デプロイして2教室で動作確認した。
教室Aで入室QRを読んだとき、教室Aの保護者だけに通知が届く。教室Bには届かない。逆もそうだ。テナントIDで処理が分岐している。
管理者は一つのスプレッドシートを見るだけで全教室の状況を把握できる。教室マスタに行を足せば新しい教室が追加される。コードには触れなくていい。
「教室を増やすたびに新しいシステムを作る」手間がなくなった。
次回は、教室の数が変わっても崩れないUIを設計した話を書く。
シンプルシステム株式会社 代表 伊藤勝彦