Search Knowledge

© 2026 LIBREUNI PROJECT

Software Architecture / Distributed Design

Integration, Contracts & Coupling

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.

Code
left to right direction
rectangle "Coupling Types" as Coupling
rectangle "Temporal" as Temporal
rectangle "Data Shape" as Data
rectangle "Semantic" as Semantic
rectangle "Availability" as Availability
rectangle "Deployment" as Deployment
rectangle "Operational" as Operational

Coupling --> Temporal
Coupling --> Data
Coupling --> Semantic
Coupling --> Availability
Coupling --> Deployment
Coupling --> Operational
Coupling TypesTemporalData ShapeSemanticAvailabilityDeploymentOperational

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.

Code
left to right direction
actor "User" as User
rectangle "Web App" as Web
rectangle "Checkout API" as Checkout
rectangle "Pricing API" as Pricing
rectangle "Inventory API" as Inventory
rectangle "Payment API" as Payment

User --> Web
Web --> Checkout : submit order
Checkout --> Pricing : price basket
Checkout --> Inventory : reserve
Checkout --> Payment : authorize
rectangle "Latency Budget\n400 ms total" as Budget
Budget .. Checkout
UserWeb AppCheckout APIPricing APIInventory APIPayment APILatency Budget400 ms totalsubmit orderprice basketreserveauthorize

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.

Code
left to right direction
rectangle "Producer" as Producer
queue "Event Stream\nOrderPaid v2" as Stream
rectangle "Consumer A\nFulfillment" as A
rectangle "Consumer B\nAnalytics" as B
rectangle "Consumer C\nMessaging" as C
rectangle "Schema Registry" as Registry

Producer --> Registry : validates schema
Producer --> Stream : publishes fact
Stream --> A : at least once
Stream --> B : replay projection
Stream --> C : customer receipt
Registry .. A : compatibility rules
Registry .. B : compatibility rules
Registry .. C : compatibility rules
ProducerEvent StreamOrderPaid v2Consumer AFulfillmentConsumer BAnalyticsConsumer CMessagingSchema Registryvalidates schemapublishes factat least oncereplay projectioncustomer receiptcompatibility rulescompatibility rulescompatibility rules

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.

Code
left to right direction
database "Operational Store" as Store
rectangle "Export Job" as Export
folder "Encrypted File Drop" as Drop
rectangle "Partner Import" as Partner
rectangle "Reconciliation Report" as Recon
rectangle "Exception Queue" as Exceptions

Store --> Export : nightly extract
Export --> Drop : signed file
Drop --> Partner : import
Partner --> Recon : accepted and rejected counts
Recon --> Exceptions : investigation
Operational StoreExport JobEncrypted File DropPartner ImportReconciliation ReportException Queuenightly extractsigned fileimportaccepted and rejected countsinvestigation

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.

Code
left to right direction
rectangle "Contract Lifecycle" as Lifecycle {
rectangle "Design" as Design
rectangle "Consumer Review" as Review
rectangle "Compatibility Tests" as Tests
rectangle "Publish" as Publish
rectangle "Observe Usage" as Observe
rectangle "Deprecate" as Deprecate
rectangle "Remove" as Remove
}
Design --> Review
Review --> Tests
Tests --> Publish
Publish --> Observe
Observe --> Deprecate
Deprecate --> Remove
Contract LifecycleDesignConsumer ReviewCompatibility TestsPublishObserve UsageDeprecateRemove

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