Verticalização de dados – Uma abordagem prática

Arquitetura - DevSpace

Última atualização em 9 de outubro de 2024 por Willian Tuttoilmondo

Fundações

Uma boa construção, independente do que seja, começa por uma boa fundação. Sejam, prédios, casas ou, até mesmo, aplicações. Se para uma casa ou prédio sólido e confiável é necessária uma fundação igualmente sólida, por que para uma aplicação seria diferente? Quando pensamos em uma fundação para aplicações, a primeira coisa que nos vem à mente é o banco de dados. Uma boa modelagem, seguindo as formas normais e tudo aquilo que foi teorizado durante anos, visto e revisto em cursos livres, no meio acadêmico e no mercado de trabalho, parece ser a melhor opção. Contudo, a coisa não é bem assim. Com o surgimento do Big Data, a necessidade de uma resposta rápida para modelos de dados vem crescendo exponencialmente. Mesmo uma base de dados bem modelada horizontalmente – onde cada propriedade corresponde a uma coluna em uma tabela – pode não ter o desempenho que se espera quando o volume de solicitações por dados é muito grande. E, acreditem, isso está mais próximo de nós do que podemos imaginar. Para solucionar este problema, uma nova forma de armazenar dados foi pensada: a verticalização de dados. E é sobre ela que falarei a seguir, dando exemplos práticos, discutindo as opções tecnológicas para o modelo e demonstrando o poder que uma coisa tão simples pode ter.

Um horizonte repleto de dados

Até alguns anos atrás, sempre que iniciávamos um projeto, partíamos de uma modelagem horizontal. Nela, cada entidade lógica da aplicação convertia-se em uma entidade física no banco de dados, respeitando sua construção, criando – em quase todos os casos – uma relação de “um para um” entre entidades lógicas e físicas. Nelas abrigávamos todas as propriedades descritas no modelo lógico, transcrevendo-as para nosso modelo entidade-relacionamento. Sendo assim, nossas entidades físicas ficavam mais “legíveis”, fáceis de compreender.

Difundida por anos, essa abordagem mostrou-se eficiente em cenários altamente complexos, com grande volumetria e alta concorrência. Bases de dados gigantescas, na casa dos terabytes, ainda são suportadas e mantidas neste modelo. Servidores de bancos de dados como Oracle, IBM DB2, Sybase ASE, PostgreSQL e, até mesmo, o Microsoft SQL Server são capazes de manterem-se performáticos sob tais circunstâncias. É muito fácil toparmos com bases de dados legadas ainda desenvolvidas desta forma, mas uma coisa é certa: a medida que a aplicação cresce, a demanda por manutenção na base de dados cresce na mesma proporção.

Em uma base horizontal, em uma relação 1:1 entre 90 e 95% em relação ao modelo lógico, quando uma nova entidade lógica é criada na aplicação, uma entidade física deve ser implementada na base de dados. Sendo assim, para que o desenvolvedor realize seu trabalho, o responsável pela base de dados deverá ser acionado para manuteni-la antes que o desenvolvedor possa começar sua atividade. A mesma coisa acontece quando uma entidade já existente precisa ser alterada. Se uma nova propriedade for adicionada a uma entidade lógica, ela deverá ser replicada na base de dados. Isso demanda tempo, planejamento e, principalmente, dinheiro.

Se estivermos utilizando uma plataforma que comporte algum ORM – como Hibernate, Migrations ou qualquer outro disponível no mercado –, isso pode facilitar um pouco a tarefa mas, nem sempre, é garantia de que não haverá intervenção. ORMs tendem a cumprir seu papel muito bem nesta relação 1:1 e podem trazer vantagens para a aplicação, mas limitam-se a isso. Em modelos mais complexos, suas vantagens são quase nulas, principalmente quando precisamos compor uma consulta com domínios de informação de diversas fontes. Numa situação como essa, acabamos construindo consultas extensas, com diversas junções, as quais podem ter um custo alto em termos de processamento.

Se, pelo menos, houvesse uma forma de diminuir o processamento de modelos de dados complexos, ampliando sua performance…

O “bom e velho” modelo horizontal

