---
term: 'Data Modeling (Pointers & Relations)'
seoTitle: 'What is Data Modeling? Relationships, Pointers & Relations'
headline: 'What is Data Modeling?'
slug: data-modeling
category: database
shortDefinition: 'Data modeling is a process of mapping entities, attributes, and relationships before deciding how a database will store them.'
relatedTerms:
  - database-schema
  - relational-queries-document-databases
  - n-plus-one-query-problem
  - auto-generated-database-apis
contrastsWith:
  - database-schema
faq:
  - question: 'What is data modeling?'
    answer: 'The process of mapping an application''s information — its entities, their attributes, and the relationships between them — into a design a database can store. It typically moves through three levels of detail: a conceptual model naming the entities, a logical model adding attributes and keys, and a physical model committing to tables, columns, and indexes in a specific engine.'
  - question: 'What are the three types of data models?'
    answer: 'Conceptual, logical, and physical — the same design at increasing zoom. Conceptual answers "what exists and how does it relate" for business stakeholders. Logical adds attributes, types, and keys while staying technology-agnostic. Physical commits to an actual engine: tables or collections, indexes, constraints. Each level is the previous one plus decisions.'
  - question: 'What is a one-to-many relationship?'
    answer: 'One parent record relating to many children, each child belonging to exactly one parent — a customer and their orders, an author and their books. It is the most common relationship in any schema. Relational databases implement it with a foreign key on the many side; document databases use an embedded array for small bounded sets or a pointer to the parent for everything else.'
  - question: 'What is a many-to-many relationship and why does it need a junction table?'
    answer: 'Both sides relate to many of the other — students and courses, books and genres. A single foreign-key column can only point at one row, so relational databases decompose many-to-many into two one-to-many relationships through a junction table holding both keys (and often relationship attributes like an enrollment date). Document databases skip the junction: arrays of references, or a dedicated relation type, carry the edge directly.'
  - question: 'How do I tell one-to-many from many-to-many?'
    answer: 'Ask the ownership question in both directions. "Can one author have many books?" Yes. "Can one book have many authors?" If no — one-to-many, foreign key on the book. If yes — many-to-many, junction table or relation. Getting this wrong is the classic modeling mistake: a many-to-many modeled as one-to-many either duplicates rows or silently loses edges.'
  - question: 'What is cardinality in an ER diagram?'
    answer: 'How many instances of one entity can relate to instances of another — one-to-one, one-to-many, many-to-many. Diagrams express it in one of three notations: numbers and letters on the connecting lines, crow''s-foot symbols (a bar for one, a three-pronged fork for many), or multiplicity ranges like 1 and asterisk. Same semantics, different drawing conventions.'
  - question: 'How do document databases model relationships without foreign keys?'
    answer: 'With three tools: embedding (nest the related data inside the parent document — right when it is read together, owned, and bounded), pointers or references (store the related document''s ID as a typed field — right for independent lifecycles and high cardinality), and relation objects or ID arrays for many-to-many. The guiding rule flips from normalization to access patterns: data read together should live together.'
  - question: 'What are the most common data modeling mistakes?'
    answer: 'A stable top five: modeling many-to-many as one-to-many; forgetting indexes on foreign-key and pointer fields (every join and lookup pays); unbounded embedded arrays in document schemas; premature denormalization before any query proved slow; and modeling from the entities alone without listing the queries — the access patterns — the model must serve.'
codeLanguages: [javascript, dart, swift, kotlin]
externalAuthorities:
  - name: 'MongoDB — Embedding vs. References'
    url: 'https://www.mongodb.com/docs/manual/data-modeling/concepts/embedding-vs-references/'
  - name: 'Many-to-many data model (Wikipedia)'
    url: 'https://en.wikipedia.org/wiki/Many-to-many_(data_model)'
  - name: 'Entity–relationship model (Wikipedia)'
    url: 'https://en.wikipedia.org/wiki/Entity%E2%80%93relationship_model'
  - name: 'SDK relational data guide'
    url: 'https://docs.parseplatform.org/js/guide/#relational-data'
cta:
  title: 'Model it once, query it everywhere'
  text: 'On Back4app, the model is the backend: classes in the dashboard, Pointers for one-to-many, Relations for many-to-many — and every edge you declare is instantly queryable through auto-generated REST, GraphQL, and SDKs with include() built in.'
  linkText: 'Start building free'
  linkUrl: 'https://www.back4app.com/signup'
