Solon Config & Multi-Environment Management: The Layering Model That Fits in Your Head

Iniciado por joomlamz, Hoje at 10:25

Respostas: 1   |   Visualizações: 1

Tópico anterior - Tópico seguinte

0 Membros e 1 Visitante estão a ver este tópico.

Saudações, família tech do **webmastersmz.com**! Aqui fala o vosso especialista de serviço.

Tive o prazer de analisar o tópico **"Solon Config & Multi-Environment Management: The Layering Model That Fits in Your Head"** e, sinceramente, malta, este é o tipo de arquitetura que precisamos de adotar mais nos nossos projetos aqui em Moçambique, especialmente quando queremos escalar soluções de forma profissional.

Para quem não está por dentro, o Solon propõe um modelo de configuração em camadas (layering) que é extremamente intuitivo. Vou destacar os pontos fundamentais que mexeram com a minha cabeça:

### 1. A Simplicidade do "Cabe na Cabeça"
Muitas vezes, lidamos com sistemas de configuração tão complexos (como no Spring ou noutras frameworks pesadas) que acabamos por nos perder em ficheiros `.properties` ou `.yaml` espalhados por todo o lado. O Solon simplifica isto. O modelo de camadas permite que tu entendas, de forma clara, qual configuração está a "mandar" em qual momento.

### 2. Gestão de Multi-Ambientes (Dev, Test, Prod)
No nosso contexto, muitas vezes saltamos do "localhost" diretamente para o servidor de produção, o que é um erro grave. O Solon facilita a separação:
*   **Layer Local:** Configurações base.
*   **Layer de Ambiente:** Onde defines se estás em `dev`, `test` ou `prod`.
*   **Layer Externo:** Configurações que vêm de fora do código (variáveis de ambiente ou propriedades do sistema).

### 3. Prioridade de Carregamento
O ponto forte aqui é a hierarquia. Se definires uma porta no teu ficheiro interno, mas o servidor (via Docker ou System Property) passar outra, o Solon respeita a camada mais externa. Isso dá uma flexibilidade enorme para quem trabalha com CI/CD e infraestrutura moderna.

### 4. Menos é Mais
O autor foca na redução da carga cognitiva. Para nós, webmasters e devs moçambicanos que muitas vezes gerimos vários projetos ao mesmo tempo, ter um modelo que não exige que consultemos a documentação a cada 5 minutos é um ganho de produtividade brutal.

---

**Malta, quero abrir o debate aqui no webmastersmz.com:**
Como é que vocês estão a gerir as vossas variáveis de ambiente hoje em dia? Ainda usam o velho `.env` básico ou já implementam camadas de prioridade como sugere o modelo do Solon? Acham que esta simplicidade ajuda ou sentem falta de algo mais robusto?

Vamos trocar impressões nos comentários! O conhecimento só cresce quando é partilhado entre nós.

Para garantir que os vossos projetos e fóruns rodam sem falhas, convido-vos a conhecer as soluções de alojamento de alta performance da AplicHost em https://aplichost.com. Estamos juntos!

Solon Config & Multi-Environment Management: The Layering Model That Fits in Your Head



Tópico: Solon Config & Multi-Environment Management: The Layering Model That Fits in Your Head
Categoria: Tutoriais | Programação & Tecnologia
Idioma Principal: Português (Conteúdo de Tecnologia)

Descrição do Conteúdo / Informações:
-------------------------------------------------------------------------
One thing I never appreciated until I ran the same jar across dev, staging, and prod is how much of "config pain" is really "config layering pain." Where does a value come from? Which file wins? How do I keep a database password out of Git without wrapping the whole thing in a secrets manager on day one?

Solon has a pretty clean answer to all of this, and it fits in your head. Here's the mental model I use.



The main config file and environment switching


Your application config lives at resources/app.yml (or app.properties). It always loads. That's the base.

Environment-specific files sit next to it with an app-{env} name:

resources/
app.yml          # always loaded (base)
app-dev.yml      # dev overrides
app-pro.yml      # prod overrides

You pick the environment with solon.env. There are four ways to set it, and they matter because they layer from least to most dynamic:

# (1) in app.yml itself
solon.env: dev

# (2) system property
java -Dsolon.env=pro -jar demo.jar

# (3) startup argument
java -jar demo.jar --env=pro

# (4) environment variable (great for containers)
docker run -e 'solon.env=pro' demo_image

The higher the number, the higher the priority. So a value baked into app.yml can be overridden by a -D system property, which in turn can be overridden by a container env var. That ordering is the whole trick — you set safe defaults inside the jar and let the deployment environment push the real values on top.

One rule worth remembering: Solon will automatically load app-{env}.yml, but a file loaded because of an environment can't itself declare a new environment. No recursive env-hopping.



Loading more config than just app.yml


Real apps outgrow a single file. You want datasource config in one place, auth in another, maybe a shared common block. Solon gives you solon.config.load for that (an array form), and it understands path expressions:

solon.config.load:
- "classpath:${solon.env}/jdbc.yml"     # env-scoped internal file
- "classpath:${solon.env}/*.yml"        # wildcard (internal only)
- "file:common/*.yml"                   # wildcard (external only)
- "docs.yml"                            # external first, else internal

The ${solon.env} interpolation means one line covers every environment. Notice the prefix semantics:


classpath:xxx — resource inside the jar


file:xxx — external file next to the jar


xxx (no prefix) — try external first, fall back to the resource

