Every application has relationships: users have orders, posts have comments, students attend courses. In MongoDB you model each relationship with one of two tools, embedding or referencing, and choosing correctly is the heart of document data modeling. This CodeYourCraft lesson gives you the decision framework, worked examples of each pattern, and the hybrid patterns professionals actually use.
Embedding stores related data inside the parent document. Referencing stores related data in its own collection and links it by _id:
// Embedded: address lives inside the customer
{ _id: 1, name: 'Meera', address: { city: 'Pune', pin: '411001' } }
// Referenced: orders point at their customer
{ _id: 501, customerId: 1, total: 4499, items: [...] }Embedding gives you everything in one read and atomic single-document updates. Referencing keeps documents small, avoids duplication, and lets the related data grow without limit.
A user and their profile settings, an order and its shipping address: these belong together. The same goes for one-to-few relationships, where the few side is small and bounded:
{
_id: ObjectId('...'),
name: 'Meera Kulkarni',
addresses: [
{ label: 'home', city: 'Pune' },
{ label: 'office', city: 'Mumbai' }
]
}Nobody has ten thousand addresses, so the array stays bounded, and the profile page needs exactly one query.
When the many side is large or unbounded, such as a customer with years of orders, embed nothing and store the reference on the child:
// orders collection
{ _id: ObjectId('...'), customerId: ObjectId('...'), total: 8999, createdAt: ISODate('...') }
// All orders for a customer (index customerId!)
db.orders.find({ customerId: someId }).sort({ createdAt: -1 })The parent stays small forever, and the child collection can be queried, indexed, and aged out independently.
Students and courses, posts and tags: store an array of _ids on one side, usually the side you query from most often:
// students
{ _id: 's1', name: 'Rohan', courseIds: ['c101', 'c204'] }
// Courses for a student
db.courses.find({ _id: { $in: student.courseIds } })
// Students in a course (multikey index on courseIds makes this fast)
db.students.find({ courseIds: 'c101' })Unlike SQL, you rarely need a junction collection unless the relationship itself carries data, such as an enrollment grade.
Extended reference: store the reference plus a small copy of the fields you always display, such as customerId along with customerName on each order. Reads skip the join; renames require a background sync.
Subset pattern: embed the newest or most relevant few items and keep the full history referenced, like the ten most recent reviews on a product document with the rest in a reviews collection. Hot reads stay single-document while data stays bounded.
Ask these questions in order: Is the related data always read with the parent? How many related items can exist at maximum, and is that bounded? How often does the related data change, and is it updated independently? Do you need to query the related data on its own? Frequent joint reads with a bounded size favor embedding; independent growth, independent queries, or frequent independent updates favor referencing.
Next we leave the shell and connect MongoDB to a real application using Node.js and Mongoose.