Imagem meramente ilustrativa de um desktop maneiro.

Dê uma chance para o NoSQL!

Fala pessoal, como estão? Reservei este post para comentar sobre algumas experiências boas que venho tendo com NoSQL no cenário onde utilizamos DDD e/ou uma OOP mais levada a sério, sem domínios anêmicos por exemplo.

Na verdade, este post não serve apenas para DocumentDb, você pode tomar estes pontos e aplicá-los em qualquer banco NoSQL, como MongoDb.

Escolhi o DocumentDb por algumas facilidades que ele oferece quando trabalhamos com .NET Core, mas nada que não possa ser portado para o Mongo utilizando o MongoClient.

Por que NoSQL?

Não entrarei em detalhes sobre o que é NoSQL, você pode descobrir mais lendo este excelente livro do Martin Fowler, chamado NoSQL Distiled. Então, prosseguindo irei expor alguns pontos no qual ele leva vantagem sobre um banco relacional. Tenha em mente que isto não é uma bala de prata, são cenários no qual o NoSQL se destaca.

Schemaless & ORMLess

Você já parou para pensar por que você precisa de um ORM na sua aplicação? Sim, para mapear Objeto/Relacional, e quando iniciamos o desenvolvimento focado no domínio, a primeira regra é esquecer o banco e focar no domínio!

Com o domínio pronto, vem a parte triste, fazer o mapeamento dos objetos para o banco, e aí começam as dores de cabeça. Nem EF, nem NH conseguem te dar um domínio totalmente limpo, é praticamente impossível. Eles não mapeiam ReadOnlyCollection, precisam dos modificadores Virtual e por aí vai.

Por outro lado, você já deve ter ouvido que os NoSQL são ótimos para persistência de estados, ou seja, podemos simplesmente "jogar" nossas entidades, convertidas como JSON para um Storage. Além disso, caso hajam mudanças nas entidades, novas versões do documento serão geradas no banco.

Se podemos simplesmente persistir nossas entidades como JSON no banco, significa que não precisamos mapear Objeto/Relacional, o que acarreta no não uso de um ORM (ORMLess, nem sei se existe este jargão).

Read/Write muito rápidos

Em termos de escrita e leitura, o NoSQL também se destaca, se não me engano (Busquem no Google), o tempo de escrita do DocumentDb é de apenas 17ms, algo sobrenatural.

Na verdade isto é óbvio. Não temos relações nem validações nos NoSQL, somente a escrita do documento direto. Isto é bom e ruim!

Em um projeto que presenciei recentemente, tínhamos uma API que recebia um POST e salvava o mesmo no DocumentDb. Fizemos um teste na API, rodando numa máquina S3 dos WebApps do Azure e ficamos de boca aberta na frente do computador. Sério, ficamos parados uns 15 segundos não acreditando no tempo de resposta! Ficou fantástico.

Por que NÃO NoSQL

Nem tudo são flores né, então vou estragar este momento romântico que criei dizendo que ele não é relacional!

Só isso? Pois é, mas vamos pensar… Você quer ter índices, validar campos, fazer inner joins, ter relação forte entre dados (Que é um caso ótimo no cenário de relatórios), então é melhor ir com calma.

O DocumentDb oferece transactions (ACID inclusive), dentre outros pontos. Na verdade ele oferece suporte a queries parecidas com do SQL Server, mas isto é apenas uma casca, ele não deixa de ser um NoSQL (Leiam o livro do Fowler que citei).

DocumentDb

Chega de blá blá blá e vamos ao que interessa. O DocumentDb é o NoSQL oferecido pela Microsoft, mais especificamente rodando no Microsoft Azure.

Tanto o DocumentDb quanto o seu SDK são bem documentados e fáceis de utilizar.

Antes de prosseguir, vou te apresentar uma amiga, a busca do Azure. Como não sou bom em identificar as coisas por ícones, sempre utilizo a busca, então vamos começar procurando por DocumentDb dentro do portal.

Campo de Busca da Dashboard do Microsoft Azure

Neste momento, abrirá uma lista com todos os bancos que você tem criado. Clique em "Add +" para criarmos um novo.

Neste momento, temos a opção de criar um DocumentDb ou MongoDb, ambos em ambiente PaaS (Quer paz, vá pro PaaS).

Criando um novo DocumentDb
IMPORTANTE: Lembre-se sempre de agrupar seus recursos (Apps, DB, Storage, etc) pelo Resource Group, isto garante que eles serão criados na mesma rede, evitando latência desnecessária.

Não sei se você notou, mas na criação do banco, não informamos nenhum recurso de escalabilidade, transações nem nada. Isto deve-se ao fato que podemos escalonar as coleções separadamente, o que eu particularmente achei fantástico.

O DocumentDb trabalha com RUs (Request Units), e podemos simplesmente escalonar as coleções que temos mais leitura e escrita. Como não pensamos nisto antes?

Vamos então criar nossa primeira coleção, acessando o banco criado, clicando em Overview e depois em Add Collection.

Na tela de criação da coleção, podemos selecionar a capacidade em GB da coleção e os RUs suportados. Tudo que você muda já altera o valor estimado, te dando uma previsão do quanto você irá gastar.

Ainda temos a opção de particionar a coleção a fins de otimizar a performance, mas por hora, selecione apenas o básico, para nosso exemplo.

Criando uma nova coleção

Ainda na tela Overview, você verá a nova coleção criada e poderá acessar as estatísticas de uso da mesma.

DocumentDb e .NET Core

Para executar este exemplo, vou criar um Console Application no .NET Core 1.1, rodando no MacOs e utilizando o Visual Studio Code, porém você pode executar no Linux ou Windows também.

A única coisa extra que utilizarei aqui é um plugin (Excelente por sinal) para download dos pacotes do NuGet no VSCode.

Plugin NuGet Package Manager

Iniciando a aplicação

Abra um Terminal ou Console, navegue para sua pasta de projetos e execute:

mkdir DocumentDbTest
cd DocumentDbTest
dotnet new console
dotnet restore
dotnet run
> hello world!

Abra o projeto criado com o Visual Studio Code e vamos codar!

Apresentando o Domínio

Nosso foco aqui é falar do uso do NoSQL em domínios ricos, então preparei um cenário (Meia boca, mas que serve) onde temos entidades, objetos de valor, relação entre elas e validações.

IMPORTANTE: Este domínio é meramente um exemplo, não tome como base para cenários reais.

Nosso primeiro objeto será o Customer, ou, cliente que estará realizando o pedido.

Domain/Entities/Customer.cs

Como podemos ver, esta entidade tem três ValueObjects, que são Name, Document e Email. O código completo você pode conferir aqui:

Movendo adiante, temos as entidades Product e OrderItem, que serão utilizadas na entidade Order, que representa nosso pedido.

Domain/Entities/Order.cs

Nesta entidade temos uma coleção de itens, cujo marcamos como ReadOnlyCollection para que o mesmo não possa ser corrompido.

IMPORTANTE: Para mais detalhes sobre a modelagem de domínios ricos, assista meu curso Modern Web Apps: http://player.balta.io/#/conteudo/1969

Basicamente temos um domínio bem limpo, sem Virtual, sem Parameterless Constructors, com ReadOnlyCollections. Recomendo que baixe o código para analisar melhor, e vamos agora persistir os dados.

Persistindo o pedido

Para persistir os dados, precisamos primeiro instalar o pacote Microsoft.Azure.DocumentDb.Core, então, basta adicionar a linha ao seu csproj, ou se você instalou a extensão que recomendei, basta apertar CMD+SHIFT+P e digitar "Nuget". Vai aparecer uma opção para pesquisar um pacote. Procure por Microsoft.Azure.DocumentDb.Core e selecione o pacote que aparecer.

Para este exemplo, utilizei o * na versão, para obter a última!

Com o pacote instalado e restaurado, precisamos da URI e PrimaryKey para acesso ao banco. No portal do Azure, na Dashboard do banco que criamos, tem uma opção chamada Key. Você pode obter estas duas informações lá.

IMPORTANTE: Não é interessante deixar estas chaves no código. Eu deixo as minhas por que depois que executo o exemplo apago os bancos, e ficando lá elas são mais fáceis de assimilar por você. Mas em um cenário real estas chaves devem ficar nas configurações da aplicação.

Com as chaves em mãos, basta instanciar um novo DocumentClient para que possamos acessar o método CreateDocumentAsync (Sim ele já trabalha com async naturalmente ❤)

Program.cs

No caso acima, eu recebo um "object", que pode ser qualquer coisa, mas no caso é o pedido (Order) e persisto na coleção orders, do banco balta que criamos.

Em seguida gero os objetos necessários para realizar o pedido e mando persistir.

Program.cs

Ao executar nosso programa, temos como resultado a inserção do pedido, já no formato JSON. Você pode conferir isto na aba Document Exmplorer, no dashboard do seu banco, no portal do Azure.

Documento inserido no banco

Conclusão

Em alguns cenários o NoSQL se destaca, e falando de domínios ricos e uma modelagem mais focada na OOP, ele talvez seja uma boa escolha para o seu negócio.