Como organizar o Spring Boot Application Properties – Monofile e Multiple Profiles

Quando trabalhamos com o Spring Boot uma das principais necessidades é parametrizar a nossa aplicação. Isso pode se tornar um caos se não seguir algum padrão e alguma organização.

Nesse post, vamos falar sobre o que pra mim é o “melhor” modelo de como organizar essas configurações.

Não vou entrar na discussão sobre YAML vs Properties, já tem 2 textos falando somente sobre esse assunto:

https://www.baeldung.com/spring-yaml-vs-properties

https://www.baeldung.com/spring-boot-yaml-vs-properties

Monofile (arquivo único) e Multiple Profiles (separação por profiles).

Minha preferencia é trabalhar no formato de Monofile com Multiple Profiles, vou explicar primeiro como é o formato e depois os motivos que me fazem acreditar nele ser melhor.

Supondo que temos o arquivo src/main/resources/application.yaml no nosso projeto, podemos ter o seu conteúdo como exemplo:

spring:
  profiles:
    active:
      - local

custom:
  filepath: /tmp/app/out.txt

---

spring:
  config:
    activate:
      on-profile: local
servers:
  - localhost:8081
  - localhost:8082

---

spring:
  config:
    activate:
      on-profile: test
servers:
  - www.abc.test.com
  - www.xyz.test.com
  
---

spring:
  config:
    activate:
      on-profile: dev
servers:
  - www.abc.dev.com
  - www.xyz.dev.com

custom:
  filepath: /opt/files/myfiles.out

---

spring:
  config:
    activate:
      on-profile: prod
servers:
  - www.abc.com
  - www.xyz.com

custom:
  filepath: /opt/files/myfiles.out
    

Analisando o arquivo, o que podemos observar é a propriedade: spring.config.activate.on-profile (link):

---

spring:
  config:
    activate:
      on-profile: NOME_DO_PROFILE

---

Esta propriedade é quem defini o nome para este bloco de profile e consequentemente o que será carregado quando ele for acionado.

Os caracteres --- fazem o trabalho de dividir os blocos, o que permite separar a nossa estrutura dentro do mesmo arquivo.

Ou seja, o que estiver dento dos --- é carregado de acordo com o nome utilizando em spring.config.activate.on-profile.

Desta forma, podemos trabalhar com 2 tipos de configurações:

1 – Uma configuração para cada profile (somente especialização):

Como exemplo, temos a propriedade servers:

servers:
  - XXX
  - YYY

Que para cada um dos profiles existentes, faz se o carregamento de uma configuração especifica (especializada) para a propriedade, assim o valor de servers muda a cada tipo de profile.

2 – Global e especialização:

O outro modelo é o que podemos observar para a propriedade custom.filepath:

custom:
  filepath: XXX

Onde temos uma configuração “global” por não estar vinculada a nenhum spring.config.activate.on-profile, mas nos profiles de dev e prod ela recebe outro valor (sendo sobrescrita).

Carregando o profile escolhido.

Para carregar o profile que se deseja, basta exportar a variável SPRING_PROFILES_ACTIVE:

export SPRING_PROFILES_ACTIVE=dev

Ou passar diretamente como parâmetro -Dspring.profiles.active:

java -jar -Dspring.profiles.active=dev application.jar

Se estiver usando uma IDE (e é aqui que a maioria dos desenvolvedores param de usar esse modelo por não entender como usar)(link), você precisa fazer a configuração de exportar a variável ou de passar o parâmetro na inicialização do projeto pela própria IDE. (Por favor, pare de matar uma tecnologia pela sua falta de conhecimento)

Defendendo este formato

Agora vou colocar os pontos que acredito serem os mais importantes em defesa deste modelo:

1 – Arquivo único, um local para ler;

Começo com o obvio, um local para procurar, um local para ler.