Hoje, um recurso muito comum para interoperabilidade de serviços é a implementação da comunicação via notação JSON (JavaScript Object Notation). Nela, um objeto complexo pode ser descrito com extrema facilidade, incluindo objetos aninhados, listas escalares e de objetos. Isso nos dá uma imensa flexibilidade para lidar com objetos complexos, compostos de informações das mais diversas, independentemente da linguagem de programação que estejamos trabalhando. Sendo assim, um único objeto pode conter dados pertencentes a vários domínios de informação distintos. Exemplo:

O objeto descrito acima é um objeto complexo que representa um município, no caso, Curitiba. Ele contém informações básicas como nome, latitude, longitude, altitude em relação ao nível do mar, população, código do IBGE e código de área do município (DDD). Mas, se nos aprofundarmos, ele também contém as informações do estado ao qual pertence, no caso, Paraná. Neste objeto, chamado estado, constam as informações relevantes ao estado do Paraná, como nome, sigla, região geográfica, código do IBGE e códigos de área existentes, em forma de lista. E, dentro deste objeto estado há mais um objeto: pais. Este novo objeto refere-se ao país ao qual o estado pertence e nele estão contidas informações como nome, mnemônico ISO Alpha2, mnemônico ISO Alpha3, código numérico universal e código de discagem internacional (DDI).

Neste exemplo, podemos perceber que acessamos a “ficha completa” do município de Curitiba, com informações sobre o município, estado e país, em um único bloco. Isso nos traz ganhos reais em termos de performance de acesso à informação, pois ela está toda concentrada em um único ponto. Se estivéssemos trabalhando em um modelo horizontal de dados, para compor o objeto JSON deste município precisaríamos acessar três entidades diferentes no banco de dados: pais, estado e cidade. Partindo do princípio lógico que um país pode conter vários estados, teríamos nossa entidade pais composta pelos atributos que o competem (nome, alpha2, alpha3, numerico e ddi) além, claro, do seu identificador (id).

Em adição a isso, teríamos uma entidade estado, a qual, além de suas informações básicas (id, nome, sigla, regiao, cod_ibge e ddds), deveria contar com uma propriedade que referenciasse o país ao qual ela pertence (pais_id). E, complementando nosso raciocínio, como um estado pode ter vários municípios, também teríamos uma entidade cidade, a qual, além de suas informações básicas (id, nome, latitude, longitude, altitude, população, cod_ibge e ddd), que conteria seu vínculo com a entidade estado (estado_id). Sendo assim, para abrangermos todos os dados constantes no objeto JSON do município, teríamos uma instrução SQL construída desta maneira:

Isso não parece nem um pouco estranho para quem está no mercado de desenvolvimento. Temos uma entidade para cada tipo (cidadeestadopais) e utilizamos junções (join) para uni-las em uma única resposta. Isso realmente funciona e muitas empresas ao redor do mundo têm usado esta técnica para modelar e desenvolver seus produtos, mas é um pouco cansativo para o desenvolvedor manter uma estrutura como essa. Se alterarmos alguma das entidades entidades, precisamos alterar a instrução SQL que busca os dados para compor o objeto. Isso encarece a manutenção, pois mais intervenções são necessárias a cada alteração da aplicação.

Um giro de 90º

Ao nos depararmos com a necessidade de armazenar grandes volumes da dados, onde a volatilidade estrutural é uma realidade, o modelo horizontal passa a ser um problema. Se precisarmos abrigar, em uma mesma estrutura, dados de fontes completamente diferentes, com estruturas e valores diferentes, a complexidade das entidades e suas relações começa a aumentar exponencialmente. O processo de manter uma base de dados horizontal neste cenário é praticamente impossível, sem levar em consideração a evidente queda no desempenho do servidor de banco de dados, o qual terá dificuldades para montar os modelos de negócio e entregar os dados referentes às consultas realizadas. Para um exemplo mais prático, tomemos algo que tem se tornado uma “febre” entre grandes redes varejistas: o marketplace.

Quando criamos um marketplace, entendemos que poderemos abrigar todo e qualquer produto que pode ser vendido, de calçados a geladeiras, de autopeças a cosméticos, de instrumentos musicais a alimentos. Em um universo tão diverso como este, manter uma base de dados horizontal é algo impensável. Imaginem o impacto de uma alteração simples, como a adição de uma propriedade em alguma entidade envolvida nos processos deste marketplace. É, não seria uma tarefa fácil.

Se imaginarmos que, em nosso servidor, temos uma aplicação do tipo API Provider, a qual recebe e entrega solicitações na notação JSON, não seria mais simples se pudéssemos, simplesmente, armazenar os objetos JSON que serão entregues como resposta? Bem, vejamos como isso é possível.

E surge a verticalização de dados

Como alternativa aos bancos de dados SQL tradicionais, e para atender à crescente necessidade pela verticalização de dados, os bancos de dados NoSQL (Not only SQL), começaram a surgir. Soluções como o MongoDB, Cassandra e diversos outros estavam disponíveis para quem quisesse se aventurar neste novo modelo de desenvolvimento de bancos de dados. O MongoDB acabou se tornando popular muito rapidamente, justamente por poder armazenar objetos JSON diretamente. Nele, não existem tabelas ou colunas, mas documentos (os objetos JSON em si) e coleções (listas de objetos JSON agrupados sob um domínio de informação).

A princípio, isso veio resolver as questões mais relevantes relativas ao grande volume e à verticalização de dados. Poder montar coleções polimórficas de objetos que podem ser completamente diferentes, dentro de uma única estrutura de dados, parecia ser um sonho. Várias empresas compraram esta ideia e passaram a migrar seus dados para estruturas verticais. São inúmeros os cases de sucesso no Brasil e no mundo, os quais serviram de base para todos os demais que se seguiram.

Uma reação em cadeia

Por conta do avanço das soluções NoSQL em detrimento à impossibilidade do uso de bases de dados SQL tradicionais para a verticalização de dados, os fornecedores de soluções começaram a reagir. Oracle, Microsoft SQL Server e PostgreSQL logo passaram a contar com tipos JSON nativos e ferramentais básicos e muito limitados para trabalhar com os novos campos JSON. Porém, varreduras dinâmicas nestes novos campos, se não eram impossíveis, eram extremamente complexas. Enquanto isso, as soluções NoSQL continuavam a avançar sobre o mercado das soluções SQL.

Contudo, a comunidade envolvida com o desenvolvimento do PostgreSQL não estava muito disposta a perder esta disputa. Durante o desenvolvimento da versão 9.4, lançada em 2014, foi adicionado o tipo jsonb, uma implementação binária do tipo json nativo do banco de dados, o que possibilitou aos analistas e desenvolvedores de bancos de dados fazer indexação direta pelo objeto, além de ampliar a velocidade de acesso em relação à implementação json original, o que permitia que se fizesse, inclusive, filtros de seleção por nós do objeto JSON contido como valor.

Após a evolução do tipo jsonb no PostgreSQL, Microsoft e Oracle começaram suas implementações de tipos JSON. Para o SQL Server, as implementações de ferramentas para uso, e não apenas armazenamento, em verticalização de dados surgiram apenas nas versões 2016 e 2017. Já a Oracle trouxe um ferramental parecido em sua versão 12c, tornando-o mais amplo na versão 12c Release 2, de 2017. A Oracle também tem evoluído o tipo JSON no banco de dados MySQL, mas seu ferramental ainda é muito restrito para ser aplicado em grandes projetos.

E o jogo vira

Após anos utilizando tecnologias NoSQL como o MongoDB, o mercado começou a se deparar com algumas limitações que estas soluções têm. A dificuldade para ligar documentos entre coleções distintas, agregando uma carga maior de processamento para executar tal ação, ou a ausência de dispositivos de segurança, como criptografia direta dos documentos, por exemplo, acabaram por minar as aspirações destas soluções em dominarem o mercado. Empresas de grande porte, as quais haviam optado pela verticalização de dados utilizando soluções NoSQL acabaram voltando a usar soluções SQL, mas sem voltar atrás na verticalização de dados.

Uma dessas reviravoltas foi anunciada em novembro de 2018 pelo jornal britânico The Guardian. No anúncio feito pelo jornal, eles mostram quais foram os aspectos que os levaram a abandonar o MongoDB e adotar o PostgreSQL como base de dados para os artigos do jornal. Além, claro, da disponibilidade do PostgreSQL no serviço RDS da Amazon, o que já é um grande incentivo, eles demonstram os aspectos técnicos dessa escolha. Com certeza, vale a leitura.

