---
title: "Vessel"
tagline: "Dependency injection"
language: "Go"
license: "MIT"
since: "2026"
canonical: "https://xraph.com/work/vessel"
---

# Vessel

The DI container underneath Forge, extracted so it can be used on its own. Generic type-safe resolution, uber/dig-style constructor injection, singleton, transient and request-scoped lifetimes, circular-dependency detection, and Start/Stop/Health on every registered service.

## Install

```bash
go get github.com/xraph/vessel
```

## What it is

Vessel is the dependency injection container underneath Forge, extracted so it can be used on its own. Type-safe through generics, with real lifecycle management rather than just construction.

## What it does

- **Constructor injection** in the uber/dig style, with automatic resolution through ProvideConstructor.
- **Typed service keys**: ServiceKey\[T\] for compile-time safety and working autocomplete.
- **Three lifetimes**: singleton, transient and scoped, with scope context storage for request-specific data.
- **Lifecycle**: Start, Stop and Health on every registered service, which is what makes ordered startup and shutdown possible at the framework level.
- **Lazy dependencies** to defer expensive initialisation.
- **Circular detection** at registration rather than at first resolution.
- **Middleware** hooks on resolution and lifecycle events, for logging, metrics and validation.
- **Service discovery**: query and filter registered services by group or lifecycle.
- **Sentinel errors**, so failures are matchable with errors.Is.

## Why lifecycle belongs in the container

Most Go DI libraries construct objects and stop there. The genuinely hard part of a service is not construction, it is knowing that the database pool must be up before the migration runner, which must finish before the HTTP server accepts traffic, and that shutdown reverses all of it. Putting that in the container is what makes it inspectable.

---

*Unpublished draft. Not peer reviewed and not submitted to a venue.*


## Anatomy

- **Resolution**: Constructor injection with typed service keys.
- **Lifetimes**: Singleton, scoped and transient, chosen at registration.
- **Lifecycle**: Start and stop hooks ordered by the dependency graph.
- **Discovery**: Service lookup with sentinel errors rather than panics.

## Constraints and trade-offs

- (decision) Lifecycle belongs in the container. Whatever knows the dependency order should own the start order.
- (constraint) Circular dependencies fail at registration, not at first resolve.
