Встраивание против ссылок

Главное решение при проектировании документной БД: положить связанные данные внутрь или сослаться на них.

Встраивание (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 — гибкость, переиспользование, нет неограниченного роста.
  • Решает не «как избежать дублирования», а «как эти данные будут читаться».
Проверьте себя
1. В каком случае связанные данные лучше встроить в документ?
AКогда они читаются вместе с родителем и принадлежат только ему
BКогда они переиспользуются множеством других сущностей
CКогда массив может расти без ограничений
DКогда они меняются чаще самого документа
2. Что такое ссылка (reference) в документной модели?
AКопия всего связанного документа
BХранение _id другого документа для связи между коллекциями
CСпециальный индекс
DИмя поля массива
3. Какой принцип помогает выбрать между встраиванием и ссылками?
AВсегда нормализовать, как в SQL
B«Что вместе читается — вместе хранится»
CВсегда ссылаться, чтобы избежать дублирования
DХранить всё в одной коллекции
Поддержать проект