E é baseado nesta escolha, assim como em minhas experiências pessoais em um projeto global para uma empresa líder mundial no setor de inspeções, que demonstrarei, na prática, como aplicar a verticalização de dados em uma base de dados PostgreSQL.

Construindo uma base com verticalização de dados

Uma das principais características da verticalização de dados é prover a multi-tenacidade dos dados em uma base única. Sendo assim, para garantirmos a unicidade dos dados, todas as chaves primárias serão artificiais, utilizando um UUID como valor. Com isso garantimos que nenhum identificador de objetos se repita dentro da estrutura.

Outra característica da verticalização de dados é a possibilidade de desacoplar completamente as fontes de dados para os diversos serviços que precisam ser integrados. Quando desenvolvemos uma arquitetura baseada em micro-serviços, cada micro-serviço pode estar, não só lógica como fisicamente separado, sendo assim, é importante que, sob nenhuma circunstância, os identificadores se repitam.

Então, agora é a hora de por a mão na massa e construir nossa base de dados.

Conhecendo melhor seu elefante

Atualmente, a versão disponível do PostgreSQL é a 14.4, lançada em 16/06/2022. Mesmo sendo uma versão extremamente recente, o ferramental já consolidado desde a versão 9.4 está presente e melhorado. É importante frisar que, para detalhes técnicos sobre o tipo de dados uuid e as funções usadas para gerá-lo, meu artigo sobre multi-tenacidade cobre amplamente estes aspectos do PostgreSQL.

Além disso, vale lembrar que o PostgreSQL contém dois tipos de dados JSON: json e jsonb. Para garantir todos os aspectos da verticalização de dados, utilizaremos o tipo jsonb. Com ele podemos criar filtros através de seus nós, indexação, junções – se bem que, para esta arquitetura, junções não são uma opção interessante -, adição, subtração e alteração de nós.

Existe ainda um recurso muito interessante no PostgreSQL. Com ele, é possível criar heranças entre as tabelas, facilitando a criação e a manutenção de tabelas dentro da sua arquitetura.

E, se você ainda não tem um servidor PostgreSQL instalado, você pode fazer o download dele através do site oficial do projeto.

Ladies and gentlemen, start your engines!

Então, para iniciarmos nossa verticalização de dados, vamos criar um um usuário chamado devspace, dois tablespaces (devspacedevspace_index) e uma tabela que servirá de base quase todas demais tabelas de qualquer sistema computacional. Ela vai conter campos que poderão ser comuns a outras tabelas, por isso vamos criar uma tabela básica, em um schema público, e utilizá-la como base para as demais tabelas. É importante lembrar que os caminhos para os tablespaces já devem existir previamente. No caso abaixo, eles foram indicados para uma instalação Linux e os diretórios foram criados antes da criação dos tablespaces, assim como a propriedade deles foi atribuída ao usuário postgres. Para mais detalhes sobre a criação de tablespaces, é interessante consultar a documentação do próprio banco de dados.

Como é possível perceber, esta tabela possui apenas cinco colunas (idatributoscriacaoatualizacaoativo). Cada uma delas tem uma função específica. Apenas para deixar bem claras estas funções, tomemos as seguintes definições:

  • id: Identificador único do registro;
  • atributos: Campo contendo o objeto JSON com as informações que serão armazenadas;
  • criacao: Data de criação do registro na base de dados;
  • atualizacao: Data de atualização do registro na base de dados;
  • ativo: Indicador booleano de status do registro (ativo/inativo).

E, inacreditavelmente, esses míseros cinco campos podem resolver quase todos os seus problemas. Mas, como estamos pensando em uma arquitetura orientada a serviços, onde cada serviço tem seu próprio domínio de dados, esses são os campos que serão comuns a todos os serviços. E para dar início à construção do nosso modelo, vamos criar  um schema para os cadastros básicos e, nesse schema, vamos criar uma tabela que abrigará os dados básicos comuns a qualquer sistema (CEP, municípios, estados, países, etc.).

Para que a verticalização de dados funcione corretamente, criamos uma tabela que abrigará os tipos de dados que podem ser armazenados no sistema. Nessa tabela, chamada de base.registro_tipo, estendemos a tabela tabela_base, adicionando a ela o nome do tipo a ser inserido – coluna tipo – e o definimos como um campo character varying de 64 bytes. Então, aqui, armazenaremos seu nome (cep, pais, estado, cidade, atc.) e suas definições para validação de esquema. Já para a tabela de registros, onde os registros efetivamente ficarão armazenados, estendemos a tabela tabela_base novamente, adicionando a coluna registro_tipo para abrigar a referência do tipo de dados a ser armazenado e a coluna registro_pai, que faz referência ao registro mestre do registro atual. Se este for o registro mestre, é claro, o valor de registro_pai permanece nulo.

Populando nossa primeira base com verticalização de dados

Depois de criarmos nossa base de dados verticalizada, podemos começar a preencher com dados para entendermos como a verticalização funciona. Então, a primeira coisa a ser feita é preencher a tabela base.registro_tipo com as definições de dados básicas. Vamos brincar e inserir alguns objetos JSON para país, estado e município.

Impontante: os IDs retornados quando sua instrução SQL for executada serão diferentes dos que você verá no artigo, portanto você deve adaptar todos os scripts à sua realidade, ou nenhuma instrução será executada corretamente.

Usando como exemplo o objeto pais descrito abaixo, teremos o schema para validação a seguir.

E, tendo o schema de validação, fazemos o registro da informação na base de dados.

Como resultado desta inserção, temos a definição de país registrada com o ID 1e249e9a-b67e-4e81-a490-2b51130165ae (e sabemos que o ID irá ser diferente a cada insert, então devemos utilizar o ID retornado pela instrução), o qual será utilizado posteriormente para a inserção de uma informação válida na tabela base.registro. Então, para os demais tipos (estado, cidade e cep), usaremos as definições de cada tipo, criando seus schemas de validação.

Comecemos pela definição do objeto estado, seu schema e sua inclusão na base de dados.

Agora, o objeto cidade, seu schema e sua inserção.

E, agora, o objeto cep, seu schema e sua inserção.

Verificando os tipos de verticalização de dados inseridos

Após inserir nossos tipos de dados na tabela base.registro_tipo, podemos verificar se eles foram corretamente inseridos e, também, seus ids correspondentes. Para isso, uma instrução select simples, como a mostrada abaixo, pode nos trazer a resposta.

Verticalização de Dados - Devspace
Verticalização de dados – Tipos de dados inseridos – Devspace

É interessante notar que a estrutura de dados herdada da tabela tabela_base permanece intacta na estrutura de dados, incluindo os valores default para os campos idcriacaoalteracaoativo, uma vez que eles não foram determinados na inserção de cada tipo. E, para não corrermos o risco de repetirmos um tipo com definições diferentes, vamos criar um índice único na coluna tipo, garantindo que o valor contido nela nunca se repita.

Com o índice criado, chegou a hora de iniciarmos a população da tabela base.registro com os dados que usaremos para teste.

Populando a tabela de registros

Para compreendermos mais facilmente a verticalização de dados, é preciso entender que os dados cadastrados são armazenados em uma única tabela. Não importa se as estruturas dos objetos são incompatíveis entre si. O que importa é que temos a definição de dados para distingui-las. Nessa arquitetura, os dados não são separados em tabelas como já vimos muitas vezes em bases horizontais, onde cada objeto da aplicação é armazenado em uma entidade distinta, destinada a ele, no banco de dados. Assim, conseguimos administrar melhor as informações constantes no banco de dados uma vez que, menos tabelas, menos tempo procurando a informação.

Então, para começarmos, vamos inserir um registro de um país (que, por razões óbvias, será o Brasil), dois registros de estados, três registros de cidades por estado e alguns registros de CEPs dessas cidades. Vamos começar pelo país, lembrando sempre de obter o retorno do ID inserido para que ele possa ser utilizado futuramente.

Mais uma vez, utilizamos a estrutura preestabelecida da tabela tabela_base para suprimirmos alguns campos durante a instrução de inserção. E como vimos na imagem anterior, ao registrar o tipo de registro, utilizamos o ID referente ao tipo pais, indicando que o dados inserido é de um país. Apesar de termos registrado seu esquema de validação juntamente com o tipo, ainda não vamos fazer a validação do objeto JSON inserido em atributos, uma vez que este é um assunto a ser abordado futuramente.

