Understanding the Three-Layer Architecture of the WorkWeave Router
The WorkWeave Router implements a strict concentric three-layer architecture comprising a Presentation/Composition Root layer for HTTP handling and dependency injection, a Business Logic/Inner-Ring layer containing pure Go domain logic without I/O, and an Infrastructure/Leaf layer for concrete adapters like PostgreSQL and upstream providers, enforced by inward-only import rules that eliminate circular dependencies.
The workweave/router repository organizes its codebase around a disciplined three-layer architecture documented in AGENTS.md. This structural pattern separates presentation concerns from domain logic and infrastructure dependencies, ensuring that the core routing engine remains agnostic of external implementations while maintaining strict boundaries between layers.
The Three Layers Explained
The architecture consists of three concentric layers where dependencies always point inward. Outer layers may depend on inner layers, but inner layers remain completely isolated from external concerns.
Layer 1: Presentation and Composition Root
The outermost layer handles HTTP concerns and serves as the application's composition root. Primary packages include cmd/router/main.go, internal/api/*, and internal/server. This layer is responsible for HTTP routing, request parsing, middleware application, and—critically—the instantiation and injection of all concrete implementations.
According to the source code, cmd/router/main.go is the sole location where adapters like database pools and provider clients are created and wired into business logic services. This ensures that inner-ring packages remain pure and free of instantiation logic.
Layer 2: Business Logic (Inner Ring)
The middle layer contains the core domain behavior and is strictly prohibited from performing I/O operations. Key packages include internal/auth (API-key verification), internal/billing (balance checks and inference debiting), internal/proxy (routing and dispatch), and internal/router/* (containing the router interface, request/decision types, planners, cache, and catalog).
Packages like internal/feedback, internal/analytics, and internal/websearch also reside here. These components expose interfaces that the outer layer implements, but they contain only pure functions and value types. For example, the planner logic in internal/router/planner operates entirely on in-memory data structures without network or filesystem calls.
Layer 3: Infrastructure and Leaf Utilities
The innermost layer provides concrete implementations for persistence, external APIs, and observability. Primary packages include internal/config (environment helpers), internal/observability (logging and OpenTelemetry), internal/postgres (SQLC-generated adapters), and internal/providers/* (upstream provider clients like OpenAI).
These adapters may import inner-ring packages to report metrics or timestamps, but the dependency graph strictly prevents inner-ring packages from importing infrastructure code. This guarantees that business logic never accidentally couples to specific database implementations or external service clients.
How the Layers Interact
Strict Import Directionality
The architecture enforces a hard rule: packages may only import inward. For example, internal/api/admin can import internal/auth and internal/router, but it cannot import internal/postgres directly. This constraint eliminates circular dependencies and forces developers to route all persistence operations through the domain layer's interfaces.
The Composition Root Pattern
In cmd/router/main.go, the application assembles all concrete adapters and injects them into inner-ring services. This is the only location where constructors like postgres.NewRepo, auth.NewService, and proxy.NewService are called with their concrete dependencies. By centralizing wiring logic, the codebase keeps domain services agnostic of whether they use PostgreSQL or another storage backend.
Pure Functions in the Inner Ring
Inner-ring packages like internal/router/planner implement logic using only pure functions that transform data structures. The Planner.Plan method, for instance, accepts a router.Request and returns a router.Decision based solely on in-memory catalog lookups and policy checks. This "no I/O" rule guarantees that unit tests can verify routing logic without mocking external services or database connections.
Architectural Patterns in Code
The following examples demonstrate how the three-layer architecture manifests in the workweave/router source code.
Wiring Dependencies in the Composition Root
The cmd/router/main.go file illustrates how concrete implementations are instantiated and injected into inner-ring services:
// Create the concrete PostgreSQL repo.
pgRepo := postgres.NewRepo(pgPool)
// Instantiate the auth service (inner‑ring) with the repo and logger.
authSvc := auth.NewService(pgRepo, logger)
// Build the proxy service (inner‑ring) that depends on auth, billing, and providers.
proxySvc := proxy.NewService(
authSvc,
billing.NewService(pgRepo, logger),
providers.NewOpenAIClient(logger), // infrastructure adapter
logger,
)
// Register HTTP handlers (presentation layer) and inject the proxy.
api.RegisterRoutes(router, proxySvc)
This snippet shows the outer layer creating concrete adapters (postgres.NewRepo, providers.NewOpenAIClient) and passing them to inner-ring services (auth.NewService, proxy.NewService).
Pure Business Logic Without I/O
The internal/router/planner/planner.go file demonstrates the inner-ring's purity:
// Plan decides which model to use for a request, using only pure data.
func (p *Planner) Plan(req router.Request) (router.Decision, error) {
// Look up model pricing from the catalog (no I/O).
modelInfo := p.Catalog.Lookup(req.Model)
// Apply policy (e.g., cost ceiling) and return a Decision.
if req.Budget < modelInfo.Cost {
return router.Decision{}, fmt.Errorf("budget too low")
}
return router.Decision{Model: modelInfo.ID, Provider: modelInfo.Provider}, nil
}
The planner works entirely with in-memory data, illustrating the strict "no I/O" rule that enables straightforward unit testing.
Infrastructure Adapter Implementation
The internal/providers/openai/client.go file shows how infrastructure interacts with the inner ring:
func (c *Client) ChatCompletion(ctx context.Context, req OpenAIRequest) (OpenAIResponse, error) {
// Perform the HTTP request (infrastructure layer).
resp, err := c.http.Do(req.WithContext(ctx))
if err != nil {
return OpenAIResponse{}, err
}
// Record timing via the inner‑ring timing package.
timing.Record(ctx, "openai.request", time.Since(start))
return parseResponse(resp)
}
This adapter performs external HTTP calls while depending on the inner-ring timing package for observability, respecting the inward-only import rule.
Key Files Demonstrating the Architecture
Several critical files illustrate how the three-layer architecture operates in practice:
cmd/router/main.go: The composition root where all adapters are instantiated and injected.internal/api/admin/health.go: Presentation layer endpoint that delegates to inner-ring health checks.internal/router/router.go: Defines theRouterinterface and core request/decision types in the business logic layer.internal/proxy/service.go: Orchestrates authentication, billing, and provider calls as a central inner-ring service.internal/postgres/repo.go: Concrete PostgreSQL adapter implementing repositories used by inner-ring services.internal/providers/openai/client.go: Infrastructure adapter performing external I/O and reporting metrics.internal/config/config.go: Leaf utility providing environment-variable helpers without importing other internal packages.
Summary
- The three-layer architecture of the WorkWeave Router separates concerns into Presentation, Business Logic, and Infrastructure layers.
- Import direction strictly flows inward, preventing circular dependencies and keeping domain logic isolated.
- The composition root at
cmd/router/main.gois the only location where concrete implementations are instantiated and wired together. - The inner ring prohibits I/O operations, ensuring pure business logic that is easily testable without external mocks.
- Infrastructure adapters depend on inner-ring packages for observability but never vice versa, enabling swappable implementations.
Frequently Asked Questions
What defines the three-layer architecture in the WorkWeave Router?
The architecture consists of a Presentation layer (cmd/router/main.go, internal/api), a Business Logic layer (internal/router, internal/proxy, internal/auth), and an Infrastructure layer (internal/postgres, internal/providers). Dependencies may only point inward, ensuring that business logic remains decoupled from HTTP handling and database implementations.
Why does the inner ring prohibit I/O operations?
The inner ring restricts I/O to guarantee that domain logic consists of pure functions operating on in-memory data structures. This design eliminates side effects during unit testing and ensures that routing decisions, billing calculations, and authentication checks remain deterministic and fast without requiring external service mocks.
How does the composition root pattern work in this codebase?
The composition root located at cmd/router/main.go instantiates all concrete adapters—such as postgres.NewRepo and providers.NewOpenAIClient—and injects them into inner-ring services like auth.NewService and proxy.NewService. This singleton wiring location keeps the remainder of the codebase agnostic of concrete implementation details.
Can infrastructure packages import business logic packages?
Yes. Infrastructure packages at the outer layer may import inner-ring packages to utilize types, interfaces, or observability utilities like internal/timing. However, the reverse is strictly prohibited: business logic packages cannot import infrastructure code, maintaining the architectural boundary.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →