goxdi is a system for organizing and sharing dependencies across a Go application by supplying them from outside a type, instead of constructing them inside it.
As an application grows, you need to reuse clients, repositories, and request-bound state without hard-wiring constructors everywhere. Dependency injection (DI) addresses that by separating registration (what exists) from resolution (who asks for it).
TIP: If you want a runnable path first, see Getting started. This page defines vocabulary used everywhere else.
- Maintainability — construction lives in one composition root; business code depends on types, not
newgraphs. - Testability — register fakes or stubs for the same type token without changing consumers.
- Lifetime control — decide once whether an instance is process-wide, per-request, or always fresh.
You interact with goxdi in two ways:
- Provide — make a value available by registering a
Factory[T]on aBuilder. - Resolve — ask for
TwithGet[T]orMustGet[T]from aContainerorScope.
A dependency is any value a type needs but does not create itself: a *sql.DB, a repository interface, a logger, configuration, etc.
provide resolve
─────── ───────
Builder.Add*(factory) → Get[T] / MustGet[T]
│ ▲
└──── Build() → Container ───────┘
│
NewScope() → Scope ─ Get[T]
| Concept | Role |
|---|---|
Builder |
Mutable registry of factories before the container exists |
Container |
Root resolver; owns singleton instances |
Scope |
Child resolver; owns scoped instances for one unit of work |
Factory[T] |
func(Resolver) (T, error) — how to create T |
Resolver |
Anything you can resolve from (Container, Scope, or the context passed into a factory) |
Lifetime |
How long a created instance is retained: singleton, scoped, or transient |
- At process start, register services on a
Builder, thenBuild()a rootContainer. - For each HTTP request, job, or command, open a
Scope. - Resolve the entry service (
Handler,UseCase, …). Its factory resolves further dependencies through the sameResolver. - Close the scope (and later the root) so resources that implement
io.Closerare disposed.
goxdi is intentionally small:
- No decorator magic, no
Injectstruct tags, no auto-wiring by constructor reflection. - No named/keyed multi-bind for the same type in core.
- No framework lifecycle hooks beyond
Close+io.Closer.
Extensions belong in your app or a separate sugar module that composes Factory and Add*.
- Getting started — install and a first program
- Registering services — how provide works in detail
- Lifetimes — when instances are shared or recreated