Responsibilities
Anvil has a narrow job: compile project shape into runtime wiring.
That boundary is what keeps the tool useful. Anvil removes generated glue; it does not take over your domain model, deployment model, or framework choices. Domain decisions stay in the application. Framework decisions stay in drivers. Integration decisions stay in plugins.
Anvil Handles
Section titled “Anvil Handles”Anvil handles protocol-neutral wiring:
- Scanning configured Go packages
- Detecting SDK marker types with
go/types - Validating controllers, groups, routes, handlers, middleware, request models, validation tags, dependency providers, and protocol markers
- Building deterministic compiler plans
- Detecting dependency graph cycles before the application starts
- Emitting generated Go wiring
- Emitting the
anvil.manifest.v1compiler manifest - Generating OpenAPI and testbed artifacts from the manifest
- Running built-in compiler rules
- Running configured Starlark rules over the manifest
- Exposing app lifecycle hooks
- Exposing a global event bus
- Exposing a global error pipeline
- Running the HTTP-family edge listener when HTTP, GraphQL, or gRPC drivers are registered
- Classifying HTTP-family requests before dispatching them to selected drivers
For HTTP, generated code handles route registration, request binding, validation calls, middleware chain order, and WebSocket route registration.
Application Code Provides
Section titled “Application Code Provides”Your application provides the business shape:
- Domain entities and value objects
- Command/query handlers
- Service interfaces
- Stores, repositories, transactions, and outbox logic
- DTOs and request structs
- Domain errors and error classification rules
- Response bodies, response status codes, and response headers
- Logging fields and observability policy
- Concrete dependency implementations
- Deployment configuration
- Which drivers and plugins are imported
Anvil works with n-tier, hexagonal, CQRS, clean architecture, or a smaller package layout. It wires the architecture you choose instead of asking you to reshape the application around Anvil.
Drivers Provide
Section titled “Drivers Provide”Drivers adapt Anvil contracts to concrete protocols and frameworks. A driver is where framework-specific configuration belongs.
Examples:
github.com/TDB-Group/anvil-http/stdprovidesnet/httpserver behaviorgithub.com/TDB-Group/anvil-http/fiberprovides Fiber config and Fiber context adaptationgithub.com/TDB-Group/anvil-http/ginprovides Gin config and Gin context adaptationgithub.com/TDB-Group/anvil-queue/redisprovides Redis connection and broker behavior
Anvil does not add wrapper options for framework-specific settings such as Fiber body limits, Gin engine options, or broker client configuration. Driver constructors accept the native configuration object where that fits the driver.
Plugins Provide
Section titled “Plugins Provide”Plugins are for cross-cutting infrastructure:
- Registering providers such as S3 clients or telemetry exporters
- Adding boot and shutdown hooks
- Observing mapped errors for Sentry, Datadog, Honeycomb, audit logs, or metrics
- Subscribing to the event bus
- Publishing application or integration events
- Extending the error pipeline
Event bus subscribers are also plugin or application code. The bus calls subscribers synchronously and does not recover panics. Use it for deterministic in-process fanout, not for hiding slow work.
Request-Time Rule
Section titled “Request-Time Rule”Anvil inspects types, struct fields, and tags during compilation. Boot-time wiring can use reflection to construct the dependency graph and inject components, but that work finishes before transports accept traffic.
At request time, execution follows the generated route function:
- The Anvil edge listener accepts HTTP-family requests.
- The edge classifies the request as gRPC, GraphQL, or HTTP.
- The selected driver creates the protocol context, such as
sdk.Ctx,sdk.GraphQLCtx, orsdk.GRPCCtx. - Generated code binds and validates request data when that protocol supports generated binding.
- Generated code executes middleware in deterministic order.
- The handler, resolver, RPC method, or job runs.
- The selected driver encodes the returned body or maps the returned error.
If a handler calls ctx.Native(), that handler has intentionally opted into the
current driver. This is useful for framework-specific behavior, but it reduces
driver portability.