Como retorno, a instrução nos trouxe o ID a8e6d59b-99ea-4a62-b9f7-cdb45b33bb13, o qual será usado para inserirmos os estados, conforme instrução mostrada a seguir.

De posse dos IDs dos estados – 1bfad603-3ed5-4fa0-9346-42e4f99310c4 para Paraná e 2cf05c19-cb3b-4647-ac16-0650839b6500 para Santa Catarina -, podemos inserir as cidades na base. Como exemplo, vamos inserir três cidades para cada estado, assim teremos uma boa massa de dados para testes futuros.

Concluída a inserção das cidades, o resultado deve ser próximo do mostrado abaixo, variando apenas os IDs relativos ao país, aos estados e cidades.

Verticalização de dados - Dados inseridos - DevSpace
Verticalização de dados – Dados inseridos – DevSpace

É interessante notar que, como todos os dados estão na mesma tabela, o que pode diferenciá-los é o seu tipo, vinculado por uma chave estrangeira à tabela base.registro_tipo, o que nos permite gerenciar um número muito menor de tabelas e encurta muito nosso tempo de desenvolvimento para o caso de APIs que transportem os dados no formato JSON (APIs RESTful), como já vimos anteriormente aqui. Em um momento futuro abordaremos a parte do desenvolvimento de uma API completa, utilizando esta estrutura de dados.

Por fim, vamos inserir alguns registros de CEP para as cidades que inserimos em nossa base de dados mas, desta vez, usaremos alguns recursos extras do PostgreSQL para cumprir essa tarefa.

Aqui, no momento da inserção dos dados na tabela, ao invés de utilizarmos diretamente o ID do tipo do registro, utilizamos uma instrução select quer retorna seu valor. Isso nos ajuda quando não temos este valor ou quando estamos menos propensos a consultá-lo no banco de dados para inserirmos manualmente na instrução a ser executa. Outro ponto, e essa é uma das partes mais legais da verticalização de dados usando o PostgreSQL, é que também executamos uma instrução select para trazer o ID da cidade à qual o CEP pertence. Contudo, nela, é possível notarmos um operador novo, utilizado pelo PostgreSQL para acessar nós dentro de um objeto JSON. Ele determina qual nó do objeto JSON constante em atributos nós leremos – no caso, nome. Esse operador (->>) é exclusivo para o tipo jsonb no PostgreSQL e mais sobre ele pode ser encontrado aqui.

E, após a inserção, podemos consultar o que já inserimos e ver se tudo corresponde ao que esperamos…

Verticalização de dados - Cidades e CEPs - DevSpace
Verticalização de dados – Cidades e CEPs – DevSpace

Beleza. E agora?

Como uma das grandes vantagens da verticalização de dados no PostgreSQL é o uso nativo de chaves estrangeiras para gerenciar o domínio de informação. Com isso, os níveis desse domínio – ou ramos – podem ser vistos através de uma junção entre o dado que será exibido e seu “registro pai”.

Através desta instrução, obtemos os objetos cidadeestadopais separados em colunas para que possamos manipulá-los na aplicação da maneira como quisermos. Isso é, sem dúvida, muito mais simples que nossa instrução SQL executada lá no início. Lá, são 21 linhas contra 7 da instrução acima, isso contando o mesmo padrão de indentação. Isso simplifica também o resultado, já que todos os dados de cada objeto podem ser vistos separadamente em cada coluna.

A essa altura você deve estar se perguntando: “por que usar essa abordagem?” A resposta é mais simples do que você possa imaginar. Independente da tecnologia que nós vamos utilizar em nosso desenvolvimento, uma coisa é certa: alterações nas entidades causam problemas a serem resolvidos tanto no banco de dados quanto na aplicação. Uma propriedade que seja adicionada, removida ou alterada implica em alterar a tabela que a abriga, assim como suas instruções DML. A aplicação, que já sofrerá o impacto da alteração da entidade, deverá ter, além dos conceitos envolvidos na obtenção, manipulação, tratamento e exibição dos dados, deverá ter suas instruções DML revistas. Se em um servidor de aplicações trabalharemos com objetos JSON sendo transportados entre ele e seus clientes, se torna lógica a opção pelo modelo de verticalização de dados proposto aqui.

Vejamos abaixo como a instrução SQL acima trará os dados para serem consumidos:

