Help us shape the future of Go! Take the 2026 Go Developer Survey

vormia-go-core

module
v0.0.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 28, 2026 License: MIT

README

Vormia Go Core

Foundation layer for the Vormia ecosystem — dependency injection, config, logging, events, lifecycle, context, and errors.

Status: pre-release v0.0.1 (versioned by git tags; not a stable 1.x API yet).
Module: github.com/vormialabs/vormia-go-core · Go: 1.21+ · Deps: standard library only.

Deep dive: aiguide/GUIDE.md · AI / contributor rules: aiguide/CURSOR_CODEX_MCP_GUIDE.md

What it provides

Package Purpose
container DI: singleton + transient services, ServiceProvider
config Key/value store from .env files and OS environment
logger Leveled logging (stdout, writer, or file)
events Sync + async publish/subscribe
lifecycle App with ordered boot/shutdown hooks and signal handling
context Typed context keys + mutable request ValueBag
errors Wrap / Exception / pluggable Handler

No HTTP, database, or cache here — those belong in higher Vormia layers.

Installation

go get github.com/vormialabs/vormia-go-core@v0.0.1

Requires the tag to be published on the remote. Check available versions:

git ls-remote --tags https://github.com/vormialabs/vormia-go-core.git
go list -m -versions github.com/vormialabs/vormia-go-core

Quick start

Thin main: create App, load config, register hooks, boot, wait for signal.

package main

import (
    "os"

    "github.com/vormialabs/vormia-go-core/lifecycle"
    "github.com/vormialabs/vormia-go-core/logger"
)

func main() {
    app := lifecycle.NewApp()

    // File first, then OS env (OS wins).
    _ = app.Config().Load(".env") // missing file is OK
    app.Config().LoadEnv()

    app.Logger().SetLevel(logger.ParseLevel(
        app.Config().GetString("LOG_LEVEL", "info"),
    ))

    app.OnBoot(func() error {
        return app.Config().Require("APP_NAME")
    })
    app.OnShutdown(func() error {
        // close pools, flush loggers, etc.
        return nil
    })

    if err := app.Boot(); err != nil {
        app.Logger().Error("boot failed", err, nil)
        os.Exit(1)
    }

    // Blocks on SIGINT/SIGTERM, then runs Shutdown in reverse order.
    _ = app.WaitForSignal()
}
Dependency injection
c := container.New()

// Singleton — factory runs once.
_ = c.Register("userRepo", func() interface{} {
    return &UserRepository{}
})

// Transient — fresh instance every Resolve.
_ = c.RegisterTransient("requestID", func() interface{} {
    return newID()
})

repo, err := c.Resolve("userRepo")
if err != nil {
    // match *container.ErrNotFound / *container.ErrCyclicDependency with errors.As
    panic(err)
}
userRepo := repo.(*UserRepository)

Wire modules with ServiceProvider (Register then Boot via RegisterProviders).

Configuration
cfg := config.New()
_ = cfg.Load(".env")
cfg.LoadEnv()

port := cfg.GetInt("PORT", 8080)
debug := cfg.GetBool("APP_DEBUG", false)
name := cfg.GetString("APP_NAME", "vormia")
err := cfg.Require("DB_HOST", "DB_USER") // one error listing all missing keys

Typed getters never fail — they return the default when missing or unparsable.

Logging
log := logger.NewStdoutLogger()
log.SetLevel(logger.LevelDebug)

log.Info("started", map[string]interface{}{"version": "0.0.1"})
log.Error("failed", err, map[string]interface{}{"user_id": 123})

// Production: append to file and mirror to stdout.
fileLog, _ := logger.NewFileLogger("logs/app.log")
defer fileLog.Close()

Depend on the logger.Logger interface, not a concrete type. Also available: NewWriterLogger(w) for tests.

Events
d := events.NewDispatcher()

d.OnPanic(func(event string, recovered interface{}) {
    // required if you use DispatchAsync — otherwise panics are silent
})

d.Listen("user.created", func(payload interface{}) {
    // sync handlers run in registration order
})

d.Dispatch("user.created", user)

w := d.DispatchAsync("user.created", user)
w.Wait() // optional
Context values
import vctx "github.com/vormialabs/vormia-go-core/context"

const UserIDKey = vctx.Key("user_id")

ctx := vctx.WithValue(vctx.Background(), UserIDKey, "u-42")

bag := vctx.NewValueBag()
ctx = vctx.WithBag(ctx, bag)
if b := vctx.BagFrom(ctx); b != nil {
    b.Set("trace_id", "abc")
}
Errors
err := errors.Wrap(cause, "loading user")

ex := errors.NewException("payment failed", "PAY_DECLINED").
    WithCause(gatewayErr).
    With("order_id", 1234)

handler := errors.NewLoggingHandler(app.Logger())
handler.Handle(ex)

Core contracts

type ServiceContainer interface {
    Register(name string, factory func() interface{}) error
    Resolve(name string) (interface{}, error)
    Get(name string, defaultValue interface{}) interface{}
}

type Logger interface {
    Debug(message string, context map[string]interface{})
    Info(message string, context map[string]interface{})
    Warn(message string, context map[string]interface{})
    Error(message string, err error, context map[string]interface{})
    SetLevel(level Level)
}

type EventDispatcher interface {
    Listen(event string, callback func(payload interface{}))
    Dispatch(event string, payload interface{})
}

Also notable:

  • container: RegisterTransient, Has, ServiceProvider, RegisterProviders
  • config: Load / LoadEnv, typed getters, Require, All, Has
  • logger: ParseLevel, NewFileLogger, NewWriterLogger
  • events: OnPanic, ListenTo, DispatchAsync, Forget, HasListeners
  • lifecycle: OnBoot / OnShutdown (+ priority variants), WaitForSignal

Design notes

  • Thread-safe by default — container, config, dispatcher, loggers, value bag, hooks.
  • Fail fast on boot — first boot-hook error aborts; shutdown runs every hook and joins errors.
  • Config load order — Load(".env") then LoadEnv() so OS wins.
  • No magic — string-keyed services and explicit factories (no reflection autowiring).

Package layout

vormia-go-core/
├── go.mod
├── README.md
├── aiguide/           # GUIDE.md + AI contributor rules
├── container/
├── config/
├── logger/
├── events/
├── lifecycle/
├── context/
└── errors/

Development

go build ./...
go vet ./...
gofmt -l .                 # must print nothing
go test ./...
go test -race -count=2 ./...

License

See LICENSE.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL