Tutorial MongoDB para iniciantes em NoSQL – Parte 4

MongoDB Logo
MongoDB Logo

E chegamos ao quarto artigo da minha série de tutoriais de MongoDB para iniciantes em NoSQL. Caso esteja caindo de pára-quedas nesta série, seguem os links dos posts e seus respectivos assuntos:

Neste quarto artigo tratarei de um tópico complicado, denso e muitas vezes controverso: modelagem de dados orientada a documentos.

Me basearei aqui em experiências próprias de mais de dois anos trabalhando com esta tecnologia, em projetos de todos os tamanhos, além das guidelines oficiais, obviamente. Ao invés de ficar falando sobre como você deve modelar, usarei uma didática baseada em comparação entre exemplos com modelos relacionais famosos para auxiliar na assimilação dos conhecimentos.

Preparado para finalmente entender como modelar seus bancos MongoDB?

Veremos neste artigo:

Princípios importantes

Primeiramente, tenha a mente aberta. Entenda que tudo que você aprendeu nas disciplinas de banco de dados da faculdade estão certas, mas em outro contexto, não nesse. Que as Formas Normais não significam nada aqui.

Eu tive a sorte de ter conhecido o MongoDB em um projeto já maduro que rodava há três anos e tinha muitos dados. Com isso, já aprendi do “jeito certo”, pois quando você cria um banco em um projeto pequeno, qualquer modelagem funciona e é difícil de imaginar como algumas decisões realmente podem lhe afetar no futuro.

O primeiro ponto a entender, e que muitas se recusam veemente é que você deve evitar relacionamentos entre documentos diferentes. Apesar dos avanços neste sentido nas últimas versões do MongoDB, este ainda é um banco não-relacional e, portanto, não faz sentido algum modelá-lo pensando em relacionamentos.

O segundo ponto é que documentos não são equivalentes a linhas de banco de dados. Essa é uma comparação muito simplória e que tende a levar ao erro. Documentos são entidades auto-suficientes, com todas as informações que lhes competem. Uma analogia que me ajuda a pensar nos documentos do jeito certo são as INDEXED VIEWS dos bancos relacionais. O plano é que, na maioria das consultas, com um filtro e sem “JOIN” algum, você consiga trazer todos os dados que necessita para montar uma tela de sua aplicação.

O terceiro e último ponto é manter simples. Não é porque o MongoDB permite que você aninhe até 100 níveis de subdocumentos dentro de um documento que você deve fazê-lo. Não é porque o MongoDB permite até 16MB por documento que você deve ter documentos com este tamanho. Não é porque você pode ter até 64 índices por coleção que você deve ter tudo isso. Essas e outras limitações estão lá na documentação oficial.

MongoDB não é magia, encare ele com a mesma seriedade que encara os bancos relacionais e estude bastante. A curva de aprendizagem inicial é realmente mais simples do que SQL, mas a longo prazo, é tão difícil quanto. Como já mencionei no post sobre persistência poliglota, os bancos não-relacionais não são melhores que os relacionais. Eles não eliminam os problemas dos relacionais, ao invés disso eles possuem os seus próprios problemas.

Para que você entenda tudo isso na prática, preparei um case simples de banco de dados que é bem comum, enquanto que futuramente espero ter tempo para apresentar cases mais complexos. Obviamente podem haver variações tanto na implementação relacional citada aqui, quanto a não-relacional que eu sugeri. Se apegue mais às ideias do que aos detalhes e use a seção de comentários para discutirmos o que você não concorda e/ou não entendeu.

Modelagem de Blog: Relacional

Bom, você está em um blog neste exato momento, então nada que eu disser aqui será uma novidade para você. Um blog possui basicamente artigos com id, título, data de publicação, conteúdo, categorias, tags, autor, status (rascunho, agendado, publicado, etc) e URL. As categorias possuem id, nome e descrição. Os autores possuem id, nome, foto, permissões, usuário, senha e bio. Além disso, temos os comentários. Ah os comentários, possuem toda uma complexidade própria: autor, data, texto, etc.

