<!--
  Dependency Injection
  From the Iris book, chapter 8, covering Iris 14.0.0 on Go 1.27.
  Import path: github.com/kataras/iris/v14
  Canonical page: https://iris-go.com/docs/dependency-injection/
-->
# Chapter 8: Dependency Injection

Iris ships a small dependency injection container. You register values and
constructor functions once, at startup; the container calls your
constructors with matching arguments and hands the finished objects to your
controllers, handlers and middleware. There is no code generation and no
service locator scattered through request code: reflection runs once while
the application wires itself, and everything that can go wrong goes wrong
before the first request. This chapter explains how registration and
resolution work, how controllers and handlers receive their dependencies,
and how the same mechanism turns testing into a one-line swap.

## Background

### What a Dependency Is

Almost no piece of code works alone. A function that saves a user needs
something that talks to storage; a handler that sends a welcome message
needs a mail client; a service that checks permissions needs the
permission rules. Each of those "somethings" is a dependency: a value
your code needs to do its job but did not create itself.

### The Hard-Wiring Problem

The tempting shortcut is to let each piece build or grab what it needs:
a package-level `var db *sql.DB` that everything reads, or a constructor
that opens its own database connection. This is hard-wiring, and it
costs you twice. First, the code becomes untestable: you cannot exercise
`UserService` logic without a real database on the network, because the
service insists on connecting to one. Second, the code becomes rigid:
swapping storage, pointing staging at different infrastructure, or
running two instances with different configuration all mean editing the
code instead of changing what you pass in. Global variables add a third
cost: any function anywhere may read or mutate them, so a function's
signature no longer tells you what it actually depends on.

### Inversion of Control, Without the Jargon

The fix is to turn the direction around: instead of code reaching out
for what it needs, the needs are handed in from outside. That is all
"inversion of control" means. Its most direct form is constructor
injection: a plain function such as `NewUserService(repo UserRepository)`
lists its needs as parameters and returns the assembled value. The
caller decides what to pass. There are no annotations and no framework
requirement, just function parameters doing what they always do.

What a container adds is bookkeeping. In a real application, building
the object graph by hand means calling dozens of `New` functions in the
right order and threading the results between them; every new parameter
ripples through `main`. The Iris container does that threading for you:
you list values and constructors once, and it matches each parameter to
a previously registered result. The problem that disappears from your
code is the hand-maintained wiring, not the constructors themselves.

### Interfaces: the Swap Point

An interface in Go is a named list of methods. Any type that has those
methods satisfies the interface automatically. There is no `implements`
keyword and no registration step. When a constructor asks for an
interface instead of a concrete type, it declares "I need something that
can `List`" without caring what that something is. That is the swap
point this chapter leans on: `main` passes the database-backed
implementation, a test passes a ten-line in-memory fake, and the code in
between cannot tell the difference.

One more term used below: reflection is Go's ability to inspect types
and call functions at run time, through the standard `reflect` package.
The container uses it once, at startup, to read your constructors'
parameter lists and match them to registrations; no reflection runs
while requests are being served.

## Why Constructor Injection

A constructor that declares its dependencies as parameters is the seam of
your application. Whoever calls the constructor decides what "storage" or
"mailer" means in that context: `main` passes the production
implementation, a test passes a fake, and the type in the middle cannot
tell the difference.

```go
// Hidden dependency: the service builds its own storage.
// Tests cannot replace it and every caller pays for the real thing.
func NewUserService() *UserService {
    return &UserService{repo: connectToPostgres()}
}

// Constructor injection: the caller decides what storage means.
func NewUserService(repo UserRepository) *UserService {
    return &UserService{repo: repo}
}
```

Constructor injection works without any framework. What the Iris container
adds is the plumbing: it calls your constructors in the right order with
the right arguments, so `main` does not degenerate into a hand-written
pyramid of `New` calls that changes every time a signature grows a
parameter.

## A Complete Wired Application

The recommended v14 stack is `iris.NewBuilder` with `Services` for the
dependencies and `NewXxxAPI` constructors for the controllers. The
following program is complete; the rest of the chapter takes it apart.

