Koncepciók

Konfiguráció

Egy projekt konfigurációjának összeállítása modul-Config structokból: envconf, env-prefix konvenciók és a .env.

A gpsystemben a konfiguráció betöltése egyetlen lépés: env-változók egy típusos Go structba töltve, egyszer, a processz indulásakor. Nincs config() helper és nincs config-cache: a struct maga a cache, és az elgépelt kulcs nem null-t ad futás közben, hanem fordítási hibát.

Ez a 12-factor "config a környezetből" elve: a konfiguráció a processz indulásakor, a környezetből olvasódik be, és a bináris minden környezetben (dev, staging, production) ugyanaz, csak az env más. Maga a betöltő mechanizmus, az envconf, önálló modul (github.com/gp-system/envconf), semmilyen gpsystem-specifikus tudás nélkül: ez az oldal arról szól, mit csinál egy generált projekt ezzel, hogyan komponálja egy tucatnyi modul beállításait egyetlen structba.

Struct-kompozíció

Minden önálló modul saját env-tagelt Config structot exportál, a saját mezőit nevezve, anélkül, hogy tudná, mi (ha egyáltalán) fogja prefixelni. Egy generált projekt ezeket a structokat egyetlen gyökér-Config-ba komponálja envPrefix-szel:

internal/platform/config/config.go
package config

import (
    "github.com/gp-system/dbx"
    "github.com/gp-system/events/outbox"
    "github.com/gp-system/gpsystem/server"
    "github.com/gp-system/gpsystem/worker"
    // gpsystem:imports
)

type Config struct {
    Server server.Config
    DB     dbx.Config `envPrefix:"DB_"`
    Worker worker.Config
    Outbox outbox.Config `envPrefix:"OUTBOX_"`
    // gpsystem:config
}
cmd/shop/main.go
cfg := envconf.MustLoad[config.Config]()

A kompozíció szabálya egyszerű: egy modul a saját mezőit nevezi, a beágyazó dönti el a prefixet. A dbx.Config mezői HOST, PORT, USER, ...: a envPrefix:"DB_" alatt ezek DB_HOST, DB_PORT, DB_USER lesznek. A prefix nélkül beágyazott structok (server.Config, worker.Config) standard, top-level neveket olvasnak.

A beágyazás mélyebbre is megy: a server.Config maga is beágyazza a telemetry.Config-ot, ami a telemetry/otelx + telemetry/logx + telemetry/sentryx konfigját fogja össze: mind prefix nélkül, mert ezek iparági standard nevek (OTEL_SERVICE_NAME, OTEL_DEV_MODE, LOG_LEVEL, SENTRY_DSN), amiket minden OTel-/Sentry-kompatibilis eszköz ugyanígy vár. A worker.Config pedig a Valkey- és scheduler-konfigot prefixeli (VALKEY_ADDR, SCHEDULER_TIMEZONE).

Az env-prefix konvenciók

A modulok közt, amiket egy projekt ténylegesen bedrótoz, a prefixek egy konzisztens készletté állnak össze:

PrefixModulPélda változók
(nincs)a kit server-jeLISTEN_ADDR, SHUTDOWN_TIMEOUT
(nincs)a kit worker-jeWORKER_CONCURRENCY, WORKER_QUEUES
(nincs)a kit realtime-jaREALTIME_LISTEN_ADDR, REALTIME_CHANNEL_PREFIX
DB_dbxDB_HOST, DB_USER, DB_PASSWORD
JWT_authJWT_SECRET, JWT_ACCESS_TOKEN_TTL
MAIL_mail/smtpMAIL_HOST, MAIL_FROM_ADDRESS
VALKEY_queueVALKEY_ADDR, VALKEY_PASSWORD
OUTBOX_events/outboxOUTBOX_POLL_INTERVAL, OUTBOX_BATCH_SIZE
SCHEDULER_events/schedulerSCHEDULER_TIMEZONE, SCHEDULER_LEASE_TTL
S3_storage/driver/s3S3_ENDPOINT, S3_BUCKET
(nincs, standard)telemetry (otelx/logx/sentryx)OTEL_*, LOG_*, SENTRY_*