Em um banco relacional, como faríamos? (note que uso tabelas no singular e sem prefixos)

  • Tabela 1: Artigo, com ID (PK), Titulo (NVARCHAR), DataPublicacao (DATETIME), Conteúdo (NVARCHAR), IDAutor (FK), Status (INT – considerando um enumerador) e URL (NVARCHAR). Esta é a tabela principal e como era de esperar, o IDAutor vai referenciar outra tabela. Mas cadê tags e categorias?
  • Tabela 2: Autor, com ID (PK), Nome (NVARCHAR), Bio (NVARCHAR), Foto (NVARCHAR – porque guardarei apenas o caminho da foto), Usuario (NVARCHAR, unique) e Senha (NVARCHAR)
  • Tabela 3: Categoria, com ID (PK), Nome (NVARCHAR) e Descricao (NVARCHAR). A descrição é útil nas páginas de posts de uma mesma categoria.
  • Tabela 4: ArtigoCategoria, com ID (PK), IDArtigo (FK), IDCategoria (FK). Um artigo pode ter várias categorias e cada categoria pode estar em vários artigos, o clássico “N para N”, um câncer dos bancos relacionais na minha opinião.
  • Tabela 5: Tag, com ID (PK), IDArtigo (FK), Nome (NVARCHAR). Aqui podemos fazer um “1 para N”, onde cada artigo pode ter várias tags ou um “N para N”, se quiser a não-repetição dos nomes de tags.
  • Tabela 6: Comentario, com ID (PK), IDArtigo (FK), Nome (NVARCHAR – autor do comentário), Texto (NVARCHAR – o comentário em si), Data (DATETIME).

Pode ser mais complexo e poderoso que isso, mas vamos manter assim, visto que o objetivo aqui não é modelar o melhor banco de dados para blogs do mundo, mas sim apenas para mostrar como uma modelagem dessas pode ser feita em um banco relacional e em um não-relacional no mesmo post.

Diagrama ER Blog
Diagrama ER Blog

Considerando o banco descrito acima, como faríamos para montar uma tela de uma postagem completa no site do blog?

Essa consulta traz as informações do Artigo e de seu Autor (só o que importa do autor para a página do post). Mas e as tags e categorias? Precisamos de mais duas consultas para isso:

Puxa, esqueci dos comentários ainda, certo? Vamos trazê-los também!

Com isso conseguimos finalmente montar uma página bem simples, com apenas uma postagem de um blog. Precisamos de 4 consultas com 5 JOINs diferentes.

Complicado, não?!

E no MongoDB, como ficaria isso?

Modelagem de Blog: Não-Relacional

Em bancos de dados não-relacionais, o primeiro ponto a considerar é que você deve evitar relacionamentos, afinal, se for pra usar um monte de FK, use SQL e seja feliz!

A maioria das relações são substituídas por composições de sub-documentos dentro de documentos, mas vamos começar por partes. Primeiro, vamos criar um documento JSON que represente o mesmo artigo do exemplo anterior com SQL (dados de exemplo apenas), como pertencendo a uma coleção Artigo:

Primeira diferença: aqui os IDs não são numéricos auto-incrementais, mas sim ObjectIds auto-incrementais. Segunda diferença: não há schema rígido, então eu posso ter artigos com mais informações do que apenas estas, ou com menos. Mas e aquele idAutor ali?

Se eu colocar daquele jeito ali, com idAutor, quando eu for montar a tela do artigo eu sempre teria de ir na coleção de Autores para pegar os dados do autor, certo? Mas se eu sei quais informações eu preciso exibir (apenas nome e id, assim como na consulta SQL), o certo a se fazer aqui é reproduzir estas informações como um subdocumento de artigo, como abaixo:

Esse é o jeito “certo” de fazer com MongoDB! Note que o subdocumento autor possui apenas os dados necessários para montar uma tela de artigo. Mas se precisarmos depois da informação completa do autor, podemos pegá-la a partir de seu id.

Como MongoDB não garante integridade referencial por não possuir FK, temos de tomar muito cuidado quando você for excluir um autor, para desvinculá-lo em todos os artigos dele, e quando você for atualizar o nome dele, para atualizar em todos artigos dele.

Já a coleção Autor, fica com documentos bem parecidos com a tabela original em SQL:

Note que aqui eu não vou embutir todos os artigos deste autor, pois seria inviável. O foco do blog são os artigos, não os autores, logo, os artigos vão conter o seu autor dentro de si, e não o contrário!

Um adendo: MongoDB trabalha muito bem com arquivos binários embutidos em documentos, então se quisesse usar um binário de imagem no campo foto, funcionaria perfeitamente bem, melhor que no SQL tradicional (BLOBs, arghhh!).

Mas e as categorias e tags? Como ficam?

Primeiro que aqui não precisamos de tabelas-meio quando temos relacionamento N-N. Todos os relacionamentos podem ser representados na modalidade 1-N usando campos multivalorados, como mostrado no exemplo abaixo, de uma v3 do documento de artigo:

Tags são apenas strings tanto na modelagem relacional original quanto nesta modelagem orientada a documentos. Sendo assim, um campo do tipo array de String resolve este cenário sem maiores problemas.

Já as categorias são um pouco mais complexas, pois podem ter informações extras como uma descrição. No entanto, como para exibir o artigo na tela do sistema nós só precisamos do nome e do id das categorias, podemos ter um array de subdocumentos de categoria dentro do documento de artigo.

Em paralelo deveremos ter uma coleção de categorias, para armazenar os demais dados, mantendo o mesmo ObjectId:

Novamente, caso no futuro você venha a excluir categorias, terá de percorrer toda a coleção de artigos visando remover as ocorrências das mesmas. No caso de edição de nome da categoria original, também. Com as tags não temos exatamente este problema, uma vez que elas são mais dinâmicas.

Para encerrar, temos os comentários. Assim como fizemos com as categorias, vamos embutir os documentos dos comentários dentro do documento do artigo ao qual eles fazem parte, afinal, não faz sentido algum eles existirem alheios ao artigo do qual foram originados.

Note que aqui optei por ter um _id no comentário, mesmo ele não possuindo uma coleção em separado. Isso para que seja possível mais tarde moderar comentários.

E esse é um exemplo de documento completo, modelado a partir do problema de um site de artigos, blog, wiki, etc.

Ah, mas e como fazemos a consulta necessária para montar a tela de um artigo? Lembra que em SQL eram inúmeras consultas cheias de INNER JOINs? No MongoDB, precisamos de apenas uma consulta, bem simples, para montar a mesma tela:

Essa consulta traz os mesmos dados que todas aquelas que fiz em SQL juntas. Isso porque a modelagem dos documentos é feita sem relacionamentos externos, o documento é auto-suficiente em termos de dados, como se fosse uma VIEW do SQL, mas muito mais poderoso.

Claro, existem aqueles pontos de atenção que mencionei, sobre updates e deletes, uma vez que não há uma garantia nativa de consistência entre coleções. Mas lhe garanto, a performance de busca é incomparável, uma vez que a complexidade é baixíssima. Você inclusive pode criar um índice no campo url da coleção Artigo, para tornar a busca ainda mais veloz, com o comando abaixo:

Demais, não?!

Você pode estar preocupado agora com a redundância de dados, afinal diversas strings se repetirão entre os diversos anúncios, como o nome do autor, tags, nomes de categoria, etc. Não se preocupe com isso, o MongoDB trabalha muito bem com volumes muito grandes de dados e desde que você tenha bastante espaço em disco, não terá problemas tão cedo. Obviamente não recomendo que duplique conteúdos extensos, mas palavras como essas que ficaram redundantes na minha modelagem não são um problema.

No entanto, lidar com subdocumentos e campos multivalorados adiciona um pouco mais de complexidade à manipulação do MongoDB, e tratarei disso no próximo post da série. Aguarde!

 