```go
package main

import (
    stdContext "context"
    "sync"

    "github.com/kataras/iris/v14"
    "github.com/kataras/iris/v14/rest/errors"
)

// User is the resource served by this chapter's examples.
type User struct {
    ID   string `json:"id"`
    Name string `json:"name"`
}

// UserRepository is the seam between logic and storage. Constructors
// depend on this interface; main and the tests decide what implements it.
type UserRepository interface {
    List(ctx stdContext.Context) ([]User, error)
}

// MemUsers is an in-memory UserRepository: real storage for a small
// tool, the base fake for tests.
type MemUsers struct {
    mu    sync.RWMutex
    users []User
}

var _ UserRepository = (*MemUsers)(nil)

func NewMemUsers() *MemUsers {
    return &MemUsers{
        users: []User{
            {ID: "1", Name: "Ada"},
            {ID: "2", Name: "Linus"},
        },
    }
}

func (m *MemUsers) List(_ stdContext.Context) ([]User, error) {
    m.mu.RLock()
    defer m.mu.RUnlock()

    list := make([]User, len(m.users))
    copy(list, m.users)
    return list, nil
}

// UserService holds the business rules. It receives its repository
// through the constructor; it never builds one.
type UserService struct {
    repo UserRepository
}

func NewUserService(repo UserRepository) *UserService {
    return &UserService{repo: repo}
}

func (s *UserService) List(ctx stdContext.Context) ([]User, error) {
    return s.repo.List(ctx)
}

// UsersAPI is the HTTP layer: a constructor that declares its
// dependencies and a Routing method that declares its routes.
type UsersAPI struct {
    svc *UserService
}

func NewUsersAPI(svc *UserService) *UsersAPI {
    return &UsersAPI{svc: svc}
}

func (api *UsersAPI) Routing(r iris.Router) {
    r.Get("/", api.list)
}

func (api *UsersAPI) list(ctx iris.Context) {
    users, err := api.svc.List(ctx)
    ctx.List(users, err)
}

// newApp assembles the application. The repository arrives as a
// parameter: main passes the real store, tests pass fakes.
func newApp(repo UserRepository) *iris.Application {
    return iris.NewBuilder().
        NoPrefix().
        AllowOrigin("none").
        Compression(false).
        LogRequests(false).
        NoHealth().
        Errors(errors.NewOptions()).
        Services(repo, NewUserService).
        RouterMiddlewares().
        Middlewares().
        API("/users", NewUsersAPI).
        Build()
}

func main() {
    app := newApp(NewMemUsers())
    app.Listen(":8080")
}
```

Two lines carry the injection. `Services(repo, NewUserService)` registers
the repository value and then calls `NewUserService` with it, because the
parameter type matches. `API("/users", NewUsersAPI)` asks the container to
call `NewUsersAPI` the same way, with the `*UserService` created a moment
earlier. Nothing in `UsersAPI` knows that `MemUsers` exists.

## Container Mechanics

Every router owns a `*dep.Container`, reachable through `DI()`.
`RegisterDependency` feeds it directly; the builder's `Services` step
feeds the same container with extra conveniences (next section). The
manual form works on any application, including plain `iris.New()`:

```go
app := iris.New()

if err := app.RegisterDependency(
    NewMemUsers,    // constructor: called now, output registered.
    NewUserService, // called with the *MemUsers created above.
); err != nil {
    log.Fatal(err)
}

app.Router("/users", NewUsersAPI)
```

The rules the container follows:

- A plain value (struct pointer, client, config) is registered under its
  concrete type and returned as-is to whoever asks for that type.
- A function is treated as a constructor and is called immediately, at
  registration time. Its parameters are resolved from the dependencies
  registered before it; its outputs become new dependencies. Singletons
  therefore exist before the first request and a broken graph surfaces
  at startup, not in production traffic.
- A constructor may include an `error` output. A non-nil error aborts the
  registration and is returned by `RegisterDependency` (the builder's
  `Services` panics with it). The error itself is never registered.
- Lookup walks the registrations in reverse order, newest first. When two
  dependencies can satisfy the same type, the one registered later wins
  for every consumer registered after it.
