シンプルさを保つ小規模チーム向け軽量CRMスキーマ
Contacts、Deals、Activities、権限をシンプルに保ちながら、信頼できるレポーティングと日常のワークフローを支える軽量 CRM スキーマの作り方。

この CRM スキーマが解決すべき問題
小規模チームは日々こうした質問に素早く答えられる CRM を必要とします:誰と話しているか、何をクロージングしようとしているか、最後に何が起きたか、次に何をすべきか。軽量な CRM スキーマの本質はこれらに答えることです。それに寄与しないものは多くの場合ノイズになります。
小さなチームは深いアカウント階層や多数のカスタムオブジェクト、複雑なスコアリングモデルは滅多に必要としません。必要なのは明確な所有権、タッチポイントのシンプルな履歴、誰もが同じように理解できるパイプラインです。
「シンプル」はフリーテキストや重複に依存すると壊れます。ある人がステージを「Negotiation」と書き、別の人が「negotiating」と書けば、レポートは推測になります。アクティビティが通話、会議、メモごとに別テーブルに分かれていると、複数の日付フィールドが生じて「最後に触れた日」の信頼できる値がなくなります。
このスキーマは、モンスター化しないでほとんどの小規模チームCRMをカバーする4つのコアオブジェクトに絞ります:
- Contacts(必要なら Organizations)として話す相手
- Deals(パイプラインで追う機会)
- Activities(タスク、会議、通話、メモの統一ログ)
- Permissions(誰が何を見て編集できるかの実用的ルール)
クリーンなレポーティングとは、今週のステージ別案件数、ステージ間の転換率、ステージ滞在の平均時間、商談ごとの最終アクティビティ、各担当の今日の未完了タスクなどを信頼して見られることです。チームが3人から15人に増えてもこれらのレポートが意味を保つべきです。
内部向けに AppMaster のようなノーコードツールで CRM を作る場合、このアプローチはデータベースを小さく保ちながらダッシュボードや週次レビューに安定した数値を提供します。
制約しすぎず軽量に保つための原則
軽量な CRM スキーマは「この事実はどこにあるか?」に一つの明確な答えを出すときにうまく機能します。同じ“真実”を二箇所に保存できると、保存されますし、レポートはぶれていきます。
少数のソース・オブ・トゥルース(真実の出所)オブジェクトを選び、その他はそこを参照するようにします。多くの小規模チームでは Contacts、Organizations(任意)、Deals、Activities です。詳細が必要になったら、ゴミ箱になりがちな単一のテキストフィールドに詰め込むより新しいテーブルを追加してください。
モデルをシンプルかつ柔軟に保つためのルール:
- 事実は一箇所:電話番号は Contact に、商談の金額は Deal に。
- 過負荷なフィールドより明示的なテーブルを:ステージ履歴はカンマ区切り文字列ではなくテーブルで管理。
- ID は安定、名前は編集可:会社名は変わっても主キーは変えない。
- 「ステータス」と「タイプ」は分ける:タスクは同時に “open” であり “call” であり得る。
- インポートは汚れる前提:空欄、重複、変なフォーマットは普通にある。
初日から汚れたデータを前提にするには、退屈でも強力なフィールドをいくつか追加します:created_at、updated_at、そしてコアテーブルにシンプルな external_id(または import_source + import_key)。これで再インポートしても重複を作りにくくなります。
具体例:スプレッドシートをインポートした結果 “Acme Inc.” が半分の行で “ACME” と出てきたとします。Organization.name を編集可能にして Organization.id を安定させておけば、後でレコードをマージしても既存の商談やアクティビティを壊さずに済みます。
Contacts と Organizations:実用的でシンプルな構造
軽量 CRM は最初の判断から始まります:人だけ追うか、人と会社を両方追うか。B2B に売っているなら、Contact(人)と Organization(会社)の両方がほとんどの場合必要です。B2C なら Organizations を省略してすべてを Contact として扱えます。
B2B の場合、関係はシンプルにしておきます:各 Contact は1つの主要な Organization(nullable)に所属し、1つの Organization は多くの Contact を持てます。これでほとんどの小規模チームのワークフローをカバーできます。
必須フィールドは最小限に
フィールドを多く必須にするとデータが乱れる最速の道です。良いベースラインは:
- Contact:
id、名前(またはfirst_name+last_name)、created_at - Organization:
id、name、created_at
他の項目(役職、ウェブサイト、住所、業界、ソース)は任意で良いです。後からルールを追加できますが、「N/A」などのプレースホルダで埋まったデータベースをクリーンにするのは難しいです。
メールと電話:厳格すぎないユニーク制約
メールをユニークにするのは魅力的ですが、共有の受信箱(sales@、info@)や再利用される電話番号のある小規模B2Bでは妨げになります。実務的な妥協案:
- 値が存在する場合のみユニークを強制(ユースケースに合う場合に限る)。
- 重複を許容しつつ、UI上で一致が見つかったらソフトワーニングを表示。
複数のメールや電話を扱うなら email_2 や phone_3 のような列を追加する代わりに別テーブル(例:ContactMethod に type、value、is_primary)を使ってください。レポートはクリーンに保たれ、モデルは自然にスケールします。
例:担当者 Pat が四半期中に転職した場合、Contact を Organization に紐づけておけば Pat を新しい会社へ移し、過去の連絡手段は履歴として残しつつ、会社別の集計も正しく保てます。
Deals とパイプライン:読みやすさを保つ構造
Deal は予測の単位です:明確な次のステップのある1つの購入機会。Deal レコードは小さく保ち、他は参照するようにします。
誰にでも説明できるフィールドから始めましょう:
- Deal name: リスト上で意味が通じる短いラベル
- Stage: パイプラインステージへの参照(手入力しない)
- Value: 期待金額(システム全体で1通貨を選ぶ)
- Owner: 前に進める責任者
- Close date: 現在の最良の見込み日
関係は商談に対して1人のプライマリコンタクトを選ぶと報告がシンプルになります。必要なら deal_contacts テーブルを追加して追加の関係者を紐づければ、すべてを多対多にする必要はありません。ほとんどの小規模チームは1人のプライマリ+任意の参加者で十分です。
ステージは CRM が混乱しやすい箇所です。ステージをフリーテキストで保存しないでください。ステージは参照データとして保存し、後で名前を変えてもレポートが壊れないようにします。最小のステージテーブルは次のようにできます:
stage_idpipeline_idstage_namestage_orderis_closed(または won/lost のフラグ)
小規模チームなら「リード」と「商談」を分けすぎない方が良い場合が多いです。ルールは単純に:実際に追跡する価値のある機会が来たら Deal にする。そうでないときは Contact に new や nurture のようなステータスを付けておきます。これでパイプラインが見やすくなり、中途半端な商談が数字を汚しません。
例:2人の営業チームが “Acme Renewal” を Sam が所有する商談として追い、ステージは “Proposal Sent”、金額12,000、来月にクローズ予定。プライマリコンタクトは購買担当、追加で財務承認者を参加者として登録。ステージ名と順序が固定されているのでレポートは一貫します。
Activities:タスク、会議、メモを1つのモデルで扱う
小規模チームは通話、メール、会議、メモのために別テーブルを必要としません。1つの Activity モデルで十分で、CRM の使いやすさとレポートの一貫性が向上します。
単一の Activity テーブル
発生した(または発生すべき)事象ごとに1レコードを作ります。type フィールドは小さな固定セット(例:call、email、meeting、note、task)にします。選択肢を短く保つことで全員が同じ語を選びやすくなります。
アクティビティを混乱なく紐づけるルール:
- 人に関するもの(フォローアップコール、紹介メール)は Contact にリンク。
- 収益に関するもの(価格交渉の通話など)は Deal にリンク。
- 両方関係する場合は両方にリンクできますが、パイプラインのレポートでは Deal を主要扱いにします。
実務では Activity に contact_id(nullable)と deal_id(nullable)、およびオーナーの owner_id(任意)を持たせます。
レポートに優しいタイムスタンプ
due_at と completed_at の両方を保持してください。役割は違います。due_at は何が起こるべきか(計画・負荷)を示し、completed_at は何が実際に起きたか(実行・サイクルタイム)を示します。
completed_at があるかどうかでステータスを派生させることができます:あれば完了、なければ未完了。これで同期の取れないステータス値を避けられます。
アクティビティの本文は summary や body のような検索可能な1フィールドに収めて、初期段階でノートを過度に構造化しないでください。例:「Maya との通話:予算確認、金曜に提案送付。」必要になったら後から専門フィールドを追加します。
所有権と可視性:実用的に保つ
所有権は次のアクションの責任者です。軽量スキーマでは通常、Deal(および多くの場合 Contact)に owner_user_id のようなフィールドを置きます。
「オーナー」には2つの意味があります:個人割当(特定の人)とチーム割当(グループ)。最初からすべてをチーム所有にしてしまうと、今日誰が行動すべきかの明確さが失われます。
多くの小規模チームに効くデフォルトは:すべては全員が見られるが、各商談には必ず1人のオーナーがいる、です。コラボレーションは簡単に保て、誰かが代行する必要があるときの権限制限の問題を避けられます。
より厳しい可視性が必要になったら、複雑なマトリクスではなく単一のスイッチにしてください。例:deals と activities に visibility フィールドを設け、public と private を持たせ、private は「オーナー(と管理者)のみが見られる」と定義します。
チームやテリトリーが必要になるのは次のような場合だけです:
- グループ間で互いの案件を見せたくない
- チーム別で業績を出し、人がチーム間を移動する
- 管理者は自分のグループにアクセスできるが全社は見せたくない
- リードを共有キューに入れて担当が取るフローがある
所有権はレポートに影響します。担当者別の数字をきれいに出すなら、現時点の owner_user_id を基準に集計します。チーム別の集計も欲しいなら owner_team_id を追加(またはオーナーのプロフィールから導出)しますが、どちらを源泉にするか明確にしておきましょう。
例:2人で共有の受信箱を使っている場合、新しい商談は owner_user_id = null、owner_team_id = Sales で未割当にします。Alex が拾ったら owner_user_id = Alex にし、チームは Sales のままにします。パイプラインは読みやすく、チーム合計も機能します。
権限設計:複雑にせず十分なコントロールを
シンプルな RBAC から始める
権限は「役割(誰)」「リソース(何)」「アクション(何ができるか)」の3つを分けると扱いやすくなります。これがロールベースのアクセス制御(RBAC)で、チームが大きくなっても理解しやすいです。
リソースはコアオブジェクトに近づけておきます:Contacts、Organizations、Deals、Activities、必要なら Pipelines(ステージを編集可能にする場合)など。アクションセットは小さく統一しておきます:view、create、edit、delete、export。
Export は分けて考える価値があります。閲覧権は広くても、データの一括抽出は制限したいことが多いからです。
オブジェクトレベルの権限(「この役割は商談を編集できるか?」)から始めるのが楽です。小規模チームではそれだけで数か月は十分に運用できます。レコードレベルのルールは複雑さの元なので本当に必要になってから追加してください。
実務的な第一歩は単一の所有ルールです:各レコードに owner_user_id があり、管理者以外は自分が所有するものだけ編集できる。もう一段必要なら team_id を加え、閲覧はチーム単位で許可、編集はオーナーのみ、のようにします。
レコードレベルのルールは本当に必要なときだけ
以下のような場合にレコードレベルの権限を追加します:
- 営業が互いの商談を見てはいけない
- サポートは商談を閲覧できるが編集はできない
- 管理者は全て見られて所有者の再割当ができる
監査は軽量でも実装してください。各主要テーブルに created_at、created_by、updated_at、updated_by を追加します。より深い履歴が必要になったら小さな audit_log テーブルを作り:オブジェクトタイプ、レコードID、アクション、誰が、いつ、変更されたフィールドの短い JSON を保存します。
ステップバイステップ:週末でスキーマを作る
これは小さなプロダクトとして扱うと最も上手くいきます:まずデータを定義し、実データで動くことを証明してから画面を作る。
1日目:データモデルを固める
紙かホワイトボードで簡単な ERD を描きます。テーブル数は少なく保ちつつリンクは明確に:Contact は(任意で)Organization に属し、Deal はパイプラインに属しオーナーを持ち、Activity は Contact と/または Deal に関連するなど。
次に基礎を順に設定します:
- オブジェクトと関係を定義:contacts、organizations、deals、activities、users/roles、必要なら小さなルックアップテーブル。
- 必須フィールドとデフォルトを定義:
created_at、owner_id、重要な名前は必須に。金額を使うならステージと通貨のデフォルトを設定。 - 列挙型やルックアップを定義:deal stages、activity types などを固定し、レポートの整合性を保つ。
- 日常的にフィルタするカラムにインデックスを追加:
owner_id、stage、due_at、created_at、およびよく使う外部キー。 - 20件程度の実データでテスト:実名、日付、雑なメモを入れて何が壊れるかを見る。
2日目:レポートがきれいに出ることを検証する
フォームを作る前に、チームが毎週聞く6〜8の質問を書き出してください。例:「ステージ ‘Negotiation’ の商談をオーナー別に」「期限切れアクティビティ」「今月の新規コンタクト」「月別の受注金額」。質問に難しい結合や特別処理が必要なら、その時点でスキーマを直します。
簡単なテストシナリオを作ると効果的です:1社に3人のコンタクト、異なるステージの2件の商談、そしてそれらにまたがる6件のアクティビティ(通話1、会議1、タスク2、メモ2)を追加して、「誰が所有しているか」「次に何があるか」「先週何が変わったか」を推測なしに答えられるか確認してください。
データが固まったら UI と自動化を最後に作ります。そうすればレポートに合うように後で履歴を書き換える必要が減ります。
レポートを乱すよくあるミス
レポートが混乱する原因は「今日早く済ませるためのクイックフィックス」を積み重ねた結果であることが多いです。軽量 CRM スキーマがうまく機能するのは、データに明確な形がありチームが実際に従うルールがあるときです。
よくある落とし穴はすべてを一つの “customer” テーブルに押し込むことです。簡単そうに聞こえますが、「一つの会社に紐づく商談はいくつあるか?」「どの人が転職したか?」といった基本的な質問に答えられなくなります。人(contacts)と会社(organizations)は分けてリンクしてください。
もう一つの致命的なミスは商談ステージをフリーテキストにすること。ある人は “Negotiation”、別の人は “negotiating” と打つとパイプラインのグラフは壊れます。ステージは固定リスト(またはステージテーブル)にして、すべての商談が同じ集合を参照するようにしてください。
複数値を1フィールドに詰め込むのも避けましょう。カンマ区切りのメールや電話は検索、重複除去、エクスポートを面倒にします。本当に複数値が必要なら子テーブルに一行ずつ保存してください(例:メールは1レコードずつ)。
アクティビティは日付が不明瞭だとめちゃくちゃになります。「日付」1つでは、先週の金曜に期限だったのか先週の金曜に完了したのか区別できません。これらの概念は分けて保持すれば期限切れや完了レポートが正しく出ます。
将来の自分を助けるチェックリスト:
- Contacts と Organizations を分けてリンクする
- ステージは ID を使い、名前はタイプ入力しない
- 1フィールドに1つの値;複数値は子テーブルへ
due_atとcompleted_atを分離する- 最初はシンプルなロールにし、実際のワークフローが出てきてからレコードレベルルールを追加する
例:チームが通話をメモとしてログして後で「完了」にしていると、フォローアップにかかった時間を報告できません。フィールドを分けておけばそのレポートはすぐに作れます。
スキーマ確定前の簡単チェックリスト
画面、オートメーション、ダッシュボードを作る前に、実際のサンプルデータ(20件のコンタクト、10件の商談で十分)でレポートとルールをチェックしてください。スキーマが小さいうちに直しておけば軽量さを保てます。
問題を見つける5つのチェック
- レポート基礎:ステージ別、オーナー別、クローズ月別に商談をグループ化できるか?どの日時フィールドを使うかで迷わないか?
- ワーク管理:単一の due 日と明確な完了判定で「オーナー別の期限切れアクティビティ」を一つのレポートで引けるか?
- Contact と Organization:組織なしでコンタクトを作れるか、後から組織に紐づけても履歴(メール、メモ、商談参加)が壊れないか?
- 一貫性:ステージとアクティビティタイプは固定リストから来ているか?“Follow up” と “Follow-up” と “Followup” が別々に生まれていないか?
- 安全性:レコードの削除やエクスポートを誰ができるか制限できるか?通常の更新(商談を次のステージに動かす等)を阻害しないか?
これらすべてに「はい」と答えられれば良い状態です。違うならスキーマがまだ小さいうちに直してください。
実用的なヒント:ステージとアクティビティタイプは一箇所(テーブルか enum)で定義し、すべての画面と処理で再利用するようにしましょう。
小規模チームの現実的な例と次のステップ
5人のエージェンシーは軽量 CRM の良い試験台です。チームは忙しく、リードはあちこちから来て、誰もデータの世話をしたくありません。想定:管理者1人、営業2人、オペレーション1人、閲覧専任の創業者1人。
新しいインバウンドリクエストが来たら(フォームかメール):「ウェブサイトのリフレッシュが必要、予算15k、納期6週間」。ルールはシンプル:会社なら1つの Organization と 1人の Contact を作り、商談をその Organization に紐づけ、主要な担当者をその商談に設定します。
手早くするために小さな受け入れチェックリストを使います:
- ドメインや会社名が既存の Organization と一致すれば使い回す
- その人のメールが既にあれば既存の Contact を使う
- 実際に購買意図があるなら必ず Deal を作る
- 元のメッセージは商談の説明欄に残す
- ソース(referral、form、outbound)は単一フィールドで管理する
Activities はミス防止に効きます。次のステップは必ず日付付きで誰かに割り当てられるからです。営業がディスカバリーコールをしたら、その会議を meeting としてログし、次に2日後のコールをタスクで追加します。オペレーションが見積もりに詳細を要するなら同じ商談にタスクを作れば一箇所で見られます。
ロールは実用的に:Admin は全て編集と設定管理、Sales は自分のコンタクト・商談・アクティビティを作成更新、Ops は納品関連フィールドとアクティビティを更新、Read-only はパイプラインとレポートを閲覧のみ。
これを実働ツールに素早く変えるなら、AppMaster(appmaster.io)は分かりやすい選択肢です:Data Designer でスキーマをモデル化(PostgreSQL)、Business Process エディタでワークフローを追加、シンプルなリード受信箱と商談ページを作って、準備ができたら AppMaster Cloud または自分のクラウドにデプロイできます。
よくある質問
4つのコアオブジェクトから始めましょう:Contacts(人)、Organizations(任意の会社)、Deals(商談)、Activities(統一されたログ)。チームの質問がそれらのどれかに当てはまれば、速く動けてレポートも壊れにくくなります。
B2B で会社別の集計が必要だったり、複数の担当者が同じ顧客に所属することがあるなら Organizations は必要です。B2C や「人」一択で売る場合は Organizations を省いて Contacts のみで問題ありません。
ステージを自由記述にするとスペルや表記揺れでダッシュボードが壊れます。ステージは固定リスト(ステージテーブル)として管理し、各 Deal にステージIDを保存してください。こうすれば歴史的データを壊さずにステージ名を変更できます。
必須フィールドは最小限に:Contacts/Organizations は id、名前、created_at。Deals はステージ、担当者(owner)、金額、クローズ予定日などのコア項目だけ必須にします。必須項目を増やしすぎると「N/A」などの placeholder が増えてデータが汚れます。
ワークフローに合っていると確信がない限り重複を完全にブロックしないのが実務的です。実務での妥協案は、重複を許容しつつ類似のメールや電話が見つかったら UI でソフト警告を出すこと、そして external_id(または import_source + import_key)を入れて再インポート時に重複を作らない方法を用意することです。
呼出、ミーティング、メモ、タスクごとに別テーブルを作る必要はほとんどありません。type が小さく固定された値(call、email、meeting、note、task)の単一 Activity テーブルを使うと、最終タッチや期限切れの管理が一貫します。
due_at と completed_at は別々に保持してください。due_at は計画・負荷管理向け、completed_at は実行やサイクルタイム分析向けです。両者を混ぜると期限・完了の両方のレポートが信頼できなくなります。
デフォルトは商談に1人のプライマリコンタクトを置くことです。報告が明確になり UI も簡潔になります。追加の関係者が必要なら deal_contacts のようなジョインテーブルで参加者を添付してください。初めから多対多にする必要はほとんどありません。
実用的なデフォルトは「全員が見られるが、各商談には次のアクションを担当する1人の owner がいる」です。後からプライバシー要件が出たら visibility フィールド(public / private)のような単純なスイッチを追加する方が、初めから複雑な権限マトリクスを作るより安全です。
まずテーブルをモデル化して、実際のサンプルデータで検証することです。スキーマで「ステージ別の案件」「期限切れアクティビティ」「商談ごとの最終アクティビティ」が簡単に取れないなら、画面や自動化を作る前にスキーマを直してください。