Quer aprender outras dicas? Leia o que escrevi sobre boas práticas com MongoDB no blog da Umbler!

Curtiu o post? Que tal aprender a usar MongoDB com Node.js? Então clica no banner abaixo e dá uma conferida no meu livro sobre programação web com Node.js!

Tutorial MongoDB para iniciantes em NoSQL – Parte 3

MongoDB Logo
MongoDB Logo

Este é o meu terceiro post da série “tutorial MongoDB para iniciantes em NoSQL “, caso esteja chegando agora aqui no blog, sugiro dar uma olhada na parte 1 e parte 2.

No tutorial de hoje, vamos explorar alguns comandos muito utilizados para administração de bancos de dados MongoDB, como os comandos de dump e restore, úteis para migrar do ambiente local para produção ou mesmo entre diferentes provedores de cloud computing. Além disso, veremos como mapear nomes de coleções e campos nos documentos.

Fazendo backup com MongoDump

Uma das maneiras de fazer backup da sua base de dados MongoDB é usando o utilitário de linha de comando mongodump, que fica na pasta bin da sua instalação de MongoDB.

Antes de fazer o dump, sugiro criar uma pasta backup na mesma pasta do MongoDB, no mesmo nível da pasta bin.

Considerando que você está rodando um servidor de MongoDB localmente e sem segurança (apenas subiu um mongod como ensinado na parte 1 desta série) o processo é muito simples. Abra seu terminal de linha de comando e navegue até a pasta bin do MongoDB. Execute o seguinte comando (considerando que tenho uma pasta backup dentro de MongoDB):

O parâmetro ‘–out’ define a pasta onde serão salvas pastas com os nomes das databases e dentro delas os arquivos .bson contendo todas as collections. Os arquivos .bson contém todos os documentos JSON da coleção em questão, mas em formato binário.

Já se o seu servidor for remoto e/ou possuir credenciais de acesso (um cenário bem comum), você deve passar alguns parâmetros adicionais ao executar o utilitário mongodump:

-h <hostname>:<port>: o parâmetro -h permite especificar o endereço completo do host onde está hospedado seu banco MongoDB, incluindo porta. Ex:

-u <username>: o parâmetro -u permite especificar o nome de usuário que será utilizado para conectar na base a ser feito o dump.

-p <password>: a senha do usuário definido no parâmetro -u. Ex:

Restaurando backup com MongoRestore

Uma vez que você tenha feito seu backup, pode restaurá-lo em outro servidor usando o mongorestore. Para realizar este exemplo da maneira mais didática possível, sugiro que tenha uma conta criada em algum provedor de nuvem que ofereça serviços de MongoDB, como a Umbler (que te dá créditos para usar de graça) ou a mlab.com, que é um serviço exclusivo de MongoDB (dão 500MB de graça pra testar).

Na Umbler, que é a empresa da qual sou Dev Evangelist, você tem de criar primeiro um site em qualquer tecnologia (que tal Node.js?) e depois acessar a opção banco de dados e criar um banco do tipo MongoDB. Não esqueça de anotar os dados de acesso que você definir neste momento, para não se esquecer depois.

Depois que você cria o banco, clicando nele para ver os detalhes, você notará que existem informações para gerenciamento externo, que é justamente o que precisamos para restaurar o backup que fizemos anteriormente.

Gerenciamento Externo do MongoDB
Gerenciamento Externo do MongoDB

Novamente no terminal de linha de comando, acesse a pasta bin da sua instalação de MongoDB para encontrar o utilitário mongorestore. Use os seguintes parâmetros junto ao mongorestore para orientá-lo corretamente:

-h <hostname>:<port>: para definir endereço do servidor e porta

-d <database>: para definir o nome da base de dados no servidor de destino. Se a base não existir, ela será criada (se o usuário utilizado possuir permissão para isso)

-u <username>: para definir o nome de usuário no servidor de destino

-p <password>: a senha do usuário definido em -u

Além destes parâmetros, a última informação para executar o mongorestore corretamente é o caminho até a pasta de backup da sua database, como abaixo:

