MongoDB e o Mundo NoSQL em C#

MongoDB e o Mundo NoSQL em C#

A outra filosofia de organizar dados: bancos NoSQL orientados a documentos. A aula instala o MongoDB, adiciona o driver ao projeto C#, conecta, insere e busca documentos — e oferece o critério para escolher entre o modelo relacional e o de documentos conforme o problema.
Linguagem C#

• • 12 min de leitura

Nas duas últimas aulas, trabalhamos com bancos de dados relacionais — PostgreSQL e MySQL —, que organizam a informação em tabelas de linhas e colunas. Agora ampliamos seu repertório conhecendo uma filosofia de armazenamento bem diferente: o NoSQL, e especificamente o MongoDB, o banco de dados de documentos mais popular do mundo. Em vez de tabelas rígidas, o MongoDB guarda a informação em documentos flexíveis, que se parecem muito com os objetos do seu programa. Não se trata de um banco "melhor" ou "pior" que os relacionais — trata-se de um banco diferente, adequado a problemas diferentes. Entender quando usar cada um, e como acessar o MongoDB a partir do C#, completa seu repertório de persistência e, mais importante, lhe dá o discernimento de escolher a ferramenta certa para cada situação — uma marca do programador maduro.

Duas filosofias de organizar dados

A diferença fundamental entre os dois mundos está em como os dados são estruturados. No modelo relacional, você separa a informação em tabelas e as conecta por relacionamentos — os itens de um pedido ficariam numa tabela, o pedido em si em outra, ligados por um identificador. A estrutura é rígida: cada tabela tem colunas fixas, e todos os registros seguem o mesmo formato. Isso é excelente para dados altamente estruturados e interligados, com forte garantia de consistência.

No modelo de documentos, você guarda cada entidade como um documento autocontido, que se parece com um objeto — com suas propriedades, inclusive aninhadas. Um pedido, com todos os seus itens dentro dele, seria um único documento. Veja como um documento se pareceria, num formato próximo ao JSON que já mencionamos:

{
  "cliente": "Ana",
  "itens": [
    { "produto": "Café", "quantidade": 2 },
    { "produto": "Pão", "quantidade": 5 }
  ],
  "total": 45.50
}

Repare: os itens estão dentro do documento do pedido, aninhados, não numa tabela separada. E a estrutura é flexível — outro pedido poderia ter campos diferentes, sem precisar alterar uma "definição de tabela" antes. Essa flexibilidade é a grande força e, ao mesmo tempo, o grande cuidado do NoSQL: liberdade para modelar dados variáveis e evoluir rápido, mas sem a rede de segurança da estrutura rígida que impede inconsistências. Note como um documento se assemelha a um objeto C#: uma "coisa" com suas propriedades reunidas numa unidade. Essa semelhança torna o MongoDB, em certos aspectos, muito natural para quem pensa em objetos.

Quando usar cada um

Aqui está o discernimento que esta aula quer lhe dar, pois é a parte mais valiosa. Prefira um banco relacional (PostgreSQL, MySQL) quando os dados são bem estruturados e fortemente interligados, quando a integridade é crítica (um sistema bancário, por exemplo, onde cada centavo precisa ser exato e consistente), e quando você fará consultas complexas cruzando várias entidades. Prefira um banco de documentos (MongoDB) quando os dados são naturalmente aninhados ou semiestruturados, quando o formato varia entre registros ou evolui com frequência (um catálogo de produtos onde cada categoria tem atributos diferentes, ou registros de eventos cujo formato muda com o tempo), e quando você prioriza flexibilidade sobre garantias rígidas de estrutura.

A regra prática: dados tabulares e interligados, com integridade crítica → relacional; dados em forma de documento, flexíveis e variáveis → documentos (NoSQL). E uma verdade importante: muitos sistemas reais usam os dois, cada um onde brilha — um banco relacional para os dados financeiros e um de documentos para o catálogo, por exemplo. Não existe um vencedor universal; existe a adequação ao problema. Saber que os dois modelos existem, e reconhecer qual serve melhor a cada caso, já o coloca à frente de muitos programadores que conhecem apenas um.

Passo 1: instalar o MongoDB

A instalação do MongoDB no Linux envolve alguns passos a mais (adicionar o repositório oficial), então, para aprender, o caminho mais simples costuma ser usar o MongoDB Atlas — um serviço gratuito na nuvem onde você cria um banco em minutos e recebe uma string de conexão pronta, sem instalar nada localmente. Para quem prefere instalar na própria máquina, o site do MongoDB (mongodb.com) traz o passo a passo por sistema operacional, incluindo o WSL. Uma vez com o servidor acessível (local ou na nuvem), o que muda para o nosso código é apenas a string de conexão. Para os exemplos, assumirei que você tem uma string de conexão — algo como mongodb://localhost:27017 para um servidor local, ou a fornecida pelo Atlas para a nuvem.

Passo 2: o driver do MongoDB

Uma diferença importante de imediato: para o MongoDB, não usamos o Entity Framework Core (que é voltado a bancos relacionais). Usamos o driver oficial do MongoDB para C# — uma biblioteca que fornece a ponte entre o seu código e o banco de documentos. Adicionamo-lo com o NuGet:

