組織やコミュニティでクローズドに使えるイベント管理アプリ『CAMPUS』をリリースしました🚀

こんにちは、フィヨルドブートキャンプ(以下、FBC)で学ぶsugiweと申します。

この度、組織やコミュニティでクローズドに使えるイベント管理アプリ『CAMPUS』をリリースしました🎉

チーム内だけで使えるイベント管理アプリ、『CAMPUS』

この記事ではCAMPUSの開発背景や今後の展開、開発の振り返りなどについて書いていきます。

🌟CAMPUSについて

🌍 サービスURL

https://campus.bz/

campus.bz

🎨 サービス概要

CAMPUSは、会社やコミュニティのメンバーだけで使えるクローズドなイベント管理プラットフォームです。

一般的なイベント管理プラットフォームでは、登録イベントは基本的にすべてオープンです。登録ユーザーであれば、どのイベントにも自由に参加申し込みできる仕組みになっています。
それに対してCAMPUSは、会社やコミュニティなど特定のメンバーだけで使えるクローズドな環境を提供しているのが特徴です。

👯 こんな人たちに使ってほしい

CAMPUSは「イベントを通じて組織・チーム内の交流を促進したいけど、うまく運用できていない…」という人たちに使ってほしいサービスです。

  • 社内で勉強会やランチ会、交流イベントを実施しているものの、告知がチャットで流れていってしまい困っているチーム
    • Slackで告知したのに気づかれない
    • 参加者の取りまとめが毎回手作業
  • リモートワークでメンバーが全国に分散しており、オンラインランチや雑談会など、ゆるい交流の場をつくりたいチーム
    • チームの一体感を作りにくい
    • 新メンバーの交流機会が少なく馴染みづらい
  • コミュニティ運営で、イベント管理をもっと手軽にしたい人たち
    • SlackやDiscord上だとイベントが埋もれてしまう
    • Googleフォームでは堅すぎるし、継続運用が面倒
  • 学習コミュニティ・学生団体など、クローズドなメンバーだけでイベントを共有したいグループ
    • 毎月の企画をLINEやメールで案内していて、管理に困っている
    • 気軽な交流の場をもっと増やしたい

📜 開発の背景

CAMPUSには元ネタがあります。今回の開発をするにあたって欠かせない話なので、よろしければお付き合いください🙏

🐣 CAMPUSが生まれたきっかけ

時は2019年に遡ります。

当時、僕の前職であり今でも業務委託で関わらせてもらっている企業で、新規事業立ち上げのお手伝いをしていました。そこでいろいろなアイデアを出しては議論する中で生まれた案の1つがCAMPUSです。

前職企業では「自分で社員旅行の企画を立ち上げて参加者を募ることができる」というユニークな社員旅行制度があります。
それまでは、企画をメールやチャットで集めてエクセル管理して社員アンケートを取って参加したい企画を決めて…というようなやり方をしていましたが、これをWebアプリで実現できるようにしよう、というのがCAMPUSの原型です。

新規事業開発チームではアプリ開発のノウハウもなく、かといってアイデアをすぐに外部の開発会社に発注する予算もありません。そこで、某CMSを使ってさまざまなプラグインを駆使し、最低限必要な機能を持つプロトタイプを作成しました。
まずこのプロトタイプを社内で使ってもらい、その手応え如何によっては新規事業の本流として開発を進めていく可能性もありました。

🔥 解散、そして6年越しの再挑戦

ところがそれからほどなくし、諸々の事情で新規事業開発チーム自体が解散となってしまいました。他のアイデアも進めたりしていたのでとても残念でしたし、開発のためには別の開発会社への依頼も必要だったりしたので、「もし自分に開発する力があれば、もっとスピーディに開発を進められて解散にはならなかったかもしれないのに…」と悔しく思ったことも覚えています。
(CAMPUSプロトタイプ版はその後も社内で使われ続けていましたが、新しい機能追加や改善活動については止まっている状態でした)

