On this page
Architectural property
COADF P-7 is published in full. Classification systems, terminology databases and external validation services live in adapter modules, marked as such, outside the core data model. An external service can raise the confidence of an attribute; none of them can gate an output. When a service is unavailable the system produces less confident evidence rather than nothing, when a licence changes the core is untouched, and licensed content is marked in its metadata and kept apart from the open core.
How an external answer changes confidence is not published by COADF and is not described here. The examples record an external answer and its status, and do nothing else with it.
Why it matters
Standards and vocabularies outlive products, change versions on their own schedule, carry licence terms and are served by systems you do not operate. Each of those is a way for somebody else's decision to become your outage or your rewrite. The COADF regulatory map names ECLASS and the IEC Common Data Dictionary as licensed vocabularies treated this way; the same reasoning holds for an identifier resolver, a classification provider or an external validation service with its own availability.
The domain needs their answers. It must not need them in order to function, and it must not start thinking in their terms.
Valid implementation strategies
- Port and adapter. The domain defines the interface it needs, in its own words; each external system gets an adapter that implements it.
- Anti-corruption layer. The adapter translates the external model into the domain's model and back, so the external concepts and names stop at the edge.
- Own your identifiers. The domain keeps its own identity for a concept, and stores an external code as an attributed reference: the scheme, the scheme's version, the code, where it came from, when, and whether it is licensed.
- Pin the version. A code means something only within one version of its scheme. Record the version with every stored reference and send it with every lookup. A new transport or a new vendor representation can stay inside the adapter; a new semantic version of the standard can change what codes mean, and then the domain's mapping has to change with it.
- Explicit absence. An unavailable service yields a recorded "not available", never a default value and never a stalled pipeline. Every call has a timeout, and the critical path does not wait for enrichment it can live without.
- Mark licensed content. Every stored value that came from a licensed source carries a marker, so exports, logs, caches and public artifacts can leave it out mechanically.
- A plugin boundary where adapters are optional or supplied by third parties: loaded through a registry the domain does not depend on.
In Python the port can be a typing.Protocol, which is checked by static type checkers; a runtime isinstance check against it needs @runtime_checkable and verifies only that the methods exist, not their signatures. The contract test is what verifies behaviour.
Failure modes
Every failure called an outage
A timeout, a revoked credential, a malformed request and an answer in a new shape all reported as "unavailable". The outage heals by itself; the broken integration is retried forever and never fixed. Keep at least not found, transient unavailability and a rejected integration apart, in the adapter's own terms, not the provider's status codes.
External classes leak through the domain
The vendor's types appear in domain signatures, then in the database schema, then in the public API.
The vendor's SDK becomes the domain model
Generated classes are used as entities because they were already there, and the vendor's model quietly becomes yours.
A licence or API change means rewriting domain logic
The terms change, the vendor retires a version, or a better provider appears, and the change touches every module instead of one adapter.
A remote outage blocks unrelated processing
A synchronous call without a timeout sits in the critical path, and a vendor incident becomes your incident.
External identifiers become accidental internal meaning
Business rules branch on an external code; the next version of the scheme reuses or splits the code, and a rule nobody touched changes behaviour.
Licensed content escapes
Into fixtures, logs, error messages, caches, public documentation, or the text sent to a model.
Tests depend on the live service
The suite passes only when the vendor is up, so the vendor's availability becomes your build's availability.
Verification
Unit test
Passes when: The domain works against a test double of the port.
Proof of teeth: Replace the double with one that raises the adapter's "unavailable" error: the domain still produces its record, marked as not enriched.
Architecture test
Passes when: Domain packages import no vendor package and no adapter module.
Proof of teeth: Plant the vendor import in a domain module: the rule fails.
Contract test
Passes when: The real adapter and the test double pass the same contract tests for the port.
Integration test
Passes when: Timeouts and connection errors from the real client become the domain's own error, and never a generic exception that stops the pipeline.
Integration test
Passes when: A new vendor representation changes the adapter only, and leaves the domain's API and tests untouched. A new scheme version changes the mapping data, and the tests that pin the domain's rules say whether any rule depended on a code that moved.
Manual evidence
Passes when: A licence review of every adapter: what may be stored, for how long, and where it may appear.
Alternative realizations
- A published language or canonical model shared by several bounded contexts, with each external scheme mapped onto it once.
- A wrapper service around a vendor, so every consumer meets the same stable interface.
- A mirrored copy of a vocabulary, where the licence allows it, so lookups stop depending on availability.
- Code generated at build time from a scheme, kept inside the adapter package and never exported from it.
Limitations
- Isolation does not settle licence terms. It makes them enforceable in one place.
- Mapping loses nuance. A mapping table is domain knowledge and needs its own review.
- The pattern does not describe how external evidence affects confidence, which belongs to P-2.
Sources
- Python Software Foundation: typing.Protocol and runtime_checkable · language documentation · Python 3.14 · Checked on 2026-09-11
