Course Content
Object Types
Define the nouns of your business: customers, flights, sensors, orders. Properties, primary keys, titles
What is an object type?
An object type is the definition of a kind of entity — the class. The instances of that class are objects.
If you come from a programming background, the analogy is exact:
class Customer { // ← object type (definition)
customerId: string; // ← property
firstName: string;
region: Region;
}
const alice = new Customer(...); // ← object (instance)
const bob = new Customer(...); // ← object (instance)If you come from relational thinking: an object type is roughly a table; an object is a row. The difference is that an object type carries a lot more than a table schema — display names, descriptions, primary key, title key, link bindings, and governance.
The minimum viable definition
Every object type needs at least:
- An API name — stable, immutable, used in code (
Customer,Shipment). - A display name — what users see (
"Customer","Shipment"). - A description — what this entity means in the business.
- A primary key — the property that uniquely identifies an instance.
- A title key — the property used to label an instance in UIs.
- At least one property.
Example (we use a YAML-ish pseudo-schema throughout this course; the exact syntax varies by platform):
objectType: Customer
displayName: Customer
description: >
A business or individual that has signed a service agreement with us.
Excludes prospects, trial accounts, and internal test users.
apiName: customer
primaryKey: customerId
titleKey: companyName
properties:
- name: customerId
type: string
description: Stable identifier from the operational DB
- name: companyName
type: string
- name: region
type: enum<Region>
- name: signedAt
type: timestampThat description matters more than people expect. A clear, scoped description prevents the “what counts as a customer?” debate every six months.
Choosing a primary key
The primary key is the decision that locks in the longevity of your object type. Get it right.
Good primary keys:
- Are stable — they never change for the lifetime of the entity.
- Are business-meaningful —
shipment_id = "SHP-2026-04-13-00871", not42. - Are traceable — you can find the same ID in the source system.
Avoid:
- Autoincrement integers from a single environment. They collide across dev/staging/prod.
- Composite keys when you can mint a single ID. Composites make link types painful.
- Mutable natural keys like email addresses — emails change.
- UUIDs with no business meaning — they work, but make debugging miserable. If you must use them, expose a human-readable secondary identifier too.
If a source system gives you a stable ID, reuse it. If not, mint one with a clear, structured format and put the source’s ID as a regular property.
Title key vs primary key
The title key is what users see in dropdowns, breadcrumbs, link previews — the human label.
- Primary key:
customerId = "cust_1f7a3"— for machines. - Title key:
companyName = "Globex GmbH"— for humans.
The title key does not have to be unique. Two customers can both be named “Acme Corp” — the primary key still distinguishes them.
Intrinsic vs derived properties
A property is intrinsic if it comes directly from the datasource:
Customer.companyName— comes from the operational DB.Customer.signedAt— recorded at signup.
A property is derived if it is computed:
Customer.totalLifetimeValue— sum of all order amounts.Customer.isActive—lastOrderAt > now() - 90d.
Both can live on an object type, but they should be tagged differently. Derived properties:
- Are computed by functions (covered in a later lesson).
- May be cached but should always be reproducible.
- Should not be writable by actions (that would invert the dataflow).
Mixing them without a clear convention causes confusion: “why did totalLifetimeValue not change after I edited the order?” Because it is derived — you cannot edit it; you edit the source.
What goes on an object type — and what does not
A common anti-pattern is the junk drawer object type: a User with 80 properties, half of which apply to 2% of users.
Rules of thumb:
Belongs on the object type:
- Properties that are always present for an instance (with a sensible nullability rule).
- Properties that describe the entity itself, not its history.
Probably belongs elsewhere:
- Time series (every login event) → a separate object type (
LoginEvent) with a link toUser. - Variant-specific properties that only apply for some instances → consider splitting into multiple object types or using an interface (covered later).
- Free-form blobs (notes, conversations) → either link to a
Noteobject type or store as attachment properties.
Cardinality rule of thumb
A useful test: if most instances of this object type have most of these properties populated, the model is healthy. If half the properties are NULL for the typical row, you have a modeling problem hiding in nullability.
A complete example — Shipment
Putting it together for a logistics company:
objectType: Shipment
displayName: Shipment
description: >
A package or pallet in transit from an origin to a destination,
associated with exactly one Order and (optionally) one Driver
at a time.
apiName: shipment
primaryKey: shipmentId # SHP-YYYY-MM-DD-NNNNN
titleKey: shipmentId # IDs *are* what users look for here
properties:
- name: shipmentId
type: string
description: Stable identifier from dispatch system
- name: status
type: enum<ShipmentStatus> # created | in_transit | out_for_delivery | delivered | exception
- name: weightKg
type: double
description: Gross weight in kilograms; null if not yet weighed
nullable: true
- name: originHubId
type: string
- name: destinationHubId
type: string
- name: createdAt
type: timestamp
- name: deliveredAt
type: timestamp
nullable: true
- name: dimensions
type: struct<length: double, width: double, height: double, unit: enum<LengthUnit>>
nullable: true
# We will add link types and actions in later lessonsA few things to notice:
- The description constrains scope. “A Shipment” is not just any row in
shipments.csv— it is a specific concept. - Properties have descriptions where the name alone is not enough.
weightKgis explicitly nullable because not every shipment is weighed at creation. We name itweightKg, notweight, so units are part of the type.dimensionsuses a struct rather than three separate properties — they belong together.
Naming conventions that age well
- Object types in PascalCase.
Customer,OrderLine,Shipment. - Properties in camelCase.
firstName,weightKg,deliveredAt. - Booleans read true.
isActivenotactive;hasInsurancenotinsurance. - Units in the name when the type is ambiguous:
weightKg,latencyMs,priceUsd. Atfor timestamps.createdAt,deliveredAt.Dateis too vague.Idfor foreign references to other objects.customerIdnotcustomer.
These look minor on day one. By month six, with thirty object types and four hundred properties, they save you constantly.
Anti-patterns to avoid
- The God object type.
Entitywithtypefield, holding customers, suppliers, partners, and prospects in one bag. Split it. - The reporting object type.
MonthlyRevenueByRegion— that is a query, not an object type. Use functions. - The mirror-of-the-table object type. Copying your raw
userstable verbatim and calling it done. The ontology should interpret the data, not just rename columns. - Property soup. Every column from every source system, glued together with NULLs. Curate.
Key takeaways
- An object type is the typed definition of an entity in your business.
- It needs a stable primary key, a human-friendly title key, and a clear description.
- Distinguish intrinsic (source-of-truth) and derived (computed) properties.
- Naming and scoping discipline early saves enormous pain later.
What’s next
Object types are made of property types. Next lesson: the typing system — strings, integers, geo-points, structs, arrays, attachments — and what good typing buys you.
The nouns are named. Now we type them. 🏷️