Principle 1
Build Around Business Capabilities
Back to principlesBusiness capabilities should outlive the vendors and technologies that implement them. Keep core workflows expressed in business terms so replacing a provider does not require rewriting the rules around it.
In practice: Checkout asks a payment capability to authorize an amount. An adapter translates that request into the provider's API and returns an outcome checkout understands, including an indeterminate result.
Tradeoff: Adapters add mapping and contract tests. They must preserve meaningful provider differences, including unsupported operations. Use them where replacement is plausible; direct SDK usage can be reasonable for a short-lived experiment.
Read the MongoDB-to-PostgreSQL migration case study for the boundaries that contained change and the operational work they could not remove.
Explore the reasoning: Build Around Business Capabilities
Keep vendor details at the boundary
An authorizePayment contract can accept an amount, currency, payment method reference, order
reference, and idempotency key. Its outcomes should distinguish approved, declined, indeterminate,
and failed requests. Vendor status codes, request objects, and retry rules belong in the adapter.
A generic execute(data): any interface hides a vendor without explaining the operation. Copying
every SDK method into a new interface preserves the coupling under another name. Model the
capability callers need, including its failure behavior.
The same approach applies to other integrations:
A Shopify adapter can expose catalog publication, order retrieval, fulfillment updates, and refunds without spreading webhook or GraphQL response shapes through orchestration code.
A loyalty interface can describe balance lookup, earn, redeem, and reversal while retaining provider identifiers and error details at the boundary.
A repository can expose
findOrderByExternalIdorsavePromotionRedemptionwhile keeping storage representation and query details local.
Preserve differences that affect the business
Providers are not interchangeable in every respect. If a payment provider cannot separate authorization from capture, represent that limitation or reject the unsupported workflow. Keep provider transaction identifiers available for reconciliation and support.
Repository boundaries should reflect business aggregates and transaction behavior. A universal storage interface that treats SQL, document storage, caches, and search engines as identical usually removes useful capabilities. Abstract where replacement is valuable, without building a private database framework.

