Estágio 02 · 02-12
LockedMongo virou meme, "use Postgres" é o conselho default e correto pra maioria dos casos. Mas Mongo continua relevante: workloads com schema flexível (eventos heterogêneos, ingestão de dados de múltiplas fontes), agregações complexas em coleções grandes, modelos hierárquicos onde JOIN seria custoso. Saber Mongo bem te dá clareza sobre quando relacional não é o ajuste certo e como pensar em modelagem orientada a documento sem cair nos antipadrões.
Este módulo é Mongo de fato: storage engine (WiredTiger), modelo BSON, schema de fato implícito, índices (single, compound, text, geo, wildcard), aggregation pipeline, transações multi-documento, replica sets, sharding, e os trade-offs reais com Postgres + jsonb. Não é "Mongo é melhor que SQL", é entender onde encaixa.
$jsonSchema validators).Document IDs:
_id é PK obrigatório.ObjectId (12 bytes: timestamp 4B + machine/pid 5B + counter 3B). Quase sortable por tempo.Mongo permite docs heterogêneos. Em prática, toda coleção tem schema implícito que sua app espera. Sem disciplina, vira bagunça.
Validação:
db.createCollection('orders', { validator: { $jsonSchema: { ... } } }).error (default) ou warn.Em projeto sério: sempre defina validator. Combinado com Mongoose/Zod no app, garante shape.
Default desde 3.2. B+Tree, compressão por block, MVCC.
Tipos:
{ status: 1 }.{ tenantId: 1, status: 1, createdAt: -1 }. Order matters (mesma regra de B-Tree).{ "$**": 1 } indexa todos os fields (cuidado, custoso).{ partialFilterExpression: { status: 'active' } }.{ expireAfterSeconds: N } em campo date, Mongo deleta docs após.Sempre crie index em background (default em versões modernas, não bloqueia).
db.orders.find(
{ tenantId: t, status: { $in: ['pending', 'paid'] } },
{ _id: 1, total: 1, customerName: 1 }
).sort({ createdAt: -1 }).limit(20);
Operators:
$eq, $gt, $gte, $lt, $lte, $ne, $in, $nin.$and, $or, $not, $nor.$exists, $type.$all, $elemMatch, $size.$regex, $expr, $jsonSchema.$expr permite usar aggregation expressions em queries.
$set, $unset, $inc, $mul.$push, $pull, $addToSet em arrays.$rename.$ (matched element), $[] (todos), $[<filter>] (filtered).arrayFilters define filtros por nome.db.orders.updateOne(
{ _id, "items.sku": "X" },
{ $set: { "items.$.quantity": 5 } }
);
A feature flagship do Mongo. Sequência de stages que transformam documentos:
db.orders.aggregate([
{ $match: { tenantId: t, createdAt: { $gte: yesterday } } },
{ $group: { _id: "$status", count: { $sum: 1 }, total: { $sum: "$total" } } },
{ $sort: { count: -1 } }
]);
Stages comuns:
$match: filter (use cedo, antes de transformações pesadas).$project / $set / $unset: shape.$group: agrupamento + accumulators.$sort, $limit, $skip.$lookup: JOIN-like (com outra collection). Custoso.$unwind: desempacota array em N docs.$facet: pipelines paralelos no mesmo input.$bucket / $bucketAuto: histogramas.$graphLookup: traversal recursivo.$merge / $out: persistir resultado.$densify, $fill: time-series helpers.$lookup é JOIN, mas executar em coleções grandes sem índice é desastroso. Em queries críticas, denormalize ou faça JOIN no app.
Decisão central. Não há "regras" universais; há trade-offs:
Embed (nested doc):
Reference:
customerId, faz query separada (ou $lookup).Heurísticas:
Pre-4.0: atomicidade só no nível de documento. 4.0+: multi-document transactions em replica set. 4.2+: transactions em sharded cluster.
const session = client.startSession();
try {
await session.withTransaction(async () => {
await orders.updateOne({ _id }, { $set: { status: 'paid' } }, { session });
await events.insertOne({ orderId: _id, type: 'paid' }, { session });
});
} finally {
await session.endSession();
}
Custo: txns multi-doc são mais lentas (locks, MVCC overhead). Mongo recomenda design que minimize txns multi-doc, embed quando possível.
Cluster de N nós: 1 primary + replicas + (opcional) arbiter.
oplog é capped collection no primary; replicas tail.Read preference (cliente escolhe):
primary (default).primaryPreferred.secondary / secondaryPreferred.nearest.Write concern:
{ w: 1 }: ack do primary (default em alguns drivers).{ w: 'majority' }: ack de majority. Mais durável, mais latente.{ j: true }: journal. Garante durabilidade local antes de ack.{ wtimeout: ms }: timeout para w concern.Read concern:
local: padrão.majority: dados commitados em majority.linearizable: stronger, mais lento.Em prod: writes com majority, reads default local (ou majority em workloads onde stale leitura é problema).
Mongo faz sharding nativo, ao contrário de Postgres (Postgres precisa de Citus/Foreign Tables).
Componentes:
mongos: router. Cliente conecta nele.Shard key: campo (ou compound) que define como docs se distribuem. Decisões:
Escolha errada: re-shard é trabalhoso. Em projetos < 1 TB, não shardar, replica set comum sustenta.
Atlas Search e Vector Search agregam features que historicamente exigiam Elasticsearch / pgvector adicionais.
Node:
mongodb (low-level).Em projetos onde tipagem importa: driver direto + Zod, ou Mongoose com Schema cuidadosamente tipados, ou wrappers como mongo-models ou Drizzle (que adicionou suporte a Mongo). Não ache que ODM substitui domain layer.
Para dúvida típica em backend de aplicação CRUD-ish: comece com Postgres. Adicione Mongo só se você bate em uma necessidade clara.
$lookup, normalização excessiva, transações multi-doc onipresentes. Você está usando Mongo errado.mongodump / mongorestore: dump lógico. Para datasets pequenos.mongosh é o shell oficial novo.compact (libera espaço pós-DELETEs); em replica sets, fazer um nó por vez.Você precisa, sem consultar:
explain('executionStats'), quais campos olhar.Adicionar MongoDB pra eventos de Logística: não substitui Postgres, complementa.
mongodb em Node (Fastify do 02-08).external_events armazena tudo. Cada doc tem source, receivedAt, tenantId, payload (livre), processedAt, result.external_events tem $jsonSchema exigindo source, tenantId, receivedAt, payload.payload pode ser qualquer shape (intencional).{ tenantId: 1, source: 1, receivedAt: -1 }, listagem por tenant + source.{ receivedAt: 1 }, expireAfterSeconds: 7776000).payload.tags (quando existe).POST /webhooks/:source, recebe e armazena evento.GET /events?tenant=X&source=Y&from=&to=, paginated listing.GET /events/stats, aggregation:
processedAt: null, processa (lógica fictícia: parse, atualizar order em Postgres se aplicável), marca processedAt e result.processedAt.$match → $group → $facet → $project que produz dashboard com 3 métricas em 1 query.$lookup na aggregation principal, denormalize se precisar (anote a decisão).explain mostrando uso de index.payload.external_events em real-time (via Mongo CDC).Destrava
02-12 é prereq dos seguintes módulos: