Context
Context is the only object that flows through the entire request lifecycle in Plumego.
It is not a service locator.
It is not a dependency container.
It is not a place to hide business logic.
Plumego’s Context is intentionally minimal, predictable, and request-scoped.
Understanding what it is — and what it is not — is essential to using the framework correctly.
What the Context Represents
A Plumego Context represents one HTTP request.
It exists to:
- Carry request data
- Carry response writers
- Carry request-scoped metadata
- Flow through middleware and handlers
Nothing more.
If a value does not conceptually belong to this request,
it does not belong in the Context.
Context Lifetime
The Context lifecycle is strictly bounded:
Request Received
→ Context Created
→ Middleware Chain
→ Handler
→ Response Written
→ Context Discarded
Key properties:
- One Context per request
- Never shared across requests
- Never reused
- Never global
This guarantee makes concurrency predictable.
What the Core Context Provides
At a minimum, the Plumego Context provides access to:
- The incoming HTTP request
- The response writer
- Request-scoped storage
- Convenience helpers for responses
Conceptually:
type Context struct {
Request *http.Request
ResponseWriter http.ResponseWriter
// request-scoped storage
}
Exact APIs may evolve, but the guarantees do not.
Request-Scoped Storage
Context provides a small key–value store for request-scoped metadata.
Typical uses include:
- Trace IDs
- Request IDs
- Authenticated identity
- Deadlines or flags
- Middleware annotations
Example (conceptual):
ctx.Set("trace_id", traceID)
id, _ := ctx.Get("trace_id")
Rules:
- Values must be request-scoped
- Keys should be unique and well-defined
- Stored values should be immutable after set
Context storage is not a general cache.
Context and Middleware Flow
The Context is passed by reference through the middleware chain.
Each middleware:
- Receives the same Context instance
- May read from it
- May add metadata to it
- Must not replace it
This ensures:
- Consistent state visibility
- Predictable execution order
- No hidden forks or branches
Context and Handlers
Handlers receive the Context as their only framework-level input.
This enforces a clear boundary:
- Handlers adapt Context → usecase input
- Usecases do not depend on Context
- Domain logic never sees Context
If Context reaches your domain, boundaries are already broken.
Context Is Not a Dependency Container
A common temptation is to treat Context as a place to fetch services:
db := ctx.Get("db")
This is explicitly discouraged.
Why:
- It hides dependencies
- It breaks testability
- It creates implicit coupling
- It turns Context into a service locator
Dependencies should be injected explicitly, not pulled from Context.
Context Is Not Business State
Do not store business entities or workflow state in Context.
Bad examples:
- Orders
- Domain aggregates
- Mutable business flags
Context is about request metadata, not business data.
Context and Cancellation
Plumego Context is aligned with Go’s context.Context.
Cancellation and deadlines propagate naturally:
- Client disconnect
- Server shutdown
- Timeout middleware
Long-running operations should respect cancellation signals.
Ignoring cancellation is a correctness bug, not an optimization issue.
Context Mutation Discipline
Mutation rules are simple:
- Middleware may add metadata
- Handlers may read metadata
- No one should remove or overwrite meaningfully owned keys
If multiple components fight over the same keys,
naming and ownership are unclear.
Context Keys: Naming and Ownership
Good practice:
- Use well-defined key names
- Prefer constants
- Document ownership
Example:
const ContextKeyTraceID = "trace_id"
Avoid ad-hoc string keys scattered across the codebase.
Context and Testing
Because Context is explicit and request-scoped:
- Middleware can be tested with a fake Context
- Handlers can be tested with constructed requests
- Usecases remain independent of Context entirely
If Context is required to test core logic,
architecture boundaries have been crossed.
Common Anti-Patterns
Using Context as Global State
This defeats request isolation and leads to race conditions.
Passing Context into Usecases
This couples application logic to the framework.
Storing Mutable Shared Objects
Context values should be treated as immutable metadata.
Why Plumego Keeps Context Small
Every additional responsibility added to Context:
- Increases coupling
- Encourages misuse
- Makes reasoning harder
Plumego deliberately keeps Context minimal so that:
Most architectural decisions remain in your code, not in the framework.
Summary
In Plumego:
- Context is request-scoped
- Context carries metadata, not logic
- Context flows through middleware and handlers
- Context never enters the domain
- Context is explicit and predictable
Treat Context as a carrier, not a container.
Next
With Context understood, continue with:
→ Router
This explains how Plumego matches requests and dispatches handlers — without magic.