全国オンライン対応受付 平日 9:00-18:00
お問い合わせ

開発記録 / junka制作記 / Vol.25 junka制作記 Vol.2

記事 01

複数の教室を一つのシステムで管理する——マルチテナント設計への移行

「教室を増やしたいんですが、システムも新しく作らないといけませんか」

2026-06-07 公開

「教室を増やしたいんですが、システムも新しく作らないといけませんか」

入退室管理システムのオーナーから連絡が来たのは、最初のシステムを稼働させてしばらく後のことだ。

習い事教室の入退室管理システムを作った。子どもが教室に入ったとき、出たときに、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を設計した話を書く。

シンプルシステム株式会社 代表 伊藤勝彦