12 (Twelve) Factor App é uma metodologia que define 12 boas práticas para criar uma aplicação SaaS moderna, escalável e de manutenção simples.
O conceito de SaaS (Software as a Service) é um tipo de aplicação construída especialmente para ser operada em ambientes em nuvem. São aplicações web nas quais você, como consumidor, não se preocupa com pontos como o provisionamento de máquinas, licenças de software ou fatores de manutenção. Em aplicações SaaS, geralmente o que é paga é uma assinatura, que pode ser mensal ou anual. Através dessa assinatura você terá a possibilidade de acesso ao software.
Além disso, o fornecedor do software é quem é responsável pela manutenção e disponibilidade da solução. Alguns exemplos de aplicações SaaS são o Office 365 e a suíte de serviços do Google (ou Google Apps).
A metodologia dos 12 Factor App foi criada por um grupo de desenvolvedores da Heroku em 2011, visando tornar mais simples os processos de manutenção e expansão de aplicações que são disponibilizadas como serviços, ou aplicações SaaS.
Na primeira parte dessa série de artigos, vamos conhecer os três primeiros pontos do 12 Factor App: Codebase, Dependencies e Config.
Fator 1: Codebase
O código produzido é um ativo importantíssimo no processo de criação de um software, afinal, é a partir dele que o produto final será gerado – no caso, o software em si. Por isso, a maneira com que o código-fonte é gerenciado torna-se um ponto central.
Além disso, outros fatores colocam complicadores no processo de gestão do código-fonte, como equipes cada vez mais multidisciplinares e distribuídas até mesmo geograficamente, e alguns rituais de metodologias ágeis que permitem o trabalho em paralelo de equipes diferentes no mesmo projeto ao mesmo tempo de maneira produtiva.
Pelo 12 Factor App, a base de código-fonte é chamada de codebase. E existem alguns pontos que são essenciais para que possamos ter um codebase coeso e confiável. São eles:
• A base de código deve ser unificada e comum para todos os participantes do processo de criação do software. Isso evita a geração de versões “paralelas” ou “desencontradas” a partir de múltiplas origens de código-fonte;
• A base de código precisa ser temporal e auditável, ou seja, deve ser possível resgatar a situação do codebase em qualquer dia e horário, além de precisar ser possível verificar quem fez as modificações e quando estas foram feitas. Por exemplo: é essencial que seja possível obtermos o codebase na situação em que ele estava há uma semana atrás, por mais que ele tenha sido modificado no decorrer do tempo.
Também é importante ressaltar a relação 1/1 entre aplicação e codebase. Caso uma aplicação esteja espalhada em vários codebases, passamos a falar de um sistema distribuído. E isso não é problema desde que cada parte da aplicação seja independente e tenha seu próprio processo de gestão. Essa é mais ou menos a ideia por trás dos microsserviços, estilo arquitetural muito popular hoje em dia.
Para auxiliar nessa tarefa de gestão de codebase, existem algumas ferramentas chamadas de VCS (Version Control System ou Sistema de Controle de Versão). VCSs permitem justamente ter um codebase centralizado mas compartilhado entre múltiplos times, além de garantir a auditoria e o aspecto temporal do codebase. Neste tipo de ferramenta, o codebase normalmente está localizado em uma estrutura chamada repositório.
Atualmente, o VCS mais utilizado no mercado é o Git. Mas há alguns anos atrás, outros VCSs foram populares, como o Subversion (SVN) e o Mercurial.
Fator 2: Dependencies
Dependências são artefatos externos dos quais sua aplicação depende para ser executada corretamente. Por exemplo, se você está pensando em criar uma API com Node.js, é grande a possibilidade de que você considere utilizar uma biblioteca externa chamada express. Nesse caso, o express é uma dependência do seu projeto.
Quando falamos sobre a gestão das dependências do seu projeto, existem alguns pontos fundamentais segundo o próprio 12 Factor App. O primeiro deles afirma que uma aplicação não pode jamais confiar na pré-existência de alguma dependência dentro do ambiente: é responsabilidade da aplicação delimitar suas dependências de maneira explícita.
Por exemplo, se você está construindo uma aplicação que depende de uma biblioteca ou framework específicos, estes artefatos precisam ser implementados junto com a aplicação. Jamais deve-se esperar que estes artefatos já existam previamente no ambiente de execução. Isso é importante principalmente porque não é muito seguro contar que determinada biblioteca ou framework estejam instalados dentro do ambiente de execução.
O segundo ponto é que, mesmo que uma dependência já exista dentro do ambiente de execução, pode ser que a aplicação precise de uma versão específica que é incompatível com a versão existente no ambiente de execução, o que pode fazer com que o software apresente comportamentos inesperados. Por isso, entra a importância de que cada dependência seja declarada de maneira completamente explícita junto com a aplicação, indicando inclusive a versão a ser utilizada.
Para ajudar nessa tarefa, ambientes de desenvolvimento modernos disponibilizam ferramentas chamadas de gerenciadores de pacote. Cada linguagem e/ou framework possui seu próprio gerenciador de pacotes, como exemplo podemos citar o Java, que comumente utiliza Maven ou Gradle; o .NET usa o Nuget; o Node.js usa o NPM ou Yarn e o Ruby geralmente usa o Rubygems. Cada um destes pacotes provê de maneira integrada ao desenvolvimento a possibilidade de definição explícita, instalação e configuração de dependências.
Fator 3: Configs
As configurações para execução de um projeto são quaisquer parâmetros que podem variar com fatores externos, como o ambiente onde a aplicação será executada, por exemplo.
Geralmente, se tratam de credenciais para acesso a recursos como bancos de dados, buckets de arquivos, chaves de acesso a APIs de terceiros e outros fatores que provavelmente vão variar principalmente devido ao ambiente. Por exemplo, provavelmente, o banco de dados no ambiente de desenvolvimento não será o mesmo que no ambiente de produção, onde provavelmente as credenciais de acesso e até mesmo o host do banco serão diferentes nos dois ambientes. Esse é um clássico exemplo de um aspecto de configuração.
Existem algumas correntes que defendem que estas credenciais sejam definidas diretamente no código, na forma principalmente de constantes. Mas, isso é uma violação do 12 Factor App, pois cada implantação da aplicação exigirá uma modificação no codebase e um processo de implantação coordenado, evitando que a aplicação vá para um ambiente com as constantes erradas. O fator 3 define que deve existir uma separação clara e explícita entre configurações e o codebase, já que o codebase geralmente não varia de acordo com o ambiente, ao contrário das configurações.
Existem diferentes estratégias de gestão de processos de configuração, cada uma delas com suas particularidades e suas aplicabilidades.
Uma das formas mais usadas consiste na definição explícita de arquivos de configurações, arquivos estes que geralmente têm suas respectivas versões para cada ambiente. Se estivermos falando de Java por exemplo, um tipo de arquivo que é muito usado para armazenamento de configurações são os arquivos YAML ou PROPERTIES. Geralmente, a aplicação possui múltiplos desses arquivos, um para cada ambiente específico. A aplicação, ao perceber o ambiente onde está sendo executada, carrega as configurações a partir do arquivo correspondente.
Outra estratégia muito comum que podemos citar é a utilização de variáveis de ambiente para armazenar as configurações. Sendo assim, basta à aplicação ler as mesmas variáveis de ambiente em cada ambiente de execução, sendo que estas variáveis são configuradas de maneira independente em cada ambiente.
Também é bem comum encontrarmos uma estratégia híbrida entre arquivos de configuração e variáveis de ambiente. Por exemplo, posso ter uma aplicação que possui diferentes IDs de cliente para conexão em uma API de um fornecedor, dependendo do ambiente de execução.
Sendo assim, cada arquivo vinculado a cada ambiente pode ter a sua definição de ID de cliente explícita. Mas cada ambiente também pode ter um usuário e senha diferentes para conexão ao banco de dados. Neste caso, cada arquivo de cada ambiente pode apontar para uma variável de ambiente, sendo que esta variável é quem define o nome de usuário e senha.
Dessa maneira, é possível isolarmos credenciais de banco de dados dos arquivos de configuração, evitando a exposição destas credenciais para os integrantes que têm acesso ao codebase.
Por fim, ainda existem aplicações que podem auxiliar no processo de gestão de configurações de maneira muito mais robusta e até mesmo distribuída. Algumas dessas ferramentas são o Chef, o Puppet, o Ansible, o TeamCity e o Consul.
Encerramos por aqui o primeiro dessa série de artigos. No próximo, veremos mais 3 fatores: Backing services, Build, release, run e Processes.
Até lá! :D