そこで今回、FBCの最終課題であり集大成である自作サービスとして、改めて前職で使ってもらえるようCAMPUSをRuby on Railsで開発することにしました。

もっと言うと、僕はCAMPUSを自分の手でゼロから開発できる技術を身につけるためにFBCの門を叩いたと言っても過言ではありません。

6年越しに目標を実現することができ、とても嬉しいです。

🧑‍💻 CAMPUSの使い方

🚀 今後の展開について

CAMPUSは現在ベータ版として無料で提供していますが、将来的に有料プランを導入したいと思っています。

今後のロードマップとして以下のような追加機能を検討しており、その中のいくつかは有料プランの機能として解放できればと考えております。

  • 各種通知機能
    • より便利にCAMPUSを活用するための、メールやチャットツールなどへの通知機能
  • 管理者用ダッシュボード機能
    • メンバーごとのイベント参加状況やイベント作成数ランキングなど、管理者に便利な情報の集約
  • 開催報告機能
    • イベント開催後には当日の写真や振り返り文章などを追加掲載できる機能
  • サブドメイン方式
    • チームごとに teamname.campus.bz のようなシンプルなURLを持てる仕組み
  • イベント画像の自動生成
    • 生成AIを用いてイベント用画像を簡単に作成できる機能




ここからは、CAMPUSの開発で使用した技術や、開発を通じて学んだことについて紹介します。技術的な内容が含まれますので、スキップされたい方は おわりに へジャンプしていただいても大丈夫です!

🛠️ 技術スタック

技術スタックは以下のようになっています。

💡 開発で工夫したこと・学んだこと

ここではCAMPUSを開発するにあたって工夫したことや学んだことを紹介します。

🏢 マルチテナント方式にした

経緯で書いたように、CAMPUSはまず前職の企業で使ってもらうことを念頭に置いたアプリです。一社で使うだけであればマルチテナント方式にすることは必須ではありませんでした。

ただ、せっかくこのサービスをゼロから開発するのであれば、他の企業やコミュニティなど、いろいろな方に使ってほしいと思い、CAMPUSは開発初期時点からマルチテナント方式を採用しました。

ここでいうマルチテナント方式とは、Slack・Notion・esaのように、利用する団体ごとに独立したグループ(スペース)を作成し、その内部でメンバー同士がやり取りしたり情報を共有したりできる仕組みのことを指します。

🎯 オブジェクト指向ベースなコードの書き方

条件判定をする際にはビュー側であれこれ条件分岐をするのではなく、モデル側でメソッドを作って呼び出すだけ、という書き方にするとコードの見通しが良くなります。頭ではわかっていたつもりなのですが実際にはビュー側の条件分岐が多発しており、メンターさんにたくさんの指摘をいただきました。

# 修正前(ビューで条件分岐)
<% if membership.role == 'admin' || membership == target %>

# 修正後
# モデルでメソッド化
def can_edit?(target)
  self == target || admin?
end

# ビューでは呼び出すだけ
<% if membership.can_edit?(target) %>

特にメンターさんに言われて印象的だったのは、「『あれ持った?これ持った?じゃあお出かけするよ』と子ども扱いするかのようにあれこれ尋ねるのではなく、シンプルに『出かける準備できてる?』とオブジェクトに問いかけるだけで済むようにしよう」という言葉です。

🎨 ビューファーストな開発

部分的にではありますが、先にビューを作ってからモデル・コントローラーを実装するという順番の開発をしてみました。

一般的なRailsアプリ開発では、まずモデルを定義して rails console で動作を確認しながら進める、というような流れが多いのではと思います。
それに対して今回は、先にビューファイルを作るというアプローチを試してみました。

これは自分がWordPressで独自テーマを作ってサイトを作成するときによく行う方法をベースにしています。(まず先に静的ページを作ってしまい、その後で必要な箇所でHTMLをPHPに書き換えてWordPressテーマにしていく、という手法)

先にビューファイルだけでそのページを全部作ると、ブラウザで完成版に近い形のページを見ることができて、作っていてテンションが上がります😄 また、完成イメージが明確になるので、必要なデータ構造やロジックが自然と見えてくるという効果もあったように思います。

