# MongoDB data modeling (/docs/orm/next/data-modeling/mongodb)

Location: ORM > Next > Data modeling > MongoDB data modeling

MongoDB stores data as documents grouped into collections. A document can nest objects and arrays directly, which gives you a real choice for related data: keep it inside the document (embed it) or store it in its own collection and link to it (reference it).

Making that choice well is the core of MongoDB data modeling, and it is what this page focuses on. If you are new to models and keys, start with the [data modeling overview](/orm/next/data-modeling).

Documents, collections, and the _id key [#documents-collections-and-the-_id-key]

A Prisma Next model maps to a collection, and each record is a document. Every document has an `_id` field as its primary key. `ObjectId` is the idiomatic type for it:

```prisma
model User {
  id    ObjectId @id @map("_id")
  name  String
  email String
  @@map("users")
}
```

`@map("_id")` maps the model's `id` field to MongoDB's mandatory `_id` field, and `@@map("users")` names the collection.

Embed or reference [#embed-or-reference]

Two pieces of related data can live together in one document (embedded) or in separate collections linked by an id (referenced).

Embedding nests the data inside its parent: an order and its line items in one document. One read returns everything, and a write updates parent and children together, atomically. The trade-off: embedded data has no independent life. You cannot query it on its own, and it rides along on every read of the parent.

Referencing keeps the data in its own collection and stores the linked document's `_id`. Loading both takes a second lookup, but each record stands on its own: it can be queried, listed, and updated independently, and it is not duplicated when several parents point at it.

A quick way to decide:

| Signal                        | Example                     | Lean toward |
| ----------------------------- | --------------------------- | ----------- |
| Always loaded with the parent | An order's line items       | Embed       |
| Small and bounded in size     | A user's mailing address    | Embed       |
| No meaning outside the parent | A post's SEO metadata       | Embed       |
| Grows without limit           | A user's activity events    | Reference   |
| Queried or updated on its own | Products in a catalog       | Reference   |
| Shared by many parents        | A tag on thousands of posts | Reference   |

A blog post's comments make it concrete. If you always render them with the post and their count stays modest, embed them. If comments can grow into the thousands, or you need "every comment by this author across posts", reference them.

Embedded documents [#embedded-documents]

Describe the shape of embedded data with a `type` block, then use it as a field. An embedded type has no collection of its own; it exists only inside its parent document.

```prisma
type Address {
  street  String
  city    String
  zip     String?
  country String
}

model User {
  id      ObjectId @id @map("_id")
  name    String
  address Address?
  @@map("users")
}
```

A single embedded value models a one-to-one. A list of embedded values models a one-to-many:

```prisma
type CartItem {
  productId String
  name      String
  amount    Int
}

model Cart {
  id    ObjectId   @id @map("_id")
  items CartItem[]
  @@map("carts")
}
```

`Cart.items` lives inside the cart document. There is no separate collection and no join to load the items; every read of a cart returns them.

An embedded `Address` has no `_id` and cannot be queried on its own. It is loaded, updated, and deleted with the document that holds it. That is the right behavior for values that belong to their parent, and the wrong one for records with their own life.

> [!WARNING]
> Embedded arrays are only a good idea while they stay bounded. An array that grows forever slows every read of the parent and pushes the document toward MongoDB's 16 MB limit. When a list has no natural cap, reference it instead.

References across collections [#references-across-collections]

When related records need their own collection, store one document's `_id` on the other and declare the relation with the same `@relation(fields:, references:)` used everywhere in Prisma Next:

```prisma
model User {
  id    ObjectId @id @map("_id")
  name  String
  posts Post[]
  @@map("users")
}

model Post {
  id       ObjectId @id @map("_id")
  title    String
  authorId ObjectId
  author   User     @relation(fields: [authorId], references: [id])
  @@map("posts")
}
```

This is a one-to-many by reference: many posts point at one user, and each post is queryable on its own. When you [include the relation in a query](/orm/next/fundamentals/relations-and-joins), Prisma Next resolves it with a `$lookup` aggregation.

A `$lookup` is a real cost on hot paths, so document models often denormalize: embed a small copy of the fields a read needs (a comment stores its author's name) while the full record stays in its own collection. You trade some duplication, and the work of keeping the copy in sync, for reads that touch one document.

Polymorphic collections [#polymorphic-collections]

MongoDB collections do not enforce a single shape, so it is idiomatic to store documents of different kinds in one collection and tell them apart with a discriminator field.

Prisma Next models this with a base model plus variant models, all sharing one collection:

```prisma
model Post {
  id       ObjectId @id @map("_id")
  title    String
  kind     String

  @@discriminator(kind)
  @@map("posts")
}

model Article {
  summary String

  @@base(Post, "article")
}

model Tutorial {
  difficulty String
  duration   Int

  @@base(Post, "tutorial")
}
```

`@@discriminator(kind)` names the field that records each document's variant. `@@base(Post, "article")` declares `Article` as the `"article"` variant of `Post`. A query for posts returns articles and tutorials together, and you can narrow to one variant in queries.

Use one polymorphic collection when the variants are handled together far more than separately: a notifications collection of email, SMS, and push messages read as one stream. Prefer separate collections when the types rarely appear in the same query or need very different indexes.

Prompt your coding agent [#prompt-your-coding-agent]

Projects scaffolded with `create-prisma@next` install [Prisma Next skills](/ai/tools/skills#available-skills-for-prisma-next) for your coding agent; the `prisma-next-contract` skill covers document modeling. Prompts that map to each section:

* "Using the prisma-next-contract skill, add an embedded Address type to the User model."
* "Cart items are always read with the cart. Model them as an embedded list."
* "Comments can grow without limit. Move them from an embedded array to a referenced collection."
* "Split the posts collection into Article and Tutorial variants with a discriminator."

Next steps [#next-steps]

* [Query documents](/orm/next/fundamentals/reading-data) and [resolve references](/orm/next/fundamentals/relations-and-joins) with `.include(...)`.
* Aggregate and reshape documents with the [pipeline builder](/orm/next/fundamentals/advanced-queries#mongodb-pipeline-builder).
* To model relational data, see the [relational data modeling guide](/orm/next/data-modeling/relational-databases).

## Related pages

- [`Relational data modeling`](https://www.prisma.io/docs/orm/next/data-modeling/relational-databases): Model one-to-one, one-to-many, many-to-many, and polymorphic relations for PostgreSQL and other SQL databases.