Встраивание против ссылок
Главное решение при проектировании документной БД: положить связанные данные внутрь или сослаться на них.
Встраивание (embedding) — хранение связанных данных внутри одного документа; ссылки (referencing) — хранение в разных документах, связанных по идентификатору.
Смена мышления: запросы определяют структуру
В реляционной БД вы нормализуете данные — раскладываете по таблицам, чтобы избежать дублирования, а собираете обратно через JOIN. В MongoDB логика обратная: вы смотрите, как данные будут читаться, и складываете их так, чтобы типичный запрос обходился без склейки. Поэтому первое проектное решение — встраивать или ссылаться.
Встраивание: всё в одном документе
У заказа есть адрес доставки и список позиций. Они не имеют смысла отдельно от заказа и всегда читаются вместе с ним — значит, их естественно вложить:
{
"_id": "o1",
"customer": "Анна",
"shipping": { "city": "Казань", "zip": "420000" },
"items": [
{ "sku": "SKU-1", "qty": 2, "price": 450 },
{ "sku": "SKU-2", "qty": 1, "price": 900 }
]
}Весь заказ читается одним запросом, без обращений к другим коллекциям. Это быстро и атомарно: обновление документа целиком — одна операция.
Ссылки: связь по идентификатору
Иногда вкладывать нельзя или невыгодно. Тогда в документе хранят ссылку — _id другого документа, как внешний ключ. Заказ ссылается на пользователя:
{
"_id": "o1",
"userId": "u1",
"total": 1800
}Данные пользователя лежат отдельно, в коллекции users. Чтобы получить их вместе с заказом, делают второй запрос или используют $lookup в агрегации (аналог JOIN, о нём в следующем разделе).
Как выбрать
| Встраивать, когда… | Ссылаться, когда… |
| данные читаются вместе с родителем | сущность нужна сама по себе и переиспользуется |
| связь «принадлежит» (адрес заказа) | связь «многие-ко-многим» |
| вложенных данных немного и они ограничены | массив может расти безгранично |
| важна атомарность обновления | данные часто меняются и дублирование вредно |
Правило большого пальца
«Что вместе читается — вместе хранится». Если, открывая сущность, вы почти всегда показываете и связанные данные — встраивайте. Если связанные данные крупные, переиспользуются или растут без предела — выносите в отдельную коллекцию и ссылайтесь. Главный страх реляционной модели, дублирование, в документной не табу: иногда продублировать поле выгоднее, чем каждый раз делать JOIN.
Итог
- Встраивание держит связанные данные в одном документе — быстрый чтение и атомарность.
- Ссылки связывают документы по
_id— гибкость, переиспользование, нет неограниченного роста. - Решает не «как избежать дублирования», а «как эти данные будут читаться».