If you'd rather specify extra config at launch instead of in a file, there's a single-value sibling, solon.config.add, aimed at runtime:

java -jar demo.jar --solon.config.add=./demo.yml
# or
java -Dsolon.config.add=./demo.yml -jar demo.jar

The convention I've settled on: put anything ops needs to change at deploy time into an external file (solon.config.add / file: load), and keep everything else internal. It keeps the jar portable and the surface for accidental prod edits small.



The full loading order


When two files set the same key, who wins? Solon defines six layers, and the rule is simple: the more static, the earlier; the more dynamic, the later — and later overrides earlier, key by key.


L1 — Main config: internal app.yml and app-{env}.yml


L2 — Internal extension files: added via solon.config.load


L3 — External local files: added via solon.config.add


L4 — Dynamic config: environment variables and system properties (any solon-prefixed env var is synced into both system properties and Solon.cfg())


L5 — Startup interface loading: Solon.cfg().loadAdd(...) / loadEnv(...) in your bootstrap


L6 — Cloud config: e.g. Nacos via a Solon Cloud plugin

Once you internalize "later beats earlier," almost every "why is this value not what I set?" question answers itself. Your prod container's env var (L4) will always win over the default in app.yml (L1).

There's also a code path for L5 if you want programmatic control:

public class App {
public static void main(String[] args) {
Solon.start(App.class, args, app -> {
app.cfg().loadAdd("file:config/demo.yml");
// load env vars by prefix — handy when building a docker image
app.cfg().loadEnv("demo.");
});
}
}



Getting config into your code


Two ways: inject it, or fetch it.

Injection uses @Inject with a ${...} expression:

@Component
public class DemoService {
// single value with a default
@Inject("${track.name:demoApi}")
String trackName;

// a whole structured block bound to an object
@Inject("${track.db1}")
Properties trackDbCfg;

// Solon can even build a bean from a config block
@Inject("${track.db1}")
HikariDataSource trackDs;
}

Bind a config section to a typed class so you can reuse it anywhere:

@Inject("${user.config}")
@Configuration
public class UserProperties {
public String name;
public List<String> tags;
}

// elsewhere
@Inject
UserProperties userProperties;

Since v3.0.7 there's also @BindProps(prefix=...) if you prefer prefix binding over an explicit expression.

Prefer the manual route sometimes? Solon.cfg() gives you direct access:

String trackName   = Solon.cfg().get("track.name", "demoApi");
Properties dbCfg   = Solon.cfg().getProp("track.db1");
HikariDataSource ds = Solon.cfg().getBean("track.db1", HikariDataSource.class);

A couple of details that saved me time:

• If the expression name is all uppercase and no matching config key exists, Solon tries an environment variable instead. So @Inject("${JAVA_HOME}") reads the env var. Nice for container-first setups.


autoRefreshed = true makes an injected field track live config changes — but only enable it on singletons and field injection. On a non-singleton it makes no sense, so leave it off.

For anything more reactive, you can subscribe to changes directly:

Solon.cfg().onChange((key, val) -> {
if (key.startsWith("track.name")) {
// react to the change
}
});



Keeping secrets out of plain text


Multi-environment config eventually runs into "I don't want the prod DB password sitting in a yml in the repo." Solon's lightweight answer is solon-security-vault. It's not a full secrets vault — the docs are honest about that: it desensitizes values so they aren't sitting in the open, it doesn't make them uncrackable. For keeping a password out of a plain-text file, that's usually the right amount of protection.

<dependency>
<groupId>org.noear</groupId>
<artifactId>solon-security-vault</artifactId>
</dependency>

You set a vault password (16 chars, mixed case + digits recommended), ideally passed at launch rather than committed:

java -Dsolon.vault.password=xxx -jar demo.jar

Generate ciphertext with the utility:

System.out.println(VaultUtils.encrypt("root"));

Then store the encrypted values wrapped in ENC(...):

solon.vault:
password: "liylU9PhDq63tk1C"

test.db1:
url: "..."
username: "ENC(xo1zJjGXUouQ/CZac55HZA==)"
password: "ENC(XgRqh3C00JmkjsPi4mPySA==)"

And inject with the dedicated @VaultInject, which decrypts as it binds:

@Configuration
public class TestConfig {
@Bean("db2")
DataSource db2(@VaultInject("${test.db1}") HikariDataSource ds) {
return ds;
}
}

Need to decrypt by hand? VaultUtils.guard(...) handles both a config block and a single value.



How I'd put it together


For a typical service across three environments:


app.yml holds structure and safe defaults, plus solon.config.load lines that pull in ${solon.env}-scoped datasource and auth files.


app-dev.yml / app-pro.yml carry the environment differences.

• Real secrets go through the vault as ENC(...), with the vault password passed via -D at launch.

• The environment itself is selected by a container env var (solon.env=pro), which sits high in the loading order and wins cleanly.

The nice part is that none of these pieces are special-cased. It's one layering rule — more dynamic wins — applied consistently from a yml key all the way up to a Nacos value. Once that clicks, config stops being a source of surprises.

If you want to go deeper, the official docs on config loading order and path expressions are worth a read: they spell out every layer with examples.


Joomlamz
Consultoria em Informática
-------------------------------------------------------
Especialista em Sistemas Web & Manutenção de Servidores.
A desenvolver o novo AplPortal com suporte a PHP 8.
Precisa de ajuda profissional? Contacte-me.

Tags: