Contracts¶
arvel's contracts are a set of Protocol interfaces that define the core services the framework
provides — the container, the application, config, logging, translation, event dispatch. They live in
arvel.contracts and are semver-stable: the concrete implementations can change between releases,
but the contracts they satisfy do not.
Introduction¶
Because they are typing.Protocols, contracts are structural — a class satisfies a contract by
having the right methods, with no explicit subclassing or registration. That makes them ideal for two
things: type-annotating what you depend on, and swapping an implementation without touching callers.
from arvel.contracts import ConfigRepository
def configure(config: ConfigRepository) -> None:
dsn = config.get("database.default") # depends on the contract, not a concrete class
When to use contracts¶
Reach for a contract when you want a loose coupling point — a service you might reimplement, a
seam you want to mock in a test, or a library you're publishing that shouldn't bind callers to arvel's
concrete classes. For ordinary application code, depending on the concrete helper (config(...),
app(...)) is simpler and perfectly fine; contracts earn their keep at boundaries.
How to use contracts¶
Resolve the concrete implementation from the container, annotated by its contract:
from arvel.contracts import Logger
class ReportService:
def __init__(self, log: Logger) -> None: # autowired to the bound concrete logger
self._log = log
def run(self) -> None:
self._log.bind(job="report").info("started")
The container resolves the constructor's annotated dependencies, so binding your own implementation of a contract makes every consumer use it — no consumer changes required. See Service Container.
Contract reference¶
| Contract | Defines |
|---|---|
Container |
Bind abstractions to concretes and resolve them |
Application |
The container that registers providers and boots them |
ServiceProvider |
register (bindings) + boot (wiring) lifecycle |
ConfigRepository |
Dotted-key configuration access (config('app.name')) |
Logger |
Structured logger — bind context, channel selection |
Translator |
Localization lookup with placeholders + pluralization |
ExceptionHandler |
The single global handler (HTTP + console + queue) |
EventDispatcher |
Event dispatch the ORM/queue resolve against |
CommandOutput |
The console output surface shared by commands and seeders |
See also¶
- Service Container — how contracts are bound and resolved.
- Service Providers — where you bind your own implementations.
- Facades — the static-accessor counterpart to resolving a contract.