---
title: Constructors
description: Register services using constructor-based dependency injection
---

`Provide` is the primary registration method in Vessel. It analyzes a function's parameter types, resolves them from the container, calls the function, and registers the return types as services.

## Basic Constructor

```go
import "github.com/xraph/vessel"

c := vessel.New()

func NewDatabase(config *Config) (*Database, error) {
    return sql.Open(config.Driver, config.DSN)
}

func NewUserService(db *Database, logger *Logger) *UserService {
    return &UserService{db: db, logger: logger}
}

vessel.Provide(c, NewConfig)
vessel.Provide(c, NewDatabase)
vessel.Provide(c, NewLogger)
vessel.Provide(c, NewUserService)

// Resolve by type
userService, err := vessel.Inject[*UserService](c)
```

Parameters are resolved by their Go type. Return types (excluding `error`) are registered as services. If the constructor returns an `error` as its last return value, it is used to signal construction failure.

## Named Instances

Use `WithName` when you have multiple implementations of the same type.

```go
vessel.Provide(c, NewPrimaryDB, vessel.WithName("primary"))
vessel.Provide(c, NewReplicaDB, vessel.WithName("replica"))

primary, err := vessel.InjectNamed[*Database](c, "primary")
replica, err := vessel.InjectNamed[*Database](c, "replica")
```

You can also register a named constructor with `ProvideNamed`:

```go
vessel.ProvideNamed(c, "primary", NewPrimaryDB)
vessel.ProvideNamed(c, "replica", NewReplicaDB)
```

## Aliases

`WithAliases` registers additional named access points while keeping the primary registration by type.

```go
vessel.Provide(c, NewDatabaseManager, vessel.WithAliases("manager", "db-manager"))

// All resolve to the same singleton instance:
mgr1, _ := vessel.Inject[*DatabaseManager](c)
mgr2, _ := vessel.InjectNamed[*DatabaseManager](c, "manager")
mgr3, _ := vessel.InjectNamed[*DatabaseManager](c, "db-manager")
```

## Value Groups

Group registrations let you resolve many implementations under a single group key as a slice.

```go
vessel.Provide(c, NewEmailNotifier, vessel.AsGroup("notifiers"))
vessel.Provide(c, NewSMSNotifier, vessel.AsGroup("notifiers"))
vessel.Provide(c, NewPushNotifier, vessel.AsGroup("notifiers"))

notifiers, err := vessel.InjectGroup[Notifier](c, "notifiers")
// notifiers is []Notifier with all three implementations
```

## Interface Registration

Use `As` to register a constructor result as additional interface types.

```go
vessel.Provide(c, NewMyService, vessel.As(new(io.Reader), new(io.Writer)))

// Resolve by interface type
reader, err := vessel.Inject[io.Reader](c)
```

## Lifecycle Options

By default, constructors produce singleton instances. Override with lifecycle options:

```go
// Singleton (default) -- one instance for the app lifetime
vessel.Provide(c, NewDatabase, vessel.AsSingleton())

// Transient -- new instance on every resolve
vessel.Provide(c, NewRequestLogger, vessel.AsTransient())

// Scoped -- one instance per scope (e.g., per HTTP request)
vessel.Provide(c, NewRequestContext, vessel.AsScoped())
```

## Eager Instantiation

By default, constructors are lazy -- they run on first resolve. Use `WithEager` to instantiate immediately after registration. This is useful for fail-fast startup behavior.

```go
// Fail immediately if the database can't connect
vessel.Provide(c, NewDatabase, vessel.WithEager())
```

## In Structs (Parameter Objects)

When a constructor has many dependencies, use an `In` struct to group them. Embed `vessel.In` as a marker.

```go
type ServiceParams struct {
    vessel.In

    DB       *Database
    Logger   *Logger           `optional:"true"`
    Cache    *Cache            `name:"redis"`
    Handlers []http.Handler    `group:"http"`
}

func NewService(p ServiceParams) *Service {
    return &Service{
        db:       p.DB,
        logger:   p.Logger,
        cache:    p.Cache,
        handlers: p.Handlers,
    }
}

vessel.Provide(c, NewService)
```

Supported struct tags on `In` fields:

| Tag | Effect |
|---|---|
| `name:"key"` | Resolve by named registration instead of type |
| `optional:"true"` | Use zero value if the dependency is not registered |
| `group:"name"` | Resolve all services in the group as a slice |

## Out Structs (Result Objects)

When a constructor produces multiple services, use an `Out` struct. Embed `vessel.Out` as a marker.

```go
type ServiceResult struct {
    vessel.Out

    UserService    *UserService
    ProductService *ProductService  `name:"products"`
    Handler        http.Handler     `group:"http"`
}

func NewServices(db *Database) ServiceResult {
    return ServiceResult{
        UserService:    &UserService{db: db},
        ProductService: &ProductService{db: db},
        Handler:        NewAPIHandler(db),
    }
}

vessel.Provide(c, NewServices)

// Each field is registered separately
userSvc, _ := vessel.Inject[*UserService](c)
prodSvc, _ := vessel.InjectNamed[*ProductService](c, "products")
```

Supported struct tags on `Out` fields:

| Tag | Effect |
|---|---|
| `name:"key"` | Register under a specific name |
| `group:"name"` | Add to a value group |

## When to Avoid String Keys

Prefer `Inject` for mainline service graphs. Use named keys only for:

- Multiple instances of the same concrete type
- Explicit grouping semantics
- Backward compatibility with existing keyed registrations
