Bevezetés
A gpsystem 12 önálló Go-modul plusz egy vékony generátor/kit, ami ezeket összeköti:
- 12 önálló modul, mindegyik saját Go-modul, külön verziózva és publikusan tagelve (
v0.1.0), önmagában is használható bármely Go-programban:errs(stackelhető hibák),envconf(típusos konfiguráció),httperr(+httperr/validate),dbx(+dbx/pg,dbx/bunx,dbx/seed),paginate,queue,events(+events/outbox,events/scheduler),auth(+auth/rbac,auth/policy),mail(+mail/smtp,mail/mjml),notify(+notify/database,notify/broadcast),storage(+storage/local,storage/driver/s3),telemetry(+telemetry/logx,telemetry/otelx,telemetry/sentryx). - Egy vékony kit (
github.com/gp-system/gpsystem): négy chassis-csomag, ami futó service-szé komponálja a modulokat (appéletciklus,serverchi/net-http-n,workerasynq-on,realtimecentrifuge-on), plusz egy CLI (go tool gpsystem), ami projektet, modult, surface-t, handlert, eventet, listenert és jobot vázol fel, a hozzájuk tartozó TypeSpec kontraktusfájlokkal együtt.
Miért létezik
A Go-ökoszisztéma ereje a kis, egymástól független, jól tesztelt library-kben van. Cserébe minden projekt elölről végigcsinálja ugyanazt a tíz döntést: melyik logger, hogyan néz ki a graceful shutdown, hogyan modellezed a hibákat, hogyan lépi át egy tranzakció a repository-határokat. A gpsystem ezt a munkát végzi el egyszer, mindenki helyett: a bevált Go-library-ket húzza össze egyetlen, karbantartott kompozícióba, szétosztva a fenti 12 modul közt, ahelyett hogy sajátot írna helyettük.
PHP-s háttérrel érkezel? A Laravelből érkezőknek és a Symfonyból érkezőknek oldal fogalomról fogalomra megfelelteti a két világot.
A modulok lefedik az összes alapfunkcionalitást, amivel a legtöbb oldalon találkozol, a kit pedig futó service-szé köti össze őket:
- RBAC: szerep- és permission-alapú hozzáférés-vezérlés
- Policy réteg, amikor az RBAC nem elegendő: kérésenkénti tulajdonos- és állapotellenőrzés
- Graceful shutdown: in-flight kéréseket kiváró, rendezett leállás
- Ütemezés: cron és intervallum, replikabiztosan
- Háttér-workerek: asynq-alapú feldolgozó bináris
- Skálázhatóság: a tranzakciós outbox és a scheduler leader electionje több replikán is helyesen viselkedik
- Tranzakciók: contextben utazó, kereszt-repository tranzakciókezelés
- Hálózati fájltárolás: S3-kompatibilis object storage (AWS, MinIO, RustFS)
- Részletes metrikák: HTTP-, adatbázis- és queue-instrumentáció OTLP-n
- Monolog-szerű dev logolás: PHP-ról ismerős, olvasható konzollog fejlesztés közben
- Sentry: hibakövetés,
errs-kód szerinti issue-csoportosítással - OpenTelemetry: trace-ek, metrikák, logok egy hívással
- Validátor: struct-tag-alapú kérésvalidáció, RFC 9457 hibaválaszokkal
- Típusos konfiguráció: env-változók struct-okba töltve,
.env-támogatással - Stackelhető hibakezelés:
fmt.Errorf-ként komponáló hibák stackkel, hibakóddal és kliensbiztos üzenettel - Migrációk: beágyazott SQL, saját
cmd/migratebinárissal - Kódgenerálás: TypeSpec-kontraktusból típusos szerverkód
Mindegyik egyszer van megírva és tesztelve a kitben; a projektjeid egyetlen go.mod-verzió emelésével frissülnek.
Mi NEM a gpsystem
- Nem framework. Nincs IoC-konténer, nincs kötelező Module-interfész, nincs kit-tulajdonú lifecycle-hook, amit egy bootstrap felfedez és meghív. A
main()-t te írod; minden függőség kézzel, explicit kóddal kerül be. A kit egyetlen inverziója a folyamat-életciklus (Run(ctx, cfg, register)): jelez, leállít, flush-öl, a business-wiringhez nem nyúl. Lásd az Architektúra oldalt, hol húzódik ez a határ. - Nem platform. A gpsystem nem birtokolja az infrastruktúrádat: nincs saját deploy-modell, saját felhő, saját runtime; a
chi.Router, a pgx pool a tiéd marad. - Nem ORM. Nincs Eloquent- vagy Doctrine-szimuláció; a repository-réteg SQL-t (pgx) vagy egy query buildert (bun) ír, explicit lekérdezésekkel.
- A generált kód a tiéd. Amit a CLI felvázol, azt úgy szerkeszted, mint bármely más kódot, kivéve a szűk, kimondottan újragenerált zónát (a TypeSpec→OpenAPI→oapi-codegen transport-stubok a
gen/alatt), amit nem kézzel írsz, hanem a specifikációt módosítod.
Nem rejti el a függőségeket
A gpsystem elve: centralizál, nem absztrahál. A külső függőségekhez való hozzáférés egy-egy modulban összpontosul, de a típusaik szabadon átfolynak a publikus API-n:
- a
server.Runregister-függvényechi.Router-t ad: a handlereid a natívnet/httpAPI-t használják; - a
dbx/pgpgx.Rows-t éspgxpool.Pool-t ad vissza, adbx/bunxbun.DB-t; - a
queue*asynq.Task-kal dolgozik, a worker escape hatch-ei (WithAsynqConfig,WithMux) nyers asynq-ot adnak.
Az upstream dokumentáció és a Stack Overflow-válaszok emiatt nálad is érvényesek maradnak, és nem egy kit-specifikus wrapper-API-t kell megtanulnod. Három helyen tér el ettől a gpsystem, mindháromszor kimondott okkal:
- a
storagemodulDriver/Diskfelosztása elrejti az AWS SDK-t egy kis backend-kontraktus mögé: az app-kódodnak így nem kell az AWS SDK-t importálnia egy fájlfeltöltéshez; - a
telemetry/sentryxbecsomagolja a sentry-go-t: a Sentry env-ből kapcsolható (SENTRY_DSNüresen = teljesen kikapcsolva), és aki nem használja, annak a binárisába be sem linkelődik; - a
mailMailerinterfésze mögött él a go-mail és az mjml-go, így tesztben memória-mailerre cserélhető.
A részek cserélhetőek és elhagyhatóak: a dbx/pg használható a server nélkül (pl. egy CLI-eszközben), a Sentry és az OTel env-vel kikapcsolható, a pgx/bun importtal választható, minden modul a többi nélkül is használható. Az Architektúra oldal modulonként mutatja, mi mitől függ.
A ~15 soros main.go
Egy modul belépési pontja ennyi (a graceful shutdown, a telemetria, a hibarenderelés és a validáció már be van kötve):
func main() {
cfg := envconf.MustLoad[config.Config]()
ctx := context.Background()
pool := pg.MustNewPool(ctx, cfg.DB)
err := server.Run(ctx, cfg.Server, func(r chi.Router) error {
api := chi.NewRouter()
deps := shop.Dependencies{
DB: pg.NewDB(pool),
Transactor: pg.NewTransactor(pool),
}
shop.RegisterApi(api, deps)
shop.RegisterAdmin(api, deps)
r.Mount("/api/v1", api)
return nil
}, server.WithCloser("pgxpool", func(context.Context) error {
pool.Close()
return nil
}))
if err != nil {
log.Fatal(err)
}
}
Ez a shop mintaalkalmazás belépési pontja. A dokumentáció példái végig erre a projektre épülnek.
Mi marad a te kezedben
- A router API-ja natív marad. A handlereid a
net/httphttp.ResponseWriter/*http.Request-jét kapják (vagy egy generált handlerben a codegen típusos request/response-át); aserver.Runmiddleware-t és életciklust köt be, de nem csomagolja be a routingot vagy a kéréskezelést. Amit achiés anet/httptud, azt az appod is tudja. - A generált kód a tiéd. A váz a te repódban landol, és úgy szerkeszted, mint bármely más kódot. A generátorok soha nem írnak felül meglévő fájlt, és csak megjelölt anchor-pontokhoz (
// gpsystem:*) nyúlnak. Nincs runtime, ami a projektedet értelmezné. - Szándékosan vékony. A
server.Runasignal.NotifyContext, a listen, a drain és egy rendezett cleanup-lista kompozíciója (az engine-agnosztikusappéletciklus), pont az a kód, amit egyébként service-enként megírnál, csak egy helyen karbantartva.
Egy fogyasztó projekt alakja
Minden modul saját belépési pontot kap (cmd/<modul>/main.go) és saját processzként fut. Külön vagy együtt deployolod, ahogy akarod. Az API kontraktus spec-first: a TypeSpec OpenAPI 3.0-ra fordul, abból szigorú, típusos szerver-interfész generálódik chi felett. A handlered egy generált interfészt implementál, így a spec-eltérés fordítási hiba.
Merre tovább
- PHP-s háttérrel érkezel? A Laravelből érkezőknek és a Symfonyból érkezőknek oldal fogalomról fogalomra megfelelteti a két világot.
- A shop mintaalkalmazás megmutatja, hogyan áll össze mindez egy projektté.
- Vagy vágj bele rögtön a Telepítéssel.