1 Service-Lifecycle
Virgil edited this page 2026-02-19 17:29:44 +00:00

Service Lifecycle

The Core struct is the central DI container managing named services, lifecycle sequencing, and the message bus.

Core Struct

type Core struct {
    App      any           // GUI runtime (Wails)
    assets   embed.FS      // Embedded frontend
    Features *Features     // Feature flags
    svc      *serviceManager
    bus      *messageBus
    taskIDCounter atomic.Uint64
}

Creating a Core Instance

core, err := core.New(
    core.WithService(NewMyService),    // Auto-discovers name from package
    core.WithName("custom", factory),  // Explicit name
    core.WithApp(wailsApp),            // Wails runtime injection
    core.WithAssets(assets),           // Embedded frontend
    core.WithServiceLock(),            // Prevent late registration
)

All configuration uses functional options: type Option func(*Core) error.

Service Registration

Auto-Named (WithService)

The service name is auto-discovered from the package path (last component):

// In myapp/services/database/service.go
package database

func NewService(c *core.Core) (any, error) {
    return &Service{}, nil
}

// Registered as "database"
core.New(core.WithService(NewService))

If the service implements HandleIPCEvents(c *Core, msg Message) error, it is automatically registered as an action handler.

Explicit Name (WithName)

For anonymous packages or multiple services from the same package:

core.WithName("primary-db", NewService)
core.WithName("cache-db", NewService)

Service Lock (WithServiceLock)

Prevents late service registration after initialisation:

core, err := core.New(
    core.WithService(NewLogger),
    core.WithServiceLock(),
)
// RegisterService() now returns "service not permitted by the serviceLock"

Service Retrieval

Type-Safe (ServiceFor)

db, err := core.ServiceFor[DatabaseService](c, "database")

Panic on Error (MustServiceFor)

db := core.MustServiceFor[DatabaseService](c, "database")

Untyped

svc := c.Service("database")  // Returns any, nil if not found

Built-in Accessors

c.Config()     // Config interface
c.Display()    // Display interface
c.Workspace()  // Workspace interface
c.Crypt()      // Crypt interface

These panic if the service is not registered.

Lifecycle Interfaces

Services implementing Startable are called during startup in registration order:

type Startable interface {
    OnStartup(ctx context.Context) error
}

Services implementing Stoppable are called during shutdown in reverse order:

type Stoppable interface {
    OnShutdown(ctx context.Context) error
}

Wails Runtime Wrapper

Runtime bridges Core to Wails v3:

rt, err := runtime.NewRuntime(wailsApp)
rt, err := runtime.NewWithFactories(wailsApp, factories)

// Wails lifecycle callbacks
func (r *Runtime) ServiceStartup(ctx context.Context, options any) error
func (r *Runtime) ServiceShutdown(ctx context.Context) error

Global Instance

For Wails integration where a global is needed:

core.SetInstance(c)
app := core.App()          // Panics if not set
inst := core.GetInstance()  // Returns nil if not set
core.ClearInstance()        // For testing

See Message-Bus for the ACTION/QUERY/TASK patterns and Service-Runtime for the generic helper.