Quando esta se programando a aplicação a primeira vez, é tranquilo estar no dia a dia vendo os arquivos e lidando com as variáveis. Mas se você não vive pulando de empresa para empresa no fim de cada projeto, sabe o quão difícil é depois de algum tempo sem olhar para um projeto tentar relembrar dele. E nem digo “anos” depois, pra mim, 3 meses depois eu já não me lembro de nada.

Por isso, manter um padrão de só 1 local, facilita em muito saber onde procurar.

2 – Global e especialização;

Este é o que mais utilizo e principalmente facilita em muito, estando em um único arquivo.

Para mim fica muito fácil de entender as variáveis, pois eu sei que o que esta no começo do arquivo é o conteúdo global e depois eu vou no profile especifico ler a sua especialização.

Exemplo mais pratico, eu configuro o numero de conexões com o banco de dados no global para 3, e depois na produção para 10.

É fácil saber que esse parâmetro existe e o quanto ele quando se sobrescrever.

3 – Diminuir a chance de “Ops! Esqueci de …”;

Como temos o conceito de global, podemos fazer todas as configurações necessárias para o funcionamento básico nele, e depois especializar com o tempo e com a necessidade.

Isso diminui em muito parte do “Ops! esqueci de: ” (pic one or any)

  • “configurar o nome da fila”
  • “configurar o timeout”
  • “configurar o endereço do outro serviço”
  • “configurar o nome do arquivo”
  • “configurar o level do log”

4 – Ter um profile padrão;

Não que não de pra fazer de outra forma, mas é mais uma da linha o “obvio precisa ser dito”.

Nessa formato, você deve setar um profile para ser o padrão de inicialização, eu recomendo sempre ser o local, e assim quando rodar ele já “configura” um ambiente de forma “conhecida” em qualquer lugar.

O bom disso, é que em local, sempre vai carregar como local, exceto quando se forçar.

Para qualquer outro lugar, precisa forçar não ser o local, sendo assim, evitando de rodar “errado” nos outros lugares. Tipo, rodar prod em hml por não ter o valor setado em hml.

5 – Git commit.

O ultimo é sobre o argumento de falar que no Git você não vai ver a alteração no arquivo com o nome de -dev ou -hlm, sempre vai ser alterar o “raiz”.

Eu até entendo que estragando o raiz afeta todos os profiles. Eu até entendo que pode se isolar.

Mas eu prefiro aceitar esse risco de ter um arquivo só, de conseguir ver pela linha onde esta a alteração.

Pra mim, é um argumento muito “fraco” para sustentar toda a complexidade de ficar tendo que abrir múltiplos arquivos. Porque ter múltiplos arquivos geram outros problemas.

Se você já trabalha com múltiplos arquivo, da uma olhada pra ver se não tem dados duplicados ou “lixo” guardado?

Também tem a questão de que múltiplos arquivos não cancela o funcionamento de “global e especialização”, só deixa ainda mais confuso saber qual é a ordem do carregamento e qual é o dado final depois do “merge”. E desta foram, tem que saber bem qual é a ordem de carregamento do Spring Boot para saber o valor final (link).

Explicando os profiles

Fazer um adendo aqui para explicar o meu entendimento do que é cada um dos “nomes” dos profiles significa:

  • local: Maquina do desenvolvedor, quando você roda a aplicação dentro da IDE;
  • test: Ambiente (servidores) de teste, ambiente que roda os test unitários e ou integrados da aplicação;
  • dev: Ambiente (servidores) de desenvolvimento, onde roda as aplicações durante o seu desenvolvimento, geralmente serve somente como base para as aplicações que estão rodando local tenha um serviço a quem chamar, ou seja, serve de apoio ao desenvolvedor;
  • hml: Ambiente (servidores) de homologação, onde se homologa (testa o funcionamento) de uma aplicação por alguém que não é o desenvolvedor;
  • prod: Ambiente (servidores) de produção, onde o usuário final utiliza.

Bom, é isso. Até o próximo post.