先にデザインを入れる必要がありますので、デザイン作成を先にやっても良いというケースに限られるかもしれませんが、個人的には結構おすすめの手法です。
(今回はTailwind CSSを導入したので1ページずつビューを先に作るのもやりやすかったなと感じていますし、生成AIの力を借りるともっとラクに実現できそうな気がしています)

💻 その他

その他にも以下などの工夫・苦労・学びがありました。印象に残っているものをいくつかご紹介します。

  • Rails Wayに則ったリソース設計
    • 当初は「招待への参加」をカスタムアクション(new_membership, create_membership)で実装する想定をしていましたが「招待の受諾」を独立したリソース(Acceptances)として捉え直すことで、よりRESTfulな設計に改善することができました。
  • 公開エリアとメンバー専用エリアの完全分離
    • これも当初の想定から改善したものですが、招待リンクは公開エリア(/invitations/*)、チーム管理はメンバー専用エリア(/teams/:slug/*)として、ルーティング構造で完全に分離しました。公開・非公開ページを構造で解決することでコードがシンプルになり、セキュリティ上のリスクも軽減できたと思います。
  • Active Storageの画像アップロード機能
    • Railsでの画像アップはActive Storageが定番ですが、デフォルトのままでは使い勝手に課題を感じました。CSSでアップロードボタンを別途用意したりStimulusを活用してプレビュー機能をつけるなど、少しでも使いやすくなるように工夫しました。
  • enumはinteger型にするかstring型にするか?
    • DB軽量化や将来的な変更柔軟性を考えるとinteger型も良かったのですが、直接DBを見た時の可視性などを考えて今回はstring型を選択しました。何日も悩んだり人に相談したりもして、トレードオフを考えて決めました。enumで扱う型について検討できたのは勉強になったし面白かったです。
  • ユーザーフレンドリーな@付きURL
    • これは自分の好みで実装した面もありますが、メンバープロフィールのURLを/teams/:slug/@:usernameの形式にすることで、直感的なURLを実現しました。(あと、URLに自分のアカウント名が入っていると嬉しい✨)
  • インフラはHerokuを選択
    • リージョンや費用面のデメリットはあるものの、デプロイの容易性や過去の知見を活用しやすいという観点から、インフラはHerokuにお任せしてアプリケーション開発に専念するという作戦を選択しました。
  • Hotwire導入のトライ
    • 全体でTurbo Driveを適用しページ全体の再読み込みを減らしたり、カテゴリー作成・更新の箇所ではTurbo Streamsを活用することで、部分的にではありますが最低限のJavaScriptでシームレスな画面遷移を実現できました。Hotwire導入は必須ではなかったのですが、今後使っていく技術として今回触れられたのは良かったです。

🎊 おわりに

開発にあたって相談に乗ってくださったりレビューをしてくださったFBCのメンターの皆さま、そして輪読会やもくもく会、その他オンライン・オフラインの場で話を聞いてくれたFBCの在校生・卒業生の皆さまに心から感謝しています。これからもよろしくお願いします。

また、僕がCAMPUS開発に改めて挑戦することを快諾していただき、そして現在進行形で協力してださっている前職の皆さまにも感謝を申し上げます。
まだまだ発展途上のサービスですが、ぜひ触ってみて、感想を聞かせてもらえたら嬉しいです。




同じFBC生のシモカワさんも最近、自作サービス『Rubree』をリリースされました!
リリースブログはこちら → Rubular の進化版「Rubree」リリース – Ruby × Rails × Wasm で正規表現をワンストップ管理 - aim2bpgのブログ

Ruby正規表現を学習・テスト・可視化・置換・コード生成まで、ブラウザだけで一度に扱えるワンストップ型ツール

めちゃくちゃ便利かつ完成度の高いサービスで、本当にすごいです✨
これから正規表現を扱う際にはガンガンRubreeのお世話になると思います、素敵なアプリ開発をありがとうございます&リリースおめでとうございます🎉