Integration, Contracts & Coupling
Integration is where architecture becomes social. A contract says what one party can depend on and what another party promises to preserve. Modern API, event-driven, and microservice guidance agree that the contract may be an HTTP API, event schema, file format, library interface, database view, command queue, or manual workflow. Good contracts reduce coordination. Poor contracts move hidden assumptions across boundaries until every change requires negotiation.
Coupling is not one thing. Systems can be coupled by time, data shape, availability, deployment, semantics, identity, operational process, and ownership. A synchronous API couples caller and provider in time and availability. A shared database couples consumers to storage structure. An event stream decouples time but couples consumers to event semantics and versioning. Shared libraries reduce duplication but couple release cadence.
Synchronous Contracts
Synchronous APIs are appropriate when the caller needs an immediate answer to continue. They are often simpler for request-response interactions, validation, and user-facing flows. The contract should define behavior, not only fields: idempotency, error model, timeout expectation, authentication, authorization, rate limits, pagination, compatibility rules, and ownership of retries.
The danger is call-chain architecture. A user request enters one service and triggers five downstream calls, each with its own latency and failure behavior. Tail latency compounds and partial failures become user-visible. Synchronous contracts need budgets: latency budget, retry budget, dependency budget, and a plan for fallback or failure.
Event Contracts
Events are contracts around facts. They should be named in business language, versioned carefully, and documented with delivery expectations. Consumers need to know whether events are ordered, duplicated, delayed, replayable, retained, and backward compatible. Producers need to know which fields are contractual and which are incidental.
The strongest event contracts are additive and tolerant. Add fields without breaking consumers. Avoid changing meaning under the same name. Prefer new event versions or new event types when semantics change. Consumers should ignore unknown fields and use idempotency keys because event delivery commonly offers at-least-once semantics.
Files, Batches, and Data Products
Senior architecture should not pretend everything is an API. Many enterprise and analytical integrations are files, batches, extracts, or data products. These interfaces can be excellent when latency tolerance is high, auditability matters, or external partners cannot support interactive APIs. They can be terrible when they hide errors for days or create ambiguous ownership.
A batch contract should define schedule, schema, completeness guarantees, correction process, replay process, retention, encryption, and ownership of rejected records. A data product should define meaning, freshness, lineage, access policy, and consumer support. Treating files as low-status integration creates fragile architecture. Treating them as first-class contracts creates predictable systems.
Compatibility and Versioning
Compatibility is an architectural quality. Backward compatibility means old consumers continue to work with new providers. Forward compatibility means new consumers can tolerate older providers or unknown fields. Contract testing helps, but it cannot replace ownership. Someone must decide what compatibility means, how long versions live, and how deprecation is communicated.
The most dangerous versioning strategy is no strategy. Teams add optional fields until optional no longer means optional, reuse field names with new semantics, or create endpoints that remain forever because no one tracks consumers. Mature architecture includes a contract lifecycle: propose, validate, publish, observe adoption, deprecate, and remove.
Anti-Corruption Layers
An anti-corruption layer protects one model from another. It translates external concepts into local concepts and prevents legacy or vendor semantics from leaking into the core. The layer is not just a mapper. It is a boundary of meaning. If a vendor calls every customer an “account” but your domain distinguishes billing account, user, and legal party, the anti-corruption layer preserves that distinction.
This pattern is especially important during migrations. A new system may need to coexist with an old system for months. Without translation, the new model becomes polluted by legacy constraints. With a clear anti-corruption layer, migration work is visible, testable, and eventually removable.
Practice
Pick one integration and describe its contract in ten lines: owner, purpose, protocol, data shape, error behavior, compatibility rule, latency expectation, retry rule, observability signal, and deprecation path. Then identify two forms of coupling it creates and one architectural tactic that reduces each.
References & Further Reading
- Microsoft Azure Architecture Center: API Design (Microsoft Learn, CC BY 4.0)
- Microsoft Azure Architecture Center: Event-Driven Architecture Style (Microsoft Learn, CC BY 4.0)
- Martin Fowler: Consumer-Driven Contracts (standard copyright)
- Enterprise Integration Patterns by Gregor Hohpe and Bobby Woolf (Addison-Wesley, standard copyright)