A shop env-felülete, több fentiből összerakott részlet:

.env
# server.Config: prefix nélkül
LISTEN_ADDR=:3000
SHUTDOWN_TIMEOUT=10s

# telemetry (a server.Config-ba ágyazva): standard nevek
OTEL_SERVICE_NAME=shop
OTEL_DEV_MODE=true
LOG_FORMAT=monolog
SENTRY_DSN=

# dbx.Config: DB_ prefixszel
DB_HOST=localhost
DB_USER=shop
DB_PASSWORD=secret
DB_NAME=shop

# worker.Config: WORKER_* és VALKEY_*
WORKER_CONCURRENCY=10
VALKEY_ADDR=localhost:6379

# outbox.Config: OUTBOX_ prefixszel
OUTBOX_POLL_INTERVAL=1s
Sem egyik modul, sem a kit egyetlen része sem vezet be saját env-namespace-t. Ahol létezik iparági standard név (OTEL_*, SENTRY_*), ott azt használja változatlanul; ahol nem, ott rövid, prefixelt neveket (DB_*, VALKEY_*, WORKER_*). Így a deployod ugyanúgy néz ki, mint bármely más OTel-instrumentált appé. A teljes változó-tábla, generált projektenként: Konfiguráció-referencia.

A .env fájl betöltési sorrendje

Parsolás előtt, best-effort jelleggel, az envconf godotenv-en keresztül betölti a munkamappa .env fájlját, ha létezik: meglévő környezeti változót sosem ír felül, tehát a .env csak kitölti, amit a processz környezete még nem adott meg. Ez az egyetlen szabály teszi lehetővé, hogy ugyanaz a Config struct minden kontextusban helyesen viselkedjen:

  • Lokális fejlesztés: a shellben semmi nincs exportálva; a generált projekt egy .env.example-t szállít, amit .env-re másolsz, és minden érték a fájlból jön.
  • A dev docker-compose stack: a mise run dev elindítja a compose service-eket (Postgres, Valkey, Mailpit, RustFS) és az app-konténert ugyanazzal a .env-vel; a fájlban lévő service-hostnevek (DB_HOST=postgres, VALKEY_ADDR=valkey:6379) a compose belső DNS-ére oldódnak fel, nem localhost-ra. A teljes stackhez lásd a Telepítés oldalt.
  • Staging/production: a valódi környezet (egy systemd unit, egy Kubernetes ConfigMap/Secret) közvetlenül állítja be a változókat; általában nincs is .env fájl, és ha lenne is, a már beállított változók akkor is nyernének.

Az envconf.MustLoad a main() első sora: hiányzó required változónál a hiba a változó nevével bukik ki, induláskor. Egy service sem bootol félig konfigurálva, hogy aztán az első kérésnél derüljön ki, mi hiányzik. Magához a Load/MustLoad/LoadPrefixed-hoz lásd az envconf: Áttekintés oldalt, a fenti alapesetnél tovább menő kompozíciós és tesztelési mintákhoz (multi-instance kapcsolatok, t.Setenv tesztekben) pedig az envconf: Receptek oldalt.

A konfiguráció mint explicit függőség

A gpsystemben a konfiguráció a main()-ben egyszer materializálódik, és onnan explicit paraméterként utazik tovább (cfg.Server a server.Run-nak, cfg.DB a pg.MustNewPool-nak, a Dependencies structon át a moduloknak). Egy service soha nem nyúl a környezethez futás közben. Amit kap, az a boot-kori pillanatkép, ami tesztben tetszőlegesen konstruálható. Elgépelt mező esetén fordítási hibát kapsz, hiányzó required env esetén pedig hibát boot-kor, a változó nevével, sosem csendes null-t futás közben.

Kapcsolódó oldalak

Copyright © 2026