Toda vez que iniciamos um projeto somos levados a alguns questionamentos quanto a arquitetura e bibliotecas de suporte que serão utilizadas. É possível afirmar que hoje em dia temos algumas bibliotecas que se tornaram unanimidade entre os desenvolvedores, há ainda algumas que dividem este posto com mais uma ou duas tão boas quanto. Porém no que se refere a persistência de dados sempre vemos muitas discussões e uma gama enorme de bibliotecas que se propõe a facilitar os processos, mas a grande verdade é que não existe de fato uma unanimidade, ou pelo menos uma opção que seja bastante satisfatória.

Não podemos ignorar que existem sim algumas boas opções, algumas bastante difundidas inclusive, mas sempre tínhamos novas opções aparecendo que se propunham a melhorar algum ponto fraco das outras existentes, e ao menos aqui na Movile, isso sempre nos levou a testarmos diferentes opções a cada projeto iniciado.
Há um pouco mais de 1 ano atrás, eu comecei o desenvolvimento de um novo projeto aqui na Movile e, como não poderia ser diferente, comecei a pensar a respeito do core do projeto. Já tinha em mente que queria usar RxJava, assim como em ter uma estrutura que me proporcionasse ter diferentes módulos dentro do aplicativo. Foi aí que surgiu a possibilidade de utilizar a Room.

Coincidentemente nessa mesma época de início de projeto, o Google havia acabado de anunciar o Android Architecture e, dentre os componentes, havia o Room que cuidava justamente da camada de persistência dos dados. Apesar de ainda estar em fase beta, resolvemos fazer alguns testes com a biblioteca, e apesar do risco decidimos que seria a nossa opção para o desenvolvimento do projeto.

Pontos positivos

Logo de cara pudemos notar alguns pontos interessantes na biblioteca. O primeiro deles era o fato de a implementação ser extremamente simples e de fácil entendimento – não que isso não existisse em outras opções, mas é um fator muito importante, pois facilita a manutenção do código. A implementação é constituída, em sua maioria, por annotations que definem: as entidades, consultas e definições do próprio database. Após isso, algumas classes são geradas pelo próprio componente, sendo responsável por conter a parte mais verbosa e algumas lógicas para garantir o bom funcionamento do banco. Da mesma forma também podem ser gerados modelos (“schemas”) do banco pra ficar mais fácil de se observar o que foi alterado e como se encontra, por hora, a estrutura das tabelas.

Além disso, a estrutura permitia a criação de diferentes Database que funcionassem de forma independente, isso viria a ser de suma importância ao projeto que possui vários módulos que funcionam, de certa forma, independentes.

As entidades suportam todos os tipos primitivos para tipagem dos campos, além disso, existe a possibilidade de criação de converters, que convertem classes complexas em tipos primitivos os quais o banco suporta. Também é possível relacionar campos a outras entidades, criando assim relacionamento entre as tabelas.

Outra coisa bastante interessante, é a possibilidade de se criar um banco em memória, ainda não precisei usar esta feature, porém creio que existam casos onde isso possa ser de grande utilidade.

O fato de ser uma biblioteca do Google nos traz um dos pontos mais relevantes: a certeza de que a biblioteca será mantida e atualizada frequentemente, além de fazer parte dos Android Architecture Components que deve se tornar em breve a principal estrutura adotada em projetos Android. Alia-se a isso uma documentação muito boa e disponível em várias linguagens, além de um rápido retorno no caso de resolução de bugs. O previamente mencionado suporte aos componentes do RxJava também são importantes, já que a programação reativa vem, cada vez mais, ganhando espaço.

Pontos de melhoria

Como não poderia ser diferente, é possível levantar alguns pontos de melhoria. Não irei focar em bugs pontuais, apesar de saber que existem sim alguns, porque se trata de uma biblioteca, até certo ponto, nova e que certamente será constantemente atualizada e corrigida – inclusive, desde sua versão beta, a atual a biblioteca já foi bastante melhorada.

Tendo dito isto, o primeiro dos pontos que levantaria é: o relacionamento entre entidades. Como comentei anteriormente, a biblioteca possui suporte a isso. No entanto, para casos mais complexos a implementação tende a ficar mais problemática e de difícil processo, bem como as queries um pouco menos inteligente na hora de retornar os objetos consolidados.

