← All guides

Object & data model

How the standard objects, record types, and custom fields fit together, plus the grain rules and relationship choices that decide whether reporting ever ties out.

GTM Systems guide

The worst object decision I ever inherited was a team that modeled “renewals” as a custom object hanging off Account instead of as an Opportunity with a type. It felt clean at the time: renewals are different, so give them their own home. Two years later the forecast could not see them, the pipeline report double-counted the ones a rep had also logged as an Opp, and the CRO’s ARR number reconciled to nothing because half the recurring revenue lived on an object the forecast engine never queried. Rebuilding it meant migrating three years of history and rewriting nineteen reports. The object model is the one architectural choice where a shortcut in month one costs a quarter in year two.

Most teams treat the object model as something the CRM ships with and you just fill in. It is closer to a load-bearing wall. Every automation, every rollup, every report reads the grain you chose, and the grain is nearly impossible to change once records and integrations depend on it. Get the objects and relationships right and the rest of the system has somewhere solid to stand. Get them wrong and you spend years papering over it with flows and formula fields that each add their own drift.

4-6
Standard objects that carry 90% of GTM
1 grain
Per object, or reporting stops tying out
years
Cost of a wrong relationship at scale

Standard objects first, custom objects only when forced

The instinct to build a custom object is almost always premature. Salesforce ships Lead, Account, Contact, Opportunity, and the junction objects (Opportunity Contact Role, Opportunity Line Item) that carry the vast majority of go-to-market process. Before you create anything custom, prove that no standard object fits, because the platform gives standard objects things custom objects have to earn: forecast integration, the price book relationship, native activity timelines, and a decade of reports built to expect them.

A custom object earns its place when it has a genuinely different grain and lifecycle. Contract line items with their own start and end dates that do not map to an Opportunity. Product usage snapshots that arrive daily per account. A partner-deal-registration record that a partner owns and a rep does not. Those are real objects. “Renewals,” “upsells,” and “expansion” are not: they are Opportunity types, and forcing them onto a custom object is how you amputate them from the forecast.

The core GTM object model Grain decides which object owns a field
Accountthe companyContactthe personOpportunitythe dealContact Rolethe committeeOpp Line Itemproducts, price1 : many1 : many
Account holds the company. Contact holds the person. Opportunity holds the deal, and Opportunity Line Items hold the products on it. Contact Roles carry the many-to-many that makes buying-committee analysis possible. Lead floats until conversion resolves it into Account plus Contact.

The grain rule: one meaning per object

Every object answers exactly one question about the world, and that question is its grain. Account answers “what do we know about this company.” Contact answers “what do we know about this person.” Opportunity answers “what is the state of this deal.” A field belongs on the object whose grain it describes. Company revenue and employee count describe the company, so they live on Account. Deal amount and close date describe the deal, so Opportunity. The test when you are stuck: if this value changes, how many rows should change? One row means you found the object.

The failure mode is mixing grains on one object to save a join. Stamping account_industry onto every Opportunity so a pipeline report is one field shorter feels efficient until the industry gets re-enriched on the Account and the 40 stale copies on old Opps drift away from the truth inside a quarter. Store it once at its true grain and read it through the relationship. If a report genuinely needs it on the child, use a formula field or a rollup, never a static copy.

FieldGrainObjectHow it is populated
Employee count, IndustryCompanyAccountEnrichment integration
Amount, Close date, StageDealOpportunityRep, user-entered
Product, Quantity, List priceLine itemOpportunity Line ItemPrice book + rep
Persona, Seniority, TitlePersonContactEnrichment or user
Buying rolePerson-on-dealOpportunity Contact RoleRep, user-entered
Account ARRCompanyAccountRollup, no direct writer

Master-detail or lookup: the relationship choice you cannot easily undo

Two objects can relate as a lookup (loose, independent lifecycles, optional) or master-detail (tight, cascade-delete, roll-up-summary enabled, child cannot exist without parent). The choice is not cosmetic. Master-detail gives you roll-up summary fields for free, which is often the whole reason to reach for it, but it also means deleting the parent deletes every child and reparenting is restricted. Lookups are forgiving and keep the objects independent, at the cost of writing your own rollups (via flow, Apex, or a tool like Rollup Helper) when you need aggregation.

Master-detail Lookup
Child without parent Not allowed; child is orphaned on parent delete Allowed; child survives independently
Roll-up summary fields Native, no code Not available; build your own
Delete behavior Cascade: parent delete removes children Independent: child remains
Reparenting Restricted by default Free
Best when Line items, tightly-bound children needing aggregation Cross-references between independently-owned records
Pick for the lifecycle, not the convenience of a free rollup. Converting between them later is a migration, not a setting toggle.

The trap I see most is defaulting every relationship to lookup because it feels safer, then discovering six months in that you need Account-level ARR rolled up from child records and lookups will not give it to you natively. Decide up front whether the parent needs to aggregate the children. If it does, and the child genuinely cannot exist without the parent, master-detail is the honest model.

Custom objects created vs custom objects that earned it
Illustrative audit of a mid-size org. Most custom objects duplicate a standard object's grain and should have been a record type or a picklist. The gap is where reporting complexity and license cost hide.
View as table
StageValue
Custom objects built34
Map to a standard object19
Could be a record type8
Genuinely new grain7

Record types partition process without multiplying objects

When the same object needs different page layouts, picklist values, or processes (a New Business Opportunity versus a Renewal, a Partner Account versus a Direct Account), the answer is record types, not a new object. Record types keep the grain unified so the forecast and the pipeline report still see everything, while giving each variant its own layout and its own subset of picklist values. This is the single most under-used lever for taming a sprawling model: most “we need a new object” requests are really “we need a record type.”

Object build order: six checks before you create anything
  1. 1

    1. Can a standard object hold it?

    Lead, Account, Contact, Opportunity, or a junction. Standard objects get forecast integration and native reports; custom objects have to earn them.

  2. 2

    2. Is this a new grain or a variant?

    Different meaning about the world means a new object. Different process on the same meaning means a record type.

  3. 3

    3. Name the single grain in one sentence

    If you cannot say what one row means in a sentence, the object is mixing grains and reporting will not tie out.

  4. 4

    4. Choose the relationship deliberately

    Master-detail if the parent must aggregate the children and the child cannot exist alone. Lookup for independent records.

  5. 5

    5. Place every field at its true grain

    Company data on Account, deal data on Opportunity. Expose to children via formula or rollup, never a static copy.

  6. 6

    6. Document what one record means

    Write the object's definition and its lifecycle so the next builder does not model a renewal as a custom object.

The object model is the one part of the system you build once and live with for years, so it deserves more thought than any single field or flow. Prove a standard object cannot hold it before you build custom. Use record types to partition process instead of multiplying objects. Keep one grain per object so reporting ties out. Choose master-detail versus lookup for the lifecycle, not the convenience. Get those four right and every automation, rollup, and report downstream has solid ground; get them wrong and you spend years building workarounds on a foundation that was pouring itself crooked from day one.