Verticalização de dados - Cidade, Estado e País - DevSpace
Verticalização de dados – Cidade, Estado e País – DevSpace

Assim, dentro da nossa aplicação, quando alterarmos qualquer uma das entidades, adicionando, removendo ou alterando propriedades, nenhuma alteração na estrutura do banco de dados se fará necessária, diminuindo – e muito – nosso esforço de manutenção e evolução do projeto.

Mas, com a verticalização de dados, não dá pra trazer tudo num único JSON?

Essa é uma questão muito recorrente quando trabalhamos com verticalização de dados. Como manipulamos objetos JSON dentro de nosso banco de dados, podemos adicionar níveis de domínio de informação infinitos em cada objeto, ampliando sua complexidade. Isso pode trazer vantagens em alguns momentos, como instruções DML simplificadas para serem utilizadas na aplicação, mas se partirmos para uma arquitetura distribuída em micro-serviços, as bases de dados podem estar desvinculadas, onde a informação só deve ser acessada através de sua respectiva API. Sendo assim, mesmo que esse não seja o nosso caso no momento, a verticalização de dados deve ser projetada de forma a poder quebrar o vínculo de informações sem maiores traumas.

Ainda assim, neste nosso projeto, é possível realizar essa tarefa de maneira bem simples, bastando usar um operador de concatenação, o mesmo usado para concatenar strings, e uma função própria do PostgreSQL. Vejamos abaixo como ela ficaria:

É importante frisar que este recurso é ligeiramente mais lento que o utilizado anteriormente, uma vez que o banco de dados precisa manipular o objeto JSON em tempo de execução, mas retorna todo o conteúdo em um único objeto. Vejamos como ficou:

Verticalização de dados - Um único objeto JSON - DevSpace
Verticalização de dados – Um único objeto JSON – DevSpace

As diferenças são pequenas, alterando apenas a parte da instrução SQL que se refere ao que será exibido. Usando o operador de concatenação de objetos JSON podemos unir dois ou mais objetos em um único. Além disso, usamos a função jsonb_build_object para criar um novo objeto JSON a partir das informações fornecidas. Fizemos, ainda, tudo isso de maneira aninhada, fazendo com que pais seja um nó em estado, e que  estado seja um nó em cidade. Capturando o resultado e exibindo como um objeto JSON, teremos o seguinte:

Ou seja, sim, é possível, dentro de uma estrutura de verticalização de dados, obter todo o objeto em uma única instrução de consulta.

E eu já posso usar a verticalização de dados nos meus projetos?

Como costumo dizer: tudo depende. Os conceitos para verticalização de dados são complexos em um primeiro contato, principalmente se você não tem uma equipe madura ou aberta a novas experiências. Isso pode impactar sua equipe negativamente ou, pior, criar uma arquitetura que seja difícil de manter. Então, tenha em mente o seguinte: optar pela verticalização de dados exige maturidade e estudo, e eu só recomendo para novos projetos que estejam em fase de concepção. Para projetos já em andamento, essa camada extra de complexidade pode mais prejudicar seu projeto do que ajudar.

Os próximos passos

Como já vimos como a verticalização de dados funciona em um projeto simples, nos próximos artigos veremos como validarmos, através do próprio banco de dados, as estruturas dos objetos JSON inseridos, assim como o desenvolvimento de uma API que irá interagir com nossa base de dados.

Dúvidas, críticas ou sugestões, é só deixar seu comentário.

Um abraço e até a próxima.

Sobre Willian Tuttoilmondo 12 Artigos
Arquiteto de software com mais de 25 anos de experiência em desenvolvimento de software, especialista em desenvolvimento multicamadas utilizando Embarcadero Delphi e NodeJS para o back-end e o próprio Delphi para o front-end. Usuário Linux desde 1998, é evangelista PostgreSQL, banco de dados no qual é especialista. Ocupa hoje a posição de arquiteto de software na TOTVS, a maior empresa de tecnologia do Brasil, além de ser sócio fundador da LT Digital Labs, empresa especializada em desenvolvimento e treinamentos.

1 Trackback / Pingback

  1. Construindo APIs RESTful nível 3 com Delphi e Datasnap

Faça um comentário

Seu e-mail não será publicado.


*