Padrão Embedded Document no MongoDB

Johnnes Cruz
luizalabs
Published in
4 min readMar 31, 2023

--

Você já ouviu falar desse padrão? Como utiliza-lo? Quais vantagens e desvantagens?

O que é?

Esse padrão de modelagem de banco de dados consiste em incluir todas informações dos relacionamentos em um documento único. Quando falamos de modelagem de banco de dados orientados a documentos podemos mudar totalmente a forma de pensar no seu esquema. Confesso que no primeiro contato se você está habituado com tabelas e relacionamentos no modelo tradicional (SQL) terá um pouco de dificuldade com esse conceito mas este artigo tem a intenção de introduzir esse tema.

O banco de dados é uma parte importante de quase toda aplicação e especificar como os dados estarão armazenados no SGBD é uma tarefa bem comum no processo de desenvolvimento de software e se você vir do mundo de banco de dados relacional como PostgreSQL, MySQL estará provavelmente acostumado com MERs ou DERS [1] e também com normalização de dados [2], que consiste resumidamente em especificar/documentar o esquema do banco de dados e remover a duplicação de dados, respectivamente. Porém, quando vamos aplicar esse padrão Embedded Document queremos otimizar a busca de dados e para isso removemos a normalização.

De forma fictícia trarei um exemplo de um DER de um sistema de pedidos:

Figura 1 — DER de uma sistema que recebe pedidos

Continuando a simplificação trarei dados para 2 pedidos fictícios.

Praticando SQL

Código SQL do exemplo usando PostgreSQL:

CREATE TABLE produto(
id INT GENERATED ALWAYS AS IDENTITY,
titulo TEXT NOT NULL,
descricao TEXT,
preco NUMERIC(8, 4) NOT NULL,
PRIMARY KEY(id)
);

CREATE TABLE pedido(
id INT GENERATED ALWAYS AS IDENTITY,
data_pedido date NOT NULL,
PRIMARY KEY(id)
);

CREATE TABLE pedido_item(
id INT GENERATED ALWAYS AS IDENTITY,
pedido_id INT NOT NULL,
produto_id INT NOT NULL,
quantidade INT NOT NULL,
total NUMERIC(12, 2) NOT NULL,
PRIMARY KEY(id),
CONSTRAINT fk_pedido FOREIGN KEY(pedido_id) REFERENCES pedido(id),
CONSTRAINT fk_produto FOREIGN KEY(produto_id) REFERENCES produto(id)
);


INSERT INTO produto (titulo, descricao, preco)
VALUES ('Produto 1', 'Descrição do produto 1', 10.0),
('Produto 2', 'Descrição do produto 2', 20.0);


INSERT INTO pedido(data_pedido) VALUES ('2023-02-19'), ('2023-02-20');


INSERT INTO pedido_item(pedido_id, produto_id, quantidade, total)
VALUES (1, 1, 10, 100), (2, 1, 6, 60), (2, 2, 7, 140);

Imaginemos que haja a necessidade de realizar a seguinte tarefa:

Construa uma tela que liste os pedidos demonstrando os campos: Data, quantidade de itens e o total do pedido

Provavelmente a query para realizar essa tarefa seria algo como:

SELECT
t1.id,
t1.data_pedido,
count(t2.id) as quantidade_itens,
sum(t2.total) as total
FROM pedido as t1
INNER JOIN pedido_item as t2 ON t1.id = t2.pedido_id
GROUP BY 1, 2;

Resultado da query:

id  | data_pedido | quantidade_itens | total  
----+-------------+------------------+--------
1 | 2023-02-19 | 1 | 100.00
2 | 2023-02-20 | 2 | 200.00

Mas e se fosse necessário também trazer a lista de produtos que compõe o pedido? Seria necessário mais uma consulta ou no mínimo uma junção com a tabela produto.

E como ficaria no padrão Embedded document?

Ainda teríamos uma coleção produto (Coleção é forma para especificar no MongoDB o que seria uma tabela no modelo relacional) porém com uma única coleção chamada pedido que conteria toda informação relacionada a esse domínio, exemplo:

Código mongo do exemplo:

db.pedido.insertMany([
{
"id": 1,
"data_pedido": "2023-02-19",
"items": [
{
"produto_id": 1,
"titulo": "Produto 1",
"descricao": "Descrição do produto 1",
"preco": 10.0,
"quantidade": 10,
"total": 100.0
}
],
"quantidade_itens": 1,
"total_pedido": 100.0
},
{

"id": 2,
"data_pedido": "2023-02-20",
"items": [
{
"produto_id": 1,
"titulo": "Produto 1",
"descricao": "Descrição do produto 1",
"preco": 10.0,
"quantidade": 6,
"total": 60.0
},
{
"produto_id": 2,
"titulo": "Produto 2",
"descricao": "Descrição do produto 2",
"preco": 20.0,
"quantidade": 7,
"total": 140.0
}
],
"quantidade_itens": 2,
"total_pedido": 200.0
}])

Com o registro de pedidos dessa forma você precisa somente buscar os dados nessa coleção sem nenhuma junção ou processamento. Inclusive, pode solicitar somente dados necessários para atender o pedido inicial.

Exemplo de query no MongoDB:

# Linha 2 - Busca tudo na base, equivale ao select sem where
# Linha 3 - Seleciona todos os campos exceto _id e items
db.pedido.find(
{},
{"_id": 0, "items": 0}
)

Resultado da query:

[
{
id: 1,
data_pedido: '2023-02-19',
quantidade_itens: 1,
total_pedido: 100
},
{
id: 2,
data_pedido: '2023-02-20',
quantidade_itens: 2,
total_pedido: 200
}
]

Conclusão

Existe uma vantagem nessa abordagem que é a performance, se você tiver um modelo de banco de dados com muitos relacionamentos construir consultas que façam muitas junções tem custo de processamento no banco de dados, quando aplicado esse padrão Embedded Document reduzimos o processamento mas inserimos o aumento de armazenamento de informações como uma desvantagem, cada registro de pedido acaba que duplica informações de produto e isso tem um custo.

Outra possível desvantagem são as atualizações, se for um requisito do seu sistema sempre manter atualizado os registros dos relacionamento ao aplicar esse padrão você teria que realizar um processamento para atende-lo. No exemplo acima, se um produto sofrer atualização do título você teria que passar atualizando esse campo nos documentos da coleção de pedidos gerando um processamento a mais.

Possível solução via query MongoDB:

# Linha 2 - Busque pedidos que possui o produto de id 1
# Linha 3 - Atualize o título para o produto de id 1 ($ tem a posição na lista)
db.pedido.updateMany(
{'items.product_id': 1},
{$set: {"items.$.titulo": "Produto 1 - v2"}}
)

Referências

[1] https://www.alura.com.br/artigos/mer-e-der-funcoes
[2] https://www.luis.blog.br/normalizacao-de-dados-e-as-formas-normais.html
[3] https://www.mongodb.com/docs/manual/core/data-model-design/#std-label-data-modeling-embedding

Ferramentas utilizadas

QuickDatabaseDiagrams

PostgreSQL

MongoDB: The Developer Data Platform

--

--