author: 'Back4app Engineering'
publishedDate: '2026-07-25'
translationKey: data-modeling
---

**Data modeling is a process of mapping entities, attributes, and relationships before deciding how a database will store them.** The entities are the easy part — every product knows its users, orders, and posts. The craft is in the *relationships*: which records point at which, how many, and where that edge physically lives. Get the edges right and queries write themselves; get them wrong and every feature fights the schema.

## Key takeaways

| Question | Answer |
| --- | --- |
| The three levels | Conceptual (what) → logical (structure) → physical (engine) |
| The three edges | One-to-one · one-to-many · many-to-many |
| Relational tools | Foreign keys; junction tables for many-to-many |
| Document tools | Embedding, Pointers (references), Relations/ID arrays |
| The modern rule | Model for access patterns — data read together lives together |

## The same relationships, both worlds

Relational first — the foreign key carries one-to-many, and many-to-many must decompose through a junction:

```sql
-- 1:N — the foreign key lives on the "many" side
CREATE TABLE books (
  id        bigint PRIMARY KEY,
  title     text   NOT NULL,
  author_id bigint NOT NULL REFERENCES authors(id) ON DELETE CASCADE
);

-- M:N — no single FK can express it; a junction table holds both keys
CREATE TABLE book_genres (
  book_id  bigint REFERENCES books(id),
  genre_id bigint REFERENCES genres(id),
  added_at timestamptz DEFAULT now(),     -- junctions can carry attributes
  PRIMARY KEY (book_id, genre_id)         -- composite key: one edge, once
);
```

Document world, same model: the one-to-many edge is a typed **Pointer**, the many-to-many edge is a **Relation** — no junction table to invent, and the edge is declared in application code:

**JavaScript:**

```javascript
// JavaScript / Node.js — Back4app JS SDK
// One-to-many: a Pointer. Many-to-many: a Relation.
const author = await new Parse.Query('Author').get(authorId);

const book = new Parse.Object('Book');
book.set('title', 'Dune');
book.set('author', author);              // Pointer — typed one-to-many edge
await book.save();

const genres = book.relation('genres');  // Relation — many-to-many, unbounded
genres.add([sciFi, classics]);
await book.save();
```

**Flutter:**

```dart
// Flutter / Dart — Back4app Flutter SDK
// One-to-many: a Pointer. Many-to-many: a Relation.
final book = ParseObject('Book')
  ..set('title', 'Dune')
  ..set('author', author.toPointer()); // Pointer — typed one-to-many edge
await book.save();

book.addRelation('genres', [sciFi, classics]); // Relation — many-to-many
await book.save();
```

**Swift:**

```swift
// iOS / Swift — Back4app Swift SDK
// One-to-many: a Pointer. Many-to-many: a Relation.
var book = Book()
book.title = "Dune"
book.author = try author.toPointer()   // Pointer — typed one-to-many edge
let saved = try await book.save()

let relation = try saved.relation("genres")
try await relation.add([sciFi, classics]).save() // Relation — many-to-many
```

**Kotlin:**

```kotlin
// Android / Kotlin — Back4app Android SDK
// One-to-many: a Pointer. Many-to-many: a Relation.
val book = ParseObject("Book").apply {
  put("title", "Dune")
  put("author", author)                 // Pointer — typed one-to-many edge
}
book.save()

val genres = book.getRelation<ParseObject>("genres") // Relation — many-to-many
genres.add(sciFi)
genres.add(classics)
book.save()
```

## Conceptual vs. logical vs. physical: the three levels

```mermaid
flowchart LR
  accTitle: Conceptual, logical, and physical data models
  accDescr: A conceptual model names entities and relationships for business discussion; the logical model adds attributes, types, and keys independent of technology; the physical model commits to tables or collections, indexes, and constraints in a specific database engine.
  C["Conceptual<br/>Author —writes→ Book<br/>(what exists, for humans)"]
  L["Logical<br/>+ attributes, types, keys<br/>(structure, engine-agnostic)"]
  P["Physical<br/>tables/collections, indexes,<br/>constraints in one engine"]
  C --> L --> P
```

| Dimension | Conceptual | Logical | Physical |
| --- | --- | --- | --- |
| Question answered | What exists, how related | What fields, what keys | How stored, how fast |
| Audience | Everyone | Designers | Engineers + the engine |
| Contains | Entities, relationships | + attributes, types, cardinality | + indexes, constraints, partitions |
| Changes when | The business changes | Requirements refine | The engine or scale changes |