- Order matters because construction is eager: register a dependency
  before the constructors that consume it.

Those two directions are the whole mechanism. The list is built forwards
and read backwards:

\[Diagram: Dependencies are appended in the order you register them and their constructors run immediately, while resolution walks the same list backwards so the newest registration that satisfies a type wins.]

A singleton is a value created once and then shared, as opposed to
built fresh for each use. Shared is literal here: the Go HTTP server
handles requests concurrently, each on its own goroutine (a lightweight
thread managed by the Go runtime), so many requests may call into the
same instance at the same moment. "Safe for concurrent use" means the
value tolerates that: it is read-only after construction, or it guards
its mutable state with a mutex the way `MemUsers` does.

Singletons are the default and only lifetime for ordinary registrations:
one instance, shared by every consumer and every request, so anything you
register should be safe for concurrent use. The exception is the
request-scoped function dependency, covered in its own section.

You rarely touch the container at request time, but it is available for
manual work:

```go
container := app.DI()

// Resolve by exact type: pass a value of the registered output type
// (*UserService here) or a reflect.Type.
if v, ok := container.Resolve(new(UserService)); ok {
    svc := v.Interface().(*UserService)
    _ = svc
}

// Invoke a function with injected arguments and take its first result.
result, err := container.InvokeReturningFirst(
    func(svc *UserService) int {
        users, _ := svc.List(stdContext.Background())
        return len(users)
    })
if err != nil {
    log.Fatal(err)
}
count := result.(int)
```

## Interface Resolution

`NewUserService` asks for the `UserRepository` interface, but the
container holds a `*MemUsers`. Resolution bridges the gap:

1. An exact type match wins immediately.
2. When the requested type is an interface, the container checks each
   dependency's outputs with `Implements`. Within a dependency the
   candidates are ordered by method-set size, largest first, so the most
   specific output is preferred.
3. Across dependencies the reverse-order rule still applies: the newest
   registration that satisfies the interface wins.

(A method set is simply the collection of methods a type provides.
"Largest first" means that when one registration produces several
outputs that all satisfy the interface, the output with the most methods
is treated as the most specific and wins.)

This rewards the standard Go advice: constructors return concrete types,
consumers accept interfaces. Swapping implementations is a registration
change, not a refactor:

```go
app.RegisterDependency(NewMemUsers) // development default.
// Registered later, wins for every consumer registered after it.
// NewPgUsers stands for any alternative implementation.
app.RegisterDependency(NewPgUsers)
app.RegisterDependency(NewUserService) // receives the Postgres store.
```

Because injection happens at registration time, an override placed after
the consumers has no effect on them. Put replacements before the
constructors that should see them.

## Services and the Builder

`Services(deps ...any)` is the builder's registration step. It forwards
its arguments to the router's container, in order, left to right, after
routing a few shapes specially:

- `func()` and `func() error` become shutdown closers, not dependencies.
- A value with a `Close()` or `Close() error` method is registered as a
  dependency and as a shutdown closer. When the application shuts down
  gracefully, its `Close` runs without a separate `Deferrables` entry.
- An `iris.Configurator` is applied to the application instead of being
  registered.
- `nil` entries are skipped.
- Everything else is registered exactly like `RegisterDependency` would.

```go
db, err := sql.Open("postgres", dsn) // *sql.DB has Close() error.
if err != nil {
    log.Fatal(err)
}

app := iris.NewBuilder().
    // ... prefix, CORS, compression, logging, health, errors ...
    Services(
        db,             // dependency, closed automatically on shutdown.
        NewUserRepo,    // constructor: receives the *sql.DB above.
        NewUserService, // constructor: receives the repository.
    ).
    RouterMiddlewares().
    Middlewares().
    API("/users", NewUsersAPI).
    Build()
```

The auto-closer applies to values you pass in, not to what constructors
create internally. When a constructor opens a resource, construct it in
`main` and pass the value, or register a cleanup through `Deferrables`.

Any registration error makes `Services` panic immediately. That is
deliberate; see [Fail Fast at Startup](#fail-fast-at-startup).

One more convenience: `iris.NewBuilder` registers the `*iris.Application`
itself as a dependency, so a constructor that needs the application (for
its logger, for example) can simply accept `app *iris.Application`.

## Controllers and Dependencies

A controller is any type with a `Routing(r iris.Router)` method
([Chapter 4](https://iris-go.com/docs/routing-controllers/)). Dependencies reach it three
ways.

### Constructor Resolution

Pass the constructor itself to `API` or `Router` and the container calls
it:

```go
// Builder style.
.API("/users", NewUsersAPI)

// Direct style, same resolution.
app.Router("/users", NewUsersAPI)

// Manual style: no container involved, you call the constructor.
app.Router("/users", NewUsersAPI(svc))
```

This is the recommended form. The constructor documents exactly what the
controller needs, and an unsatisfiable parameter stops the program at
startup with the type and the constructor's file and line in the panic
message.

### Field Injection

When you pass a pointer to a struct, the router fills its exported,
zero-valued fields from the container:

```go
// ReportsAPI relies on field injection instead of a constructor.
type ReportsAPI struct {
    Service *UserService // exported and zero: injected by type.

    // dep:"-" opts a field out of injection; set it yourself.
    Title string `dep:"-"`
}

func (api *ReportsAPI) Routing(r iris.Router) {
    r.Get("/", api.overview)
}

func (api *ReportsAPI) overview(ctx iris.Context) {
    users, err := api.Service.List(ctx)
    ctx.OK(iris.Map{
        "title": api.Title,
        "count": len(users),
    }, err)
}

// Registration: fields are bound at this moment.
app.Router("/reports", &ReportsAPI{Title: "overview"})
```

Fields that already hold a non-zero value are left untouched, and a field
that cannot be resolved panics at startup. Prefer constructors for
anything non-trivial; field injection is best for small controllers where
a constructor would be ceremony.

### Controller Helpers

Two helpers compose with dependency injection inside a `Router` or
`API` call:

```go
// Override the inherited dependencies for the controllers that
// follow in the same call (a per-group swap).
app.Router("/preview", iris.Dependencies(previewService), NewUsersAPI)

// Reuse a controller instance already registered as a dependency.
app.Router("/admin", iris.ResolveController[AdminAPI]())
```

`iris.Dependencies` clones the group's container, so the override never
leaks to sibling groups. `iris.ResolveController[T]` panics when no `*T`
dependency exists.

There is no conditional-registration helper. A feature flag is read where
you build the router, and the branch is ordinary Go:

```go
if flags.BillingEnabled {
    app.Router("/billing", iris.ResolveController[BillingAPI]())
} else {
    app.Router("/billing", iris.Middleware(comingSoonHandler))
}
```

## Dependency-Injected Handlers

Controllers are not the only consumers. `HandlerFunc` turns a function
with dependency parameters into an ordinary `iris.Handler`: the outer
function runs once at startup with injected arguments and returns the
handler that serves requests.

```go
app.Get("/stats", app.HandlerFunc(func(svc *UserService) iris.Handler {
    return func(ctx iris.Context) {
        users, err := svc.List(ctx)
        ctx.OK(iris.Map{"count": len(users)}, err)
    }
}))
```

The outer function must take only registered dependency types, never the
context, and must return an `iris.Handler` (standard `http.Handler`-style
returns are converted when possible). The same shape works as middleware:

```go
app.Use(app.HandlerFunc(func(svc *UserService) iris.Handler {
    return func(ctx iris.Context) {
        // Inspect the request using svc, then continue the chain.
        ctx.Next()
    }
}))
```

`HandlerFuncs` maps a list of such functions at once, and the builder's
`Middlewares` and `RouterMiddlewares` steps accept them directly, mixed
freely with plain handlers. `HandlerFunc` panics when a parameter cannot
be resolved, when the function returns nil, or when the result is not a
handler.

## Request-Scoped Dependencies

Everything so far was a singleton, created once at startup. Some values
only exist during a request. The container handles them with a different
contract: a registered function whose inputs include a request type is
not called at registration. Instead, the function itself becomes the
dependency, injectable by its exact function type, and the consumer calls
it with the live context when a request is in flight.

The input types that mark a function as request-scoped in v14:

- `iris.Context`
- the standard library's `context.Context`
- `*http.Request`
- `time.Time`

(Router wiring types such as `iris.Router` and controller functions are
also reserved internally.) Custom types can be added with
`dep.RegisterRequestDependencyType` from
`github.com/kataras/iris/v14/core/dep`, before any registration.

```go
// requestID is request-scoped: the container injects this function
// itself, not its result.
func requestID(ctx iris.Context) string {
    return ctx.Header("X-Request-ID")
}

// AuditAPI declares the function type as a dependency.
type AuditAPI struct {
    requestID func(iris.Context) string
    svc       *UserService
}

func NewAuditAPI(
    rid func(iris.Context) string,
    svc *UserService,
) *AuditAPI {
    return &AuditAPI{requestID: rid, svc: svc}
}

func (api *AuditAPI) Routing(r iris.Router) {
    r.Get("/", api.show)
}

func (api *AuditAPI) show(ctx iris.Context) {
    ctx.OK(iris.Map{"request_id": api.requestID(ctx)}, nil)
}
```

Wire it like any other pair: `Services(requestID)` followed by
`API("/audit", NewAuditAPI)`.

Two rules keep this honest. The container never resolves the inputs of a
request-scoped function; the caller supplies all of them at call time.
And a struct dependency may not contain a request-scoped field: the
registration fails with an error telling you to use a function instead.
Keeping per-request state on the context itself (`ctx.Values()`,
[Chapter 5](https://iris-go.com/docs/reading-requests/)) remains the simpler tool when a
middleware already computed the value.

## Startup Hooks and Shutdown Closers

Some services need a step after wiring but before traffic: warm a cache,
prime a connection pool, load a ruleset. (A connection pool is a set of
network connections kept open and reused, so requests skip the cost of
dialing the database every time; priming it means opening those
connections up front.) Implement `iris.Initializer`:

```go
// Initializer is implemented by services registered through Services
// (or RegisterDependency) that need a startup step.
type Initializer interface {
    Init(ctx context.Context) error
}
```

`Build()` runs `Init` exactly once for every singleton that implements
it, after dependency injection completes, in registration order. A
returned error stops the application from building. Internally the
builder walks the container with `DI().VisitSingletons`, which visits
each resolved singleton value once (request-scoped functions have no
value to visit and are skipped).

A cache that warms itself at startup:

```go
// UserCache serves reads from memory after a one-time warm-up.
type UserCache struct {
    repo UserRepository

    mu    sync.RWMutex
    users []User
}

func NewUserCache(repo UserRepository) *UserCache {
    return &UserCache{repo: repo}
}

// Init implements iris.Initializer. It runs inside Build(), once,
// after injection: the app never starts with a cold cache.
func (c *UserCache) Init(ctx stdContext.Context) error {
    users, err := c.repo.List(ctx)
    if err != nil {
        return err // aborts the build.
    }

    c.mu.Lock()
    c.users = users
    c.mu.Unlock()
    return nil
}

func (c *UserCache) All() []User {
    c.mu.RLock()
    defer c.mu.RUnlock()
    return c.users
}
```

Register it with the rest: `Services(repo, NewUserService, NewUserCache)`.
No extra call is needed; implementing the interface is the registration.

`Deferrables` is the mirror image at shutdown. It accepts `func()`,
`func() error` and values with a `Close` method, and runs them when the
server stops gracefully, together with the closers that `Services`
collected from `Close()`-bearing values:

```go
.Deferrables(func() { log.Println("users: goodbye") }).
```

Startup order is registration order; use it when one service must warm up
before another. The pair gives every service a full lifecycle: construct
(constructor), start (`Init`), serve, stop (`Close` or a deferrable).

## Fail Fast at Startup

The dependency system panics instead of limping:

- `Services` panics when a registration fails: a constructor parameter
  that nothing provides, or a constructor that returned an error.
- `Router` and `API` panic when a controller constructor cannot be
  satisfied or a field cannot be bound. The message names the missing
  type (and, for constructors, the function's file and line).
- `HandlerFunc` panics on an unresolvable parameter, a nil result or a
  non-handler result.
- `Build` panics when an `Init` returns an error.

This is a feature. All of these run during startup, so a broken graph
kills the process before it binds a port: the failure is caught by your
first local run, your tests or your deploy's health check, never by a
user hitting a nil service at 3 a.m. When you want to handle a
registration failure yourself, use `app.RegisterDependency`, which
returns the error instead of panicking.

## Testing with Fakes

A fake is a hand-written stand-in that satisfies the same interface as
the real implementation: a slice instead of a database, a struct that
always fails instead of a flaky network. Unlike mocks from a mocking
library, fakes carry no expectation scripting. They are ordinary Go
code, which keeps tests readable and refactor-proof.

The payoff of constructor injection is that `newApp` from this chapter's
program accepts the `UserRepository` interface. Tests build the same
application as production, minus one argument:

```go
package main

import (
    stdContext "context"
    "fmt"
    "testing"

    "github.com/kataras/iris/v14/httptest"
)

func TestListUsers(t *testing.T) {
    app := newApp(NewMemUsers())
    e := httptest.New(t, app)

    e.GET("/users").Expect().
        Status(httptest.StatusOK).
        JSON().Array().Length().IsEqual(2)
}

// failingUsers embeds the interface and overrides one method;
// everything else is inherited from the wrapped repository.
type failingUsers struct {
    UserRepository
}

func (failingUsers) List(stdContext.Context) ([]User, error) {
    return nil, fmt.Errorf("pg: connection refused on 10.0.0.3:5432")
}

func TestListUsersFailure(t *testing.T) {
    app := newApp(failingUsers{NewMemUsers()})
    e := httptest.New(t, app)

    e.GET("/users").Expect().
        Status(httptest.StatusInternalServerError)
}
```

`failingUsers` is the whole mocking framework: interface embedding makes
a one-method fake five lines, with no library and no expectations DSL.
For a fake that only one route group should see, `iris.Dependencies`
scopes the override without a second `newApp` variant. The full testing
story, including `httptest` options and response assertions, is
[Chapter 16](https://iris-go.com/docs/testing/); the tutorial's Bookshelf project applies
this exact `failingRepo` pattern end to end.

## Summary

- Constructor injection is the design; the container is the plumbing.
  Constructors declare dependencies, `Services` (or
  `RegisterDependency`) provides them, `API` resolves controllers
  through their `NewXxxAPI` constructors.
- Registration is eager: constructors run at registration time, in
  order, so list dependencies before their consumers. Lookup is newest
  first, which makes overriding a registration-order concern.
- Interface parameters are matched by method set, so constructors return
  concrete types while consumers accept interfaces.
- `Services` routes shapes: closers are collected from `func()`s and
  `Close()`-bearing values, `Configurator`s are applied, the rest become
  dependencies. `HandlerFunc` brings injection to plain handlers and
  middleware.
- Request-scoped dependencies are functions taking `iris.Context`,
  `context.Context`, `*http.Request` or `time.Time`; the function itself
  is injected and the consumer calls it with the live context.
- `iris.Initializer.Init` runs once inside `Build()` for cache warm-up
  and priming; `Deferrables` and auto-registered closers run at
  shutdown.
- Unresolvable dependencies panic at startup by design: the process
  refuses to serve with a broken graph.
- Fakes are interface embeddings passed to your `newApp` seam; see
  [Chapter 16](https://iris-go.com/docs/testing/).

## Further Reading

- [Effective Go — Interfaces](https://go.dev/doc/effective_go#interfaces):
  the language's own guide to interfaces and implicit satisfaction,
  the mechanism every swap in this chapter relies on.
- [Go Wiki: Code Review Comments — Interfaces](https://go.dev/wiki/CodeReviewComments#interfaces):
  the canonical statement of "accept interfaces, return concrete
  types" that the container's resolution rules reward.
- [The Go Blog: The Laws of Reflection](https://go.dev/blog/laws-of-reflection):
  how reflection works in Go; what the container is doing when it
  reads your constructors' parameter lists at startup.
- [Go standard library: sync](https://pkg.go.dev/sync):
  the mutexes and other primitives that make shared singletons safe
  for concurrent use.
