Проектируем схему: блог и магазин

Собираем всё вместе: проектируем реальные схемы, осознанно выбирая встраивание и ссылки.

Хорошая схема MongoDB начинается не с сущностей, а с вопроса: какие запросы будут чаще всего?

Кейс 1. Блог

Сущности: пользователи, посты, комментарии, теги. Подумаем о запросах: чаще всего открывают пост и сразу видят его текст, автора и комментарии. Значит:

  • Комментарии встраиваем в пост — их немного и читаются они всегда вместе с постом.
  • Автора — ссылкой (authorId), но имя денормализуем в пост, чтобы лента не делала $lookup.
  • Теги — массив строк прямо в посте, по нему легко искать и индексировать.
{
  "_id": "p1",
  "title": "Знакомство с MongoDB",
  "body": "...",
  "authorId": "u1",
  "authorName": "Анна",
  "tags": ["mongodb", "nosql"],
  "comments": [
    { "author": "Борис", "text": "Спасибо!", "at": "2026-01-10" }
  ]
}

Лента постов по тегу — один индексированный запрос, без объединений:

db.posts.createIndex({ tags: 1 })
db.posts.find({ tags: "mongodb" }).sort({ _id: -1 })

Кейс 2. Интернет-магазин

Сущности: товары, пользователи, заказы. Запросы: каталог товаров, история заказов пользователя, карточка заказа. Решения:

  • Товары — отдельная коллекция: они переиспользуются во многих заказах, у них своя жизнь (цена, остаток).
  • Заказ ссылается на пользователя (userId) — заказов у пользователя может быть сколько угодно.
  • Позиции заказа встраиваем в заказ, причём с копией цены и названия на момент покупки — это денормализация ради истории: даже если цена товара потом изменится, в заказе останется та, по которой купили.
{
  "_id": "ord-100",
  "userId": "u1",
  "createdAt": "2026-02-01",
  "status": "paid",
  "items": [
    { "sku": "SKU-1", "name": "Кофе", "price": 450, "qty": 2 },
    { "sku": "SKU-2", "name": "Чай",  "price": 300, "qty": 1 }
  ],
  "total": 1200
}

История заказов пользователя — обычный запрос со ссылкой и индексом по userId:

db.orders.createIndex({ userId: 1, createdAt: -1 })
db.orders.find({ userId: "u1" }).sort({ createdAt: -1 })

Логика принятия решений

Заметьте закономерность. Встраивали то, что принадлежит родителю и читается вместе с ним (комментарии, позиции заказа). Ссылались на то, что живёт самостоятельно и переиспользуется (пользователи, товары). Денормализовали стабильные данные ради скорости частых запросов (имя автора, цена на момент покупки). Это и есть документное проектирование — от запросов к структуре.

Итог

  • Проектирование начинают с частых запросов, а не со списка сущностей.
  • Встраивают принадлежащее и совместно читаемое; ссылаются на самостоятельное и переиспользуемое.
  • Денормализуют стабильные данные (имя автора, цену на момент покупки) и индексируют поля частых фильтров.
Проверьте себя
1. Почему комментарии к посту в блоге удобно встроить в документ поста?
AИх всегда очень много
BОни принадлежат посту и читаются вместе с ним
CТак требует MongoDB
DЧтобы их нельзя было удалить
2. Почему в заказе магазина хранят копию цены и названия товара?
AИз-за ошибки проектирования
BЧтобы сохранить данные на момент покупки, даже если цена товара потом изменится
CЧтобы сэкономить место
DЭто запрещено в MongoDB
3. С чего начинают проектирование схемы в MongoDB?
AСо списка всех сущностей и их нормализации
BС анализа того, какие запросы будут самыми частыми
CС создания индексов
DС выбора имени базы
Поддержать проект