Skip to content

Service Container

Overview

The service container is a lightweight registry that stores and retrieves services by their Go type. It’s the simplest form of dependency injection in Lemmego, used internally by the framework and by your application code.

Adding Services

Register services via app.AddService():

type MyService struct {
    // ...
}

func (p *MyProvider) Provide(a app.App) error {
    svc := &MyService{}
    a.AddService(svc)
    return nil
}

Retrieving Services

Via the App Instance

svc := a.Service(&MyService{}).(*MyService)

Via the Generic Helper

import "github.com/lemmego/api/app"

svc := app.Get[*MyService](a)

The generic Get[T] helper composes with type safety:

session := app.Get[*session.Session](a)
filesystem := app.Get[*fs.FileSystem](a)

Built-in Services

The framework registers several services through its providers:

ServiceTypeProvider
Session Manager*session.Sessionsession.Provider
File System*fs.FileSystemfs.Provider
Database*gorm.DB / *bun.DBgormconnector / bunconnector
Inertia*inertia.Inertiainertia.Provider
Auth*auth.Authauth.Provider

How It Works

The container uses reflection to store services by their pointer type:

type serviceContainer struct {
    services map[reflect.Type]interface{}
    mutex    sync.RWMutex
}

func (sc *serviceContainer) Add(service interface{}) {
    t := reflect.TypeOf(service)
    sc.services[t] = service
}

func (sc *serviceContainer) Get(service interface{}) error {
    // Resolves the service by type and populates the provided pointer
}

When to Use

Use the service container for:

  • Framework internals (session, filesystem, database connections)
  • Simple service registration where you need exactly one instance
  • Services that need to be available across the entire application

For more advanced DI needs (multiple lifetimes, auto-resolution of dependencies, scoped instances), see the Dependency Injection page.