dotnet add package MongoDB.Driver

Passo 3: a classe e a conexão

A classe que representa nossos dados é uma classe C# comum, com pequenas anotações que dizem ao driver como mapear cada documento. O identificador de cada documento, no MongoDB, chama-se _id, e o marcamos com um atributo:

using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;

class Produto
{
    [BsonId]                                    // marca esta property como o _id do documento
    [BsonRepresentation(BsonType.ObjectId)]     // formato do identificador do MongoDB
    public string? Id { get; set; }

    public string Nome { get; set; } = "";
    public decimal Preco { get; set; }
}

Conectar envolve três conceitos: um cliente (a conexão com o servidor), um banco de dados, e uma coleção — que é, no MongoDB, o equivalente a uma tabela, mas guardando documentos em vez de linhas:

using MongoDB.Driver;

var cliente = new MongoClient("mongodb://localhost:27017");   // conecta ao servidor
var banco = cliente.GetDatabase("loja_db");                  // escolhe o banco
var colecao = banco.GetCollection<Produto>("produtos");     // a coleção de produtos

Note uma diferença marcante em relação ao mundo relacional: não há migrações nem criação prévia de tabela. O MongoDB cria o banco e a coleção automaticamente na primeira vez que você escreve neles. Essa é a flexibilidade do NoSQL em ação — você não precisa definir a estrutura de antemão; ela simplesmente se forma conforme você adiciona documentos.

Passo 4: inserir e buscar

Inserir um documento é direto e assíncrono (acesso a banco é operação de espera, como sempre):

await colecao.InsertOneAsync(
    new Produto { Nome = "Caderno", Preco = 12.5m });

E para buscar? Aqui há uma ponte agradável com o que você já sabe: o driver do MongoDB também suporta LINQ. Você pode consultar com a mesma sintaxe que já domina, e o driver a traduz para as consultas nativas do MongoDB:

// LINQ sobre a coleção — o driver traduz para a linguagem de consulta do MongoDB.
var baratos = await colecao
    .AsQueryable()
    .Where(p => p.Preco < 10)
    .OrderBy(p => p.Nome)
    .ToListAsync();

foreach (var p in baratos)
    Console.WriteLine($"{p.Id}: {p.Nome}");

Repare como o Where e o OrderBy são idênticos aos que você usou com o EF Core e com listas — o LINQ, mais uma vez, unifica o acesso a fontes de dados muito diferentes. O padrão geral do MongoDB é o mesmo dos outros bancos — inserir, buscar, atualizar, remover —, com uma biblioteca diferente por baixo, adequada ao modelo de documentos. Atualizar e remover têm seus próprios métodos (ReplaceOneAsync, DeleteOneAsync), sempre assíncronos, seguindo a mesma lógica.

Fechando a persistência

Com esta aula, você domina as três formas mais importantes de guardar dados em C#: dois bancos relacionais (PostgreSQL e MySQL) via Entity Framework Core, e um banco de documentos (MongoDB) via driver nativo. Mais do que a mecânica de cada um, você ganhou o discernimento — sabe que o relacional brilha em dados estruturados e interligados com integridade crítica, e que o NoSQL brilha em dados flexíveis, aninhados e variáveis. Essa capacidade de escolher a ferramenta certa, e de acessá-la com o LINQ que já conhece, é o que diferencia quem apenas "salva dados" de quem projeta a persistência de um sistema. E note, mais uma vez, como tudo se apoiou no que você já sabia: as classes da fase de objetos, o LINQ da fase de coleções, a assincronia da fase de concorrência, o NuGet das ferramentas. Persistência não foi um assunto totalmente novo — foi a aplicação do que você domina.

O panorama da persistência ficou completo com a outra grande filosofia de organizar dados. Bancos NoSQL como o MongoDB guardam documentos flexíveis em vez de linhas em tabelas de estrutura rígida, o que favorece dados heterogêneos e esquemas que mudam com frequência.

A escolha entre os dois modelos deixou de ser questão de moda e passou a ter critério: relacional quando os dados são estruturados e as relações importam, com garantias fortes de integridade; documento quando a flexibilidade e a escala horizontal pesam mais. Saber que existem duas famílias, e por que cada uma existe, é mais valioso do que dominar a sintaxe de qualquer uma delas.

Fontes e leituras recomendadas

Exercícios

Exercício 1

Explique, com suas palavras, a diferença fundamental entre como um banco relacional e um banco de documentos organizam a informação. Use o exemplo de um pedido com vários itens para ilustrar a diferença.

Ver resposta

✓ Resposta: Num banco relacional, a informação é separada em tabelas de linhas e colunas, conectadas por relacionamentos: um pedido com vários itens ficaria em pelo menos duas tabelas — uma de pedidos e uma de itens —, ligadas por um identificador, pois uma tabela não guarda naturalmente uma lista aninhada dentro de uma célula. Num banco de documentos, a mesma informação é guardada como um documento autocontido: o pedido, com todos os seus itens aninhados dentro dele, forma um único documento, lido e gravado de uma vez. A diferença é que o relacional distribui os dados relacionados em tabelas separadas e rígidas, enquanto o de documentos os reúne numa estrutura flexível e aninhada, próxima de um objeto.

Exercício 2

Configure o acesso a um MongoDB (via Atlas na nuvem ou instalado localmente), obtenha sua string de conexão, adicione o MongoDB.Driver a um projeto, e conecte-se a uma coleção. Insira dois produtos e confirme (pelo Atlas ou por uma busca no código) que foram criados. Note que você não precisou criar a coleção manualmente.

Ver resposta

✓ Resposta: Após configurar o acesso (obtendo a string de conexão do Atlas ou de uma instalação local), adicionar o MongoDB.Driver, e conectar com MongoClient, GetDatabase e GetCollection, insere-se com InsertOneAsync. Verificando (no Atlas ou por uma busca no código), os dois produtos aparecem. A coleção e o banco foram criados automaticamente na primeira inserção, sem necessidade de defini-los antes — o que ilustra a flexibilidade de estrutura do NoSQL.

Exercício 3

Escreva uma consulta que use LINQ (AsQueryable().Where(...).OrderBy(...)) para buscar os produtos com preço abaixo de um valor, e exiba-os. Compare a experiência de escrever essa consulta com a que você escreveu para o EF Core na Extensão #02.

Ver resposta

✓ Resposta: Exemplo:

var baratos = await colecao.AsQueryable()
    .Where(p => p.Preco < 10).OrderBy(p => p.Nome).ToListAsync();
foreach (var p in baratos) Console.WriteLine($"{p.Nome}: R$ {p.Preco}");

A experiência de escrita é praticamente idêntica à do EF Core na Extensão #02 — o mesmo Where e OrderBy da fase de coleções —, o que mostra como o LINQ unifica o acesso a fontes de dados muito diferentes. A diferença está por baixo: no EF Core, o LINQ é traduzido para a linguagem de bancos relacionais; no MongoDB, é traduzido para a linguagem de consulta de documentos do Mongo. Para o programador, o estilo permanece o mesmo.

Exercício 4

Para cada sistema a seguir, escolha entre banco relacional e MongoDB e justifique: (a) um sistema bancário com contas e transações, onde a integridade é crítica; (b) um catálogo de produtos de e-commerce em que cada categoria tem atributos completamente diferentes (roupas têm tamanho, eletrônicos têm voltagem); (c) um registro de logs de eventos de uma aplicação, cujo formato muda ao longo do tempo.

Ver resposta

✓ Resposta: (a) Sistema bancário: relacional — exige integridade rígida (transações consistentes, saldos exatos) e dados fortemente estruturados e interligados (contas, transações), que são os pontos fortes do relacional. (b) Catálogo com atributos diferentes por categoria: MongoDB — o esquema flexível de documentos permite que cada produto tenha seus próprios campos (tamanho para roupas, voltagem para eletrônicos) sem forçar uma estrutura de colunas fixa, o que no relacional geraria tabelas com muitas colunas vazias ou modelagens complicadas. (c) Logs de formato variável: MongoDB — o esquema flexível absorve variações de estrutura sem exigir alterações prévias, e o modelo de documentos lida bem com grandes volumes de registros de formato mutável ao longo do tempo.

Exercício 5

Sem código: a aula afirma que "muitos sistemas reais usam os dois" tipos de banco. Explique por que isso faz sentido, dando um exemplo de um sistema que poderia se beneficiar de usar um banco relacional para uma parte dos dados e um banco de documentos para outra.

Ver resposta

✓ Resposta: Faz sentido usar os dois porque diferentes partes de um mesmo sistema podem ter necessidades diferentes de armazenamento, e cada tipo de banco brilha num tipo de dado. Um exemplo: um site de comércio eletrônico poderia usar um banco relacional para os dados financeiros e de pedidos — onde a integridade e a consistência são críticas (pagamentos, estoque, contas) e os dados são bem estruturados e interligados — e um banco de documentos (MongoDB) para o catálogo de produtos — onde cada categoria tem atributos variados e flexíveis, e o formato evolui com frequência. Usar cada banco onde ele é mais adequado aproveita os pontos fortes de ambos, em vez de forçar todos os dados num único modelo que serviria mal a uma parte deles. Reconhecer essa possibilidade é parte de projetar a persistência de um sistema com maturidade.

Comentários

Mais em Linguagem C#

Tipos Primitivos: As Naturezas dos Valores
Tipos Primitivos: As Naturezas dos Valores

Os tipos primitivos do C# e a razão de existirem: int, double, decimal…

Concorrência: Fazendo Várias Coisas ao Mesmo Tempo
Concorrência: Fazendo Várias Coisas ao Mesmo Tempo

Como fazer um programa não travar enquanto espera: async e await. A aula…

O Que É Programar: Instruções, Máquinas e Linguagens
O Que É Programar: Instruções, Máquinas e Linguagens

A aula que assenta a fundação de tudo: o que significa, de fato, programar…