Лучший способ реализовать структуру в MongoDB — начать не с коллекций, а с вопросов: какие данные читаются вместе, какие часто обновляются, что может бесконечно расти и какие фильтры будут в интерфейсе. MongoDB хорошо работает с вложенными документами, но не все нужно вкладывать в один огромный документ
Простое правило:
- читается вместе и не растет бесконечно — можно вложить;
- часто обновляется отдельно — лучше отдельная коллекция;
- список может стать большим — лучше отдельная коллекция;
- нужен быстрый фильтр — заранее думайте об индексе.
- Пример: пользователь и адреса
- Пример: пользователь и заказы
- Вложение или ссылка
- Как проверить структуру
- Антипример: все в одном документе
- Денормализация допустима
- Частые ошибки
- Копируют SQL-схему один в один
- Вкладывают бесконечные массивы
- Не думают о запросах
- Не создают индексы
- Боятся любых повторов данных
- Что почитать дальше по MongoDB
Пример: пользователь и адреса
Если у пользователя 1-3 адреса доставки, их можно хранить внутри документа:
{
name: "Dinar",
addresses: [
{ city: "Sochi", street: "Lenina", isDefault: true }
]
}
Так профиль пользователя читается одним запросом
Пример: пользователь и заказы
Заказы лучше хранить отдельно:
{
_id: ObjectId("..."),
userId: ObjectId("..."),
status: "paid",
total: 4900,
createdAt: new Date()
}
Заказов может быть много, у них свой жизненный цикл, статусы, оплата и доставка. Если положить все заказы в пользователя, документ быстро станет неудобным
Вложение или ссылка
Вложение удобно, когда данные принадлежат документу и почти всегда нужны вместе. Ссылка удобна, когда объект живет отдельно: пользователь, заказ, товар, платеж, сообщение
Для ссылок храните ObjectId:
{
userId: ObjectId("665f1c1a7b8c2e0012a4d111")
}
И добавляйте индекс:
db.orders.createIndex({ userId: 1, createdAt: -1 })
Как проверить структуру
Напишите 3-5 главных запросов приложения. Например:
db.orders.find({ userId }).sort({ createdAt: -1 }).limit(20)
db.products.find({ active: true, category: "hoodies" })
db.messages.find({ conversationId }).sort({ createdAt: -1 }).limit(50)
Если эти запросы выглядят просто и их можно поддержать индексами, структура выбрана нормально. Если каждый экран требует сложной агрегации и ручного склеивания десятков коллекций, модель стоит пересмотреть
Антипример: все в одном документе
Плохой вариант для пользователя:
{
name: "Dinar",
orders: [
{ number: 1, items: [...], messages: [...], payments: [...] }
]
}
Сначала кажется удобно: все лежит внутри пользователя. Потом заказы растут, платежи обновляются отдельно, сообщения добавляются постоянно, а документ становится тяжелым. Лучше разделить:
users— профиль пользователя;orders— заказы сuserId;payments— платежи сorderId;messages— сообщения сconversationId.
Так каждый тип данных получает свой жизненный цикл
Денормализация допустима
MongoDB не запрещает хранить небольшие копии данных. Например, в заказе можно хранить userName и userEmailSnapshot, чтобы старый заказ не менялся при изменении профиля пользователя. Главное — понимать, что это снимок данных, а не единственный источник правды
Частые ошибки
Копируют SQL-схему один в один
MongoDB не обязана повторять реляционную схему. Иногда вложенный объект проще и быстрее
Вкладывают бесконечные массивы
Комментарии, сообщения и события лучше хранить отдельными документами
Не думают о запросах
Структура должна помогать реальным чтениям и обновлениям
Не создают индексы
Даже хорошая структура будет медленной без индексов под частые фильтры
Боятся любых повторов данных
Небольшая денормализация часто нормальна, если она упрощает чтение и не создает путаницу в обновлениях
Что почитать дальше по MongoDB
Если нужен общий маршрут по теме, откройте рубрику MongoDB. Для соседних задач пригодятся эти разборы:
- Как лучше искать переписку в MongoDB перед созданием
- Как лучше показывать MongoDB ObjectId в адрес
- Discord bot на Python и MongoDB: как задать проверку
- Failed to start MongoDB database server: что проверить



