Adapters and Bridges¶
Page Maps¶
graph LR
family["Python Programming"]
program["Python Object-Oriented Programming"]
section["Aggregates Events Collaboration Boundaries"]
page["Adapters and Bridges"]
capstone["Capstone evidence"]
family --> program --> section --> page
page -.applies in.-> capstone
flowchart LR
orient["Orient on the page map"] --> read["Read the main claim and examples"]
read --> inspect["Inspect the related code, proof, or capstone surface"]
inspect --> verify["Run or review the verification path"]
verify --> apply["Apply the idea back to the module and capstone"]
Read the first diagram as a placement map: this page is one concept inside its parent module, not a detached essay, and the capstone is the pressure test for whether the idea holds. Read the second diagram as the working rhythm for the page: name the problem, study the example, identify the boundary, then carry one review question forward.
Why this lesson matters¶
Your object model should not learn the dialect of every database, queue, web API, or framework it touches.
When that dialect leaks inward, you get a design that is hard to test, hard to reason about, and hard to change. Domain rules start depending on transport details. Error handling becomes inconsistent. Every replacement of an outer system turns into a rewrite of inner logic.
Adapters and bridges solve that by keeping translation at the edge.
The problem they solve¶
The inner model wants clear concepts such as:
- a rule was activated
- a policy was saved
- an alert was delivered
- a report was loaded
Outer systems usually speak in a different language:
- HTTP responses
- SQL rows
- SDK exceptions
- framework callbacks
- serialized payloads
If you let those outer shapes spread through the core model, the core stops being a core. It becomes a meeting point for unrelated dependencies.
What an adapter is¶
An adapter converts one interface into another interface your code actually wants.
In this course, the usual pattern is:
- the application or domain defines the needed port
- infrastructure implements that port with an adapter
- the adapter translates inputs, outputs, and failures
That translation can include:
- converting framework or SDK data into domain values
- mapping storage rows into object state
- turning external exceptions into stable local failure meanings
- normalizing inconsistent outer naming into domain language
The key point is simple: the adapter absorbs mismatch so the rest of the system does not have to.
What a bridge means here¶
In Python teaching material, "bridge" is often less about ceremony and more about separation of responsibility.
The core idea is:
- one side owns the abstraction
- another side provides interchangeable implementations
For this module, that usually means:
- the application layer depends on a protocol or port
- infrastructure provides one or more concrete implementations
- the object model keeps its reasoning unchanged while the outer mechanism changes
You can think of it this way:
- the adapter handles translation
- the bridge keeps the inner abstraction from collapsing onto one outer implementation
They often appear together.
A concrete monitoring example¶
Suppose a monitoring policy needs recent metric values. The application layer may want something like:
class MetricHistoryPort(Protocol):
def recent_samples(self, metric_name: str, limit: int) -> list[float]: ...
The external client may instead provide:
fetch_timeseries(name: str, count: int) -> dict- timestamps you do not need for this decision
- failure types that mention HTTP, auth, or quota details
An adapter should:
- call the client
- extract the values needed for policy decisions
- raise a local, meaningful failure when the source is unavailable
The aggregate or strategy should not need to know whether the data came from HTTP, SQLite, a file, or a fake used in tests.
Error translation is part of the boundary¶
Students often understand data translation first and forget failure translation. That is where a lot of design damage happens.
If inner layers must handle:
TimeoutClientErrorOperationalErrorConnectionResetError
then the outer system has already leaked inward.
Usually the inner system needs a smaller set of meanings, such as:
- source unavailable
- storage unavailable
- malformed external record
- unsupported response version
The adapter owns the work of turning outer failures into those stable local meanings.
What should never cross inward¶
The following should normally stop at the adapter boundary:
- framework request and response objects
- ORM entities used only for persistence mechanics
- transport-specific status codes
- vendor exception trees
- serialization details that matter only at the edge
Once those shapes spread inward, every lesson about aggregates, policies, invariants, and event reasoning gets harder to preserve.
Common mistakes¶
- letting the aggregate call SDK clients directly
- returning raw database rows to the application layer
- handling transport-specific errors inside domain objects
- mixing translation with business decision logic in the same method
- treating one concrete infrastructure implementation as if it were the abstraction
These mistakes usually start as convenience and end as dependency sprawl.
Review checklist¶
| Question | Good sign |
|---|---|
| is the inner model speaking domain language rather than client or framework language? | yes |
| are outer data shapes translated before they reach core decisions? | yes |
| are vendor and transport errors mapped to local meanings? | yes |
| can a fake or second implementation replace the first without changing the inner model? | yes |
Capstone connection¶
In the capstone, this lesson matters anywhere the system touches:
- persistence
- network clients
- message delivery
- CLI or HTTP entrypoints
- reporting or export surfaces
If the capstone core layer already knows too much about one framework or storage shape, the next improvement is not a naming cleanup. It is a boundary cleanup.
Exit check¶
Leave this lesson only when you can do all of these:
- explain why translation belongs at the edge rather than in aggregates
- identify one place where error mapping is as important as data mapping
- describe one capstone dependency that should remain behind a port and adapter