Opcionalmente, você pode passar um parâmetro -c informando apenas uma coleção que deseja restaurar (ao invés de restaurar a database inteira).

Caso tenha tido problemas em fazer o dump e o restore, eu mostro na prática no finalzinho do vídeo abaixo, que gravei pra Umbler:

Alterando metadados

Nos bancos relacionais temos duas categorias de comandos: DDL e DML. Comandos DML ou Data Manipulation Language são o equivalente ao CRUD que fizemos nas duas partes anteriores desta série. Já os comandos DDL ou Data Definition Language, são os comandos CREATE, ALTER, DROP, etc que os bancos relacionais possuem para modificar os metadados.

O MongoDB não possui uma diferenciação de dados e metadados tão clara quanto nos bancos relacionais, o que impacta diretamente no que você pode e não pode fazer depois que sua base já está em produção. Mudar o nome da sua base de dados, por exemplo, não é possível.

Apesar disso, mudar nome de coleções é possível, embora não recomendado durante horários de muito acesso ao seu banco, pois todas as queries em andamento àquela coleção são dropadas. Para mudar o nome de uma coleção, conecte-se à sua base de dados usando o utilitário mongo, como vimos nos artigos anteriores, e depois use a função renameCollection, passando o novo nome da coleção:

Note que o MongoDB não é case-sensitive nos nomes de databases, mas nos nomes de coleções, nomes de campos e valores de campos, sim.

Nos bancos relacionais, quando queremos adicionar uma nova coluna, devemos fazer um ALTER TABLE, certo? No MongoDB não há necessidade disso, uma vez que o schema é flexível e conforme você adiciona novos documentos com novos campos, eles passam a surgir e pronto. Mais tarde, se quiser adicionar novos campos em documentos já existentes, basta usar um comando ‘update’ usando o update-operator $set, como já vimos anteriormente:

Novamente falando dos bancos relacionais, quando queremos remover uma coluna, também temos de fazer um ALTER TABLE. No MongoDB não é necessário, basta executar um update passando o update-operator $unset, como abaixo:

E por fim, caso deseje apenas renomear um campo já existente (coisa que também iria requerer um ALTER TABLE no SQL), basta fazermos um update novamente, usando o update-operator $rename:

Note que usei aqui um updateMany e que como filtro passei um objeto vazio. Isso fará com que o update seja realizado sobre TODOS os documentos da coleção clientes. Outro ponto de atenção é que o $rename permite múltiplas renomeações no mesmo update, apenas informando todos os campos que serão renomeados e seus novos nomes, no formato JSON tradicional (que nem em um $set).

A título de curiosidade, e para evitar surpresas, o comando $rename é apenas um atalho para o uso do comando $unset no nome antigo, seguido de um $set com o novo nome e valor antigo. Por causa disso, certifique-se de que não ocorram colisões de nomes na sua coleção para evitar surpresas indesejadas.

No próximo artigo da série, falo sobre modelagem de banco de dados orientado à documentos, para tentar jogar uma luz sobre como fazê-lo conforme mandam as guidelines oficiais do MongoDB. Você também pode ler o que escrevi sobre boas práticas com MongoDB no blog da Umbler!

Curtiu o post? Que tal aprender a usar MongoDB com Node.js? Então clica no banner abaixo e dá uma conferida no meu livro sobre programação web com Node.js!

Microservices e Agile: o futuro da programação?

Este artigo é uma reprodução do artigo homônimo publicado na edição impressa da revista iMasters do mês de agosto de 2017.

Revista iMasters
Revista iMasters

Nenhum destes termos é exatamente novo, mas por algum motivo, jamais estiveram tão na moda como atualmente. Mas o que teriam, a arquitetura microservices e os métodos ágeis de desenvolvimento de software em comum? Ou melhor: por que seriam eles o futuro da programação? Não tenho a pretensão de ser eu a dizer como as empresas de tecnologia deveriam trabalhar, mas vou tentar trazer uma luz para esse assunto e provocar uma reflexão (espero) na forma como você trabalha.