The discipline the levels enforce: don't argue about indexes while the team still disagrees about what an "order" is.

## Every relationship, every implementation

The table the SERP never built — each edge type with its implementation in both worlds:

| Relationship | Example | Relational implementation | Document implementation |
| --- | --- | --- | --- |
| One-to-one | User ↔ profile | FK with UNIQUE, or same row | Embed it — almost always |
| One-to-few | Person → addresses | Child table + FK | Embedded array (bounded) |
| One-to-many | Author → books | FK on the many side | **Pointer** on the many side |
| One-to-enormous | Device → events | FK + partitioning | Pointer on the *child*; never an array |
| Many-to-many | Books ↔ genres | Junction table, composite PK | **Relation** or arrays of pointers |
| Self-referencing | Employee → manager | FK to own table | Pointer to own class |

Cardinality notation, for reading diagrams: crow's-foot draws a bar for "one" and a three-pronged fork for "many"; the [ER tradition](https://en.wikipedia.org/wiki/Entity%E2%80%93relationship_model) writes 1 and N on the lines; UML writes multiplicities like `1` and `*`. Three dialects, one grammar.

## Normalize, denormalize, or embed?

Relational modeling starts from **normalization** — split data so every fact lives once, protecting writes from anomalies. Analytical and document modeling deliberately bend it: **denormalization** duplicates read-hot fields to kill joins, and **embedding** is denormalization's document-native cousin. The [embed-vs-reference checklist](https://www.mongodb.com/docs/manual/data-modeling/concepts/embedding-vs-references/) compresses to three questions: read together? owned by one parent? bounded in size? Three yeses embed; any no references. And over all of it, the modern rule that generalist guides skip: **list the queries first**. A model is correct when the access patterns it must serve are cheap — entities alone can't tell you that.

## Common use cases

- **Designing a new backend.** The classic pass: name entities, draw edges, choose implementations per the table above — before the first line of code.
- **The junction-with-attributes pattern.** Enrollments, memberships, line items — when the *edge itself* has data (date, quantity, role), the junction (or an edge class with two pointers) is a first-class entity.
- **Untangling a grown schema.** Symptoms map to edges: duplicated rows reveal a mis-modeled many-to-many; document bloat reveals an unbounded embed.
- **Migrating between worlds.** Relational→document means re-deciding every FK as embed vs. pointer; the table above is the translation dictionary.
- **AI and analytics feeds.** Warehouses want the edges explicit and stable — modeling debt surfaces the day you try to export.

## How should you model each edge? A decision matrix

| Choose… | When… | Watch out for… |
| --- | --- | --- |
| Embed | Read together, owned, bounded | Growth: today's "few" becomes tomorrow's thousands |
| Pointer / FK | Independent lifecycle, high cardinality | Index it — every lookup and include pays |
| Relation / junction | Many-to-many, or the edge carries data | Query direction: know which side you'll ask from |
| Duplicate (denormalize) | A read-hot field crossing an edge | Update fan-out — duplication is a debt with interest |

## Limitations and trade-offs

- **Models freeze assumptions.** Cardinality decisions ("a user has one address") become schema; cheap to change on paper, expensive after launch — model for tomorrow's cardinality.
- **Both worlds punish the unindexed edge.** FK columns and pointer fields are join paths; forgetting their indexes is the most common silent performance bug.
- **Embedding trades integrity for locality.** No engine enforces that an embedded copy stays consistent with its source — that's your update logic now.
- **Junctions multiply joins; relations hide them.** Every many-to-many read crosses the edge store — budget the query path, whichever world you're in.
- **Access-pattern modeling has a cost too.** Optimizing for today's queries can wed the schema to today's product; keep the conceptual model around as the neutral ground truth.

## Data modeling on Back4app

Back4app is an open-source Backend-as-a-Service (BaaS) platform that combines a managed database, auto-generated REST and GraphQL APIs, authentication, file storage, and Cloud Code serverless functions. Its modeling vocabulary is the document column of the tables above, made first-class: **[Pointers](https://docs.parseplatform.org/js/guide/#relational-data)** are typed one-to-many edges, **Relations** carry many-to-many without a hand-built junction, and arrays cover the bounded-few. Every edge you declare is immediately traversable — `include()` walks pointers in one request, GraphQL nests relations in one query — and the schema stays visible and editable in the dashboard, so the physical model never drifts out of sight of the team that designed the conceptual one.
