ノート共有アプリは便利な反面、不適切な投稿や迷惑行為への備えがなければ安心して使えません。
メディカルサークルでは、通報・ブロック・コンテンツ非表示という 3 つの仕組みを土台に、ユーザーが自分で快適さをコントロールできる設計にしました。
本記事では、その実装方針を整理します。
目次
通報・ブロック・非表示を別コレクションに分離する
3 つの機能は、それぞれ別のコレクション(reports / blocks / hidden)に分離しました。
1 つにまとめるとセキュリティルールの条件が爆発し、保守が一気に重くなります。
分離した方が各ルールがシンプルに書け、クライアント側のフィルタ処理も見通しが良くなりました。
また、それぞれの利用頻度やアクセス権限が異なるため、コレクションを分けたことでパフォーマンスも安定しています。
ブロックはクライアント側フィルタ + ストリーム監視で全画面に反映する
ブロックは「相手のファイル・コメント・メッセージが自分から見えなくなる」必要があります。
ここでは Firestore クエリ側で除外するのではなく、クエリ結果をクライアント側でフィルタする方式を採用しました。
漏れ防止のため、ブロックリストの取得は起動時 1 回ではなくストリームで常時監視し、ブロック操作した瞬間に全画面のフィルタが自動で更新されます。
新しい画面を追加する際も「ブロックリストで除外する」1 行を挟むだけで済むため、機能拡張に強い設計になりました。
フィルタとパフォーマンスを両立する
タイムラインや検索結果は、ブロックと非表示を考慮しながらパフォーマンスも保たなければなりません。
メディカルサークルでは、Firestore クエリ自体はシンプルに保ち、フィルタ処理はすべてクライアント側で行う方針を採用しました。
クエリ条件を複雑にして複合インデックスを増やすより、軽い後処理に倒した方が長期の運用コストも低く済みます。
セキュリティルールで公開・非公開を担保する
ファイルの作成・読み取り・更新・削除はすべて Firestore セキュリティルールで制御しています。
苦労したのは、フィールドが未定義の場合のハンドリングです。
古いドキュメントに新しいフィールドが存在しないケースで、ルールが意図せずアクセスを拒否してしまうことがありました。
デフォルト値付きの安全な読み出し構文を使い、null 安全に判定するパターンで解消しています。
通報フローと運営対応を 1 本のデータフローで設計する
ユーザーがアプリ内で通報ボタンを押すと、理由選択と任意コメント付きの通報ドキュメントが Firestore に作成されます。
ドキュメント ID は「通報者 + 対象種別 + 対象 ID」の組み合わせで決定的に生成しており、同じ人が同じ対象を二重通報できない構造です。
運営側は管理画面でこれらを一覧表示し、対応状況を更新できます。
シンプルな構造ですが、運営の処理速度と監査性のバランスが取れています。
アップロード前の確認 UI でやさしく誘導する
アップロード画面では、送信ボタンの手前に「私個人のノートです」「教科書・市販教材の複製ではありません」「他者の著作権を侵害していません」の 3 項目のチェックボックスを設置しました。
全てチェックしないとアップロードできない設計です。
さらに、ユーザー名・タイトル・コメント・チャット・自己紹介の入力フォームには NG ワードフィルタをバリデーションに組み込み、不適切な表現が含まれていれば送信前にエラーを出します。
あくまで一次フィルタとしての位置づけで、最終判断は通報機能と運営対応に委ねる二段構えです。
まとめ
コミュニティ機能は「便利さ」より先に「安心」が前提になる領域です。
メディカルサークルでは、通報・ブロック・非表示・ルール・UI 誘導という 5 つのレイヤーを組み合わせ、運用負荷も含めて持続できる設計に整えました。
どれか 1 つに頼らず、複数の手段を重ねて初めて成立する世界観だと感じています。

.webp%3Falt%3Dmedia%26token%3D6ca2c2ef-9413-4453-b992-55b66b11ed54&w=3840&q=75)


.webp%3Falt%3Dmedia%26token%3D900f385d-12a2-449b-8d1e-83a57cef0088&w=3840&q=75)
.webp%3Falt%3Dmedia%26token%3D0e802fb0-2dda-44a7-bf80-5d39019635ba&w=3840&q=75)
.webp%3Falt%3Dmedia%26token%3D3fb3dc66-ecca-402e-8fb8-fbec9407f7f5&w=3840&q=75)
.webp%3Falt%3Dmedia%26token%3Ddb21d760-e1ed-4ec2-af28-3462041e31b5&w=3840&q=75)
.webp%3Falt%3Dmedia%26token%3Dcce7bd72-f11e-4292-86bf-e6ccf3e7bf32&w=3840&q=75)
.webp%3Falt%3Dmedia%26token%3D457ff920-e0df-4ff5-95eb-e29f74b73823&w=3840&q=75)
.webp%3Falt%3Dmedia%26token%3Dc21fcc77-7404-458d-9eb5-85b8d84ae1bc&w=3840&q=75)
.webp%3Falt%3Dmedia%26token%3D92052f12-5280-49df-877a-b514582e95db&w=3840&q=75)

.webp%3Falt%3Dmedia%26token%3Da7c14698-1b08-4fea-89c6-f77a9121f4c5&w=3840&q=75)
.webp%3Falt%3Dmedia%26token%3D899eeefd-f4c9-44a6-9ec2-3ced0b223ffd&w=3840&q=75)
.webp%3Falt%3Dmedia%26token%3Dca25fa6b-e233-43f7-90c3-e68e4c5b0bc5&w=3840&q=75)
.webp%3Falt%3Dmedia%26token%3D7f18e5f1-cfda-4148-ab86-b3d2e6547262&w=3840&q=75)
.webp%3Falt%3Dmedia%26token%3D5f10e078-4d87-4c87-928c-21b719cbf1cb&w=3840&q=75)
.webp%3Falt%3Dmedia%26token%3D957b18b6-9b01-4c94-9207-7b9fca22a787&w=3840&q=75)
.webp%3Falt%3Dmedia%26token%3Dd952e11d-4461-47ae-892d-622fc3f2a48a&w=3840&q=75)
.webp%3Falt%3Dmedia%26token%3D532bb657-5670-49b4-9165-5f758062d8dd&w=3840&q=75)