Os métodos ágeis existem desde a virada do século, mas, mais recentemente, nos últimos cinco anos, uma nova onda de “agilistas” passou a defender um modelo que seria uma evolução dos métodos ágeis originais. Essa evolução foi proposta pelo Spotify em um famoso vídeo onde é apresentado o modelo de “squads” (esquadrões) do Spotify, que, em teoria, não possui grandes diferenças em relação ao Scrum Team original mas que propõe um mindset focado em um único produto à cada um dos times/squads ao invés do foco tradicional em projetos.

O termo viralizou entre as startups e hoje empresas como Nubank, Umbler e Conta Azul, por exemplo, organizam-se em squads também. Organizar um squad ao redor de cada produto faz com que as pessoas “abracem” o mesmo como se fosse “seu” e que queiram o melhor para ele em termos de tecnologia, experiência, resultados, etc. Permite que os times tenham mais controle (e mais resultados) com seus OKRs, KPIs, etc. Como bem o Scrum prega, cada time decide como seu produto deve ser melhor desenvolvido com base na estratégia da empresa e isso inclui TODOS os aspectos técnicos, como linguagem utilizada, banco de dados, etc.

Tudo isso é muito lindo e é exatamente o que as empresas querem: esquadrões focados no produto que entreguem software rapidamente (já ouviu falar de CI?) com as melhores tecnologias para resolver cada problema. Mas sabemos que na prática não é tão simples assim, certo? Mas deveria, e já, já, eu explico o porquê.

Estamos há décadas desenvolvendo software em grandes e complexos blocos de software que a própria TI chama de monolíticos, em alusão às grandes pedras (monólitos). Não importa se você organiza sua aplicação em ‘x’ camadas. Se você não pode fazer o deploy de apenas uma delas sem precisar compilar o projeto inteiro, o seu software é monolítico. Se você não pode escrever uma de suas camadas em uma linguagem diferente das demais, também. E isso não é ruim, aprendemos a construir softwares assim desde a faculdade e construímos grandes softwares ao longo das décadas que a profissão de programador existe. É apenas uma característica, sem julgamentos.

No entanto, cada vez mais o mercado nos pede softwares maiores e mais complexos, e nossos monólitos ficam ainda maiores e mais complexos. Mas ao invés de ficarem igualmente resistentes como a analogia, estão mais para um castelo de cartas altíssimo, infelizmente. Isso porque as demandas atuais estão exigindo pluralidade de tecnologias. E as aplicações monolíticas não trabalham muito bem com pluralidade, não foram concebidas desta forma. Fato.

Mas então, como  podemos alinhar diferentes tecnologias, diferentes produtos, mantendo uma alta velocidade de integração, qualidade e atendendo às expectativas, internas e externas, em relação aos projetos em que trabalhamos?

Com uma outra tendência que voltou muito forte após décadas: microservices.

Microservices não é algo exatamente novo, embora tenha-se voltado a falar dessa arquitetura há poucos anos. A ideia central é que você quebre sua aplicação em serviços bem pequenos, semelhante ao que SOA e CORBA propõem, mas sem as complicações que as grandes corporações criaram no em torno destas duas excelentes ideias (ESB?). Cada um desses microservices é auto suficiente na sua responsabilidade, é independente de linguagem, de persistência e comunica-se com os demais serviços através de protocolos comuns, como HTTP.

Não é à toa que tecnologias como Node.js, Elixir, Scala e Go (sem citar as funcionais em geral) estejam tão em alta ao mesmo tempo em que só se fala de arquitetura microservices e squads (não esqueçamos a persistência poliglota!). Ao que tudo indica, essa combinação de tecnologias leves e focadas em paralelismo, em micro serviços, mantidos por squads, para compor grandes soluções; são a “bola da vez” e podem ser a “salvação” para conseguirmos construir (e manter) os sistemas do futuro. Grandes empresas como Amazon, ThoughtWorks, Uber e Netflix acreditam nisso.

Por que eu não acreditaria? 🙂