Em segundo lugar, porém bem menos importante, na verdade, mais a nível de capricho, seria a parte das migrações do banco. Atualmente, as migrações precisam ser escritas (query) e funcionam muito bem, não tem nenhum maior segredo e nem problemas conhecidos a despeito da implementação, porém creio que isso poderia ser um pouco mais automatizado, principalmente para alterações menores como o acréscimo e remoção de campos. Como as entidades são definidas através de annotations, ela podia também conter informações referentes às migrações ou ao menos algumas classes de suporte para os casos mais comum.

Menos código

Agora sim vamos abordar mais especificamente o que motivou o título deste artigo, e acho que a melhor forma de exemplificar o porquê de menos código é mostrar como seria a implementação de um modelo simples.
Sendo assim, vamos começar pelas entidades. A princípio elas devem ser definidas como um pojo qualquer contendo todos os campos daquela tabela, bem como o tipo de cada um deles. A classe deve ser anotada como `@Entity` e aceita alguns parâmetros como o nome da tabela e um conjunto de campos que vão compor uma chave primária composta `@Entity(tableName = “address”, primaryKeys = {“postal_code”, “number”})`.

Os atributos da classe serão os campos da tabela que serão criados, mas podemos definir um nome personalizado para cada um através de `@ColumnInfo(name = “id”)`, além de podermos definir um campo como @PrimaryKey. De forma que temos a definição de uma tabela de forma bastante concisa.

Print Git

Depois de definir a tabela, vem a definição do DAO, que é uma interface que contém várias funções onde cada uma delas representa uma query diferente. A classe deve ser anotada como `@Dao`, e os métodos podem ser anotados como `@Insert`, `@Update`, `@Delete` ou `@Query`.

Print Git 2

Por fim, vem o Database que deve ser anotado como `@Database`, contendo as informações de entidades daquele Database, assim como a versão do mesmo `@Database(entities = arrayOf(Address::class), version = 1)`, além de implementar a classe RoomDatabase.

Print Git 3

Agora digamos que eu queira fazer um update no app, de forma que a minha tabela de address possua mais um campo de um tipo não suportado pelo room. Sendo assim, além de alterar a minha entidade, preciso criar um converter pro Room conseguir suportar aquele novo tipo.


Print Git 4


Print Git 6

Por último teria que criar a migração no meu database, adicionando a referência ao converter, e executando as queries necessárias para a migração.

Print Git 8

Menos bugs

Na minha concepção, o principal motivo que leva a diminuição dos bugs é pela abstração de boa parte do código que envolve a criação e manutenção do database. Além disso, em tempo de compilação, a biblioteca já faz toda uma validação em cima das entidades e dos dao’s e, em caso de erros, interrompe a build, diminuindo drasticamente o envio de builds problemáticas. Por mais que alguns dos erros não sejam tão claros, a maioria aponta exatamente no que consiste o erro e onde ele se encontra, tornando o desenvolvimento muito mais rápido e eficaz.

A abstração faz também com que a conversão da tabela pra entidade e vice-versa seja feita pela própria biblioteca. Mesmo no caso de tipos de objetos não suportados, temos os type converters que permitem que possamos definir uma forma de converter o tipo não suportado para um suportado de forma bastante simples e segura. Sendo assim, não é necessário, em nenhum momento, a manipulação dos problemáticos cursores que sempre geram uma dor de cabeça muito grande.

Por último, temos as migrations que, apesar de eu ver uma oportunidade para melhoria, possui uma configuração para que, nos casos de migrations mal sucedidas, por qualquer motivo que seja, faça com que aquela tabela seja recriada, o que pode fazer com que o usuário perca alguns dados. Mas, por outro lado não interrompe a utilização do aplicativo.


Conclusão

É sempre complicado falar a respeito de bibliotecas, pois o tema envolve também a questão pessoal de gosto e convicções. Pode ser que você discorde de algum ponto levantado, e se não você, alguma outra pessoa com certeza discorda. Apesar disso, em nossa equipe já passaram alguns desenvolvedores, com diferentes conceitos e estilos, e essa biblioteca tem se mostrado bastante satisfatória a todos eles, para alguns mais, para outros menos. Mas, tendo em vista as demais opções e todos os pontos levantados neste artigo, posso afirmar que não cogitamos mudar isso a curto prazo e, inclusive, temos adotado essa biblioteca para os novos projetos.

Temos também um vídeo com o Gabriel Bertini explicando como foi implementar o Room em um dos nossos projetos. Clique aqui e assista!

 

Posted by:Matheus Fonseca

Deixe seu comentário