Enterprise Architecture Patterns Tutorial 0/65 lessons ~6 min read Lesson 50

    Adapter

    The adapter pattern in cloud-native systems normalizes heterogeneous external interfaces — legacy protocols, varied config formats, different logging APIs — into a consistent in…

    Course progress0%
    Focus
    18 guided sections
    Practice signal
    Examples included
    Career prep
    Interview Q&A included

    Introduction

    The adapter pattern in cloud-native systems normalizes heterogeneous external interfaces — legacy protocols, varied config formats, different logging APIs — into a consistent interface the application expects. Amazon's retail services integrate with hundreds of vendor systems, mainframe endpoints, and region-specific payment gateways via adapter containers.

    Staff architects deploy adapters as containers that translate between the application's expected contract and the external system's actual interface — without polluting domain code with integration quirks.

    Real production story

    Amazon's inventory sync integrated with a legacy vendor via SOAP over a VPN tunnel. The vendor changed WSDL quarterly; each change required edits in the inventory service's core domain module. Extracting a dedicated adapter container that exposed a clean gRPC interface internally and handled SOAP/WSDL/retries externally let the inventory team freeze their contract while the adapter team owned vendor churn. When the adapter's memory leak during large catalog syncs OOMKilled the pod, only the sync pipeline stalled — not the real-time inventory API — because adapter ran as a separate deployment, not a sidecar.

    Business problem

    Business pressure: Amazon retail integrates with diverse vendor inventory systems, payment processors, and regional compliance APIs — each with different protocols, auth, and failure modes. Embedding integration logic in domain services couples product velocity to vendor release schedules.

    • Revenue at risk: Vendor API change breaking inventory service deploy blocks catalog updates during peak shopping events.
    • Engineering velocity: Domain teams should not become experts in SOAP, EDI, or regional payment gateways.
    • Compliance / trust: Payment gateway adapters must isolate PCI scope — card data never reaches domain service.

    Architecture overview

    A cloud-native adapter is a containerized service that translates between an external system's interface and the application's expected internal contract. It handles protocol conversion, auth, retry, and format mapping.

    • Definition: Container normalizing heterogeneous external interfaces to a stable internal API.
    • When to adopt: External system protocol differs from internal standard; vendor changes frequently; PCI/compliance boundary needed.
    • When to defer: External API already matches internal contract (native gRPC partner) — direct integration is simpler.
    • Operability: Adapter error rate, translation latency, vendor API health, and sync lag metrics.

    Architecture motivation

    Why architects care: The adapter pattern creates an anti-corruption boundary at the infrastructure level. Domain services speak internal gRPC/REST; adapter containers translate to/from external protocols. This is the containerized cousin of Hohpe's anti-corruption layer.

    • Force: Multiple external systems with incompatible interfaces feeding one domain model.
    • Constraint: Cannot replace vendor systems — must adapt to their contracts.
    • Outcome: Adapter as separate deployment with internal API contract, vendor-specific logic isolated, PCI/network boundaries enforced.

    Internal architecture

    Amazon vendor inventory adapter — separate deployment with internal gRPC:

    • Domain service never imports SOAP libraries — only gRPC client to adapter.
    • Adapter is separate deployment — vendor OOM/leak does not crash inventory API pods.
    • NetworkPolicy restricts vendor VPN access to adapter pods only — smallest PCI/network scope.
    text
    Inventory Service (domain)
    ↓ gRPC: GetVendorStock(vendor_id, sku)
    ↓ internal contract (stable protobuf)
    Adapter Deployment: vendor-acme-adapter (replicas: 2)
    ├─ gRPC server: :50051 (internal contract)
    ├─ translation layer: protobuf ↔ SOAP/XML
    ├─ vendor client: HTTPS + WS-Security auth
    ├─ retry + circuit breaker on vendor calls
    └─ cache: Redis (vendor catalog, TTL 5m)
    External: vendor-acme.com SOAP endpoint (VPN)
    ↑ adapter is only component with VPN access
    NetworkPolicy: only adapter pod can egress to vendor VPN

    Data flow

    Request: domain service gRPC call → adapter translates to vendor protocol → vendor response normalized → gRPC response. Sync: adapter polls vendor catalog on schedule, publishes normalized events to internal Kafka topic.

    • Write path: Inventory adjustment → adapter → vendor SOAP update with vendor-specific error mapping.
    • Read path: gRPC GetVendorStock → adapter checks Redis cache → vendor API on miss.
    • Async path: Scheduled catalog sync → adapter polls vendor → publishes VendorCatalogUpdated to Kafka.
    typescript
    // Adapter internal gRPC service (TypeScript)
    const server = new grpc.Server();
    server.addService(VendorInventoryService, {
    async getVendorStock(call, callback) {
    const { vendorId, sku } = call.request;
    try {
    const cached = await redis.get(`stock:${vendorId}:${sku}`);
    if (cached) return callback(null, JSON.parse(cached));
    const soapResponse = await vendorClient.getStock(sku); // SOAP + WS-Security
    const normalized = translateToInternal(soapResponse);
    await redis.setex(`stock:${vendorId}:${sku}`, 300, JSON.stringify(normalized));
    callback(null, normalized);
    } catch (err) {
    const mapped = mapVendorError(err); // vendor error codes → gRPC status
    callback(mapped);
    }
    },
    });
    // Domain service — only knows gRPC contract
    const stock = await vendorInventoryClient.getVendorStock({ vendorId: "acme", sku: "B08N5WRWNW" });

    System design diagram

    Two diagrams show the Adapter topology and the primary request/event path used in production at scale.

    Adapter — system view
    Domain service
    Edge
    Adapter container
    Core
    External vendor
    Data
    Internal API
    Async
    High-level topology for Adapter.
    Adapter — request / event flow
    Domain gRPC call
    Ingress
    Adapter translate
    Store
    Vendor SOAP/REST
    Store
    Normalize response
    Emit
    Follow this path when reviewing production designs.

    Production code example

    Adapter deployment template — Amazon platform standard:

    • NetworkPolicy is part of adapter architecture — not optional security add-on.
    • Internal gRPC service exposed via ClusterIP — domain services discover via service catalog.
    • Vendor credentials in K8s secrets synced from Vault — never in image or env plaintext.
    yaml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: vendor-acme-adapter
    labels: { pattern: adapter, vendor: acme }
    spec:
    replicas: 2
    template:
    spec:
    serviceAccountName: vendor-adapter
    containers:
    - name: adapter
    image: amazon/vendor-acme-adapter@sha256:...
    ports: [{ containerPort: 50051, name: grpc }]
    env:
    - name: VENDOR_ENDPOINT
    valueFrom: { secretKeyRef: { name: acme-vendor-creds, key: endpoint } }
    resources:
    requests: { cpu: "500m", memory: "512Mi" }
    ---
    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
    name: vendor-acme-adapter-egress
    spec:
    podSelector: { matchLabels: { vendor: acme } }
    policyTypes: [Egress]
    egress:
    - to: [{ ipBlock: { cidr: 10.50.0.0/16 } }] # vendor VPN only
    ports: [{ protocol: TCP, port: 443 }]

    Enterprise case study

    Amazon — vendor inventory adapter fleet: One adapter deployment per major vendor with stable internal gRPC contract. Vendor WSDL changes require adapter-only deploy — inventory service unchanged for 18 months.

    • Before: Vendor changes caused 4 inventory service incidents per year; SOAP libs in domain module.
    • Decision: Extract adapter per vendor; internal protobuf contract owned by platform team.
    • After: Zero domain service deploys for vendor changes; PCI scope reduced to payment adapter pods.

    Trade-offs

    • Isolation vs latency: Separate adapter deployment adds network hop (~1-2ms) vs in-process adapter.
    • Separate deploy vs sidecar: Deployment isolates failure domains; sidecar shares pod lifecycle but lower latency.
    • Cache freshness: Adapter cache reduces vendor load but may serve stale stock — TTL tuned per vendor SLA.
    • Maintenance: One adapter per vendor — N vendors = N adapter deployments to operate.

    Security considerations

    Adapter is the trust boundary: Vendor credentials, VPN access, and PCI-scoped data stay in adapter — never in domain service.

    • Identity: Adapter holds vendor API credentials in Vault; domain service uses internal mTLS only.
    • Data: Card data stops at payment adapter — domain sees tokenized payment_id only.
    • Supply chain: Vendor SDK dependencies isolated in adapter image — not in domain service supply chain.

    Scalability analysis

    Scale dimensions: Amazon integrates 100k+ vendor SKUs. Adapter sync jobs are batch-heavy; real-time lookups are cache-friendly. Scale adapter replicas independently from domain service.

    • Horizontal scale: Adapter replicas scale on gRPC request rate; sync job runs as CronJob separately.
    • Hot spots: Popular SKU cache miss floods vendor API — warm cache during sync; per-SKU rate limit.
    • Cost: One adapter deployment per vendor — consolidate similar vendors into parameterized adapter where possible.

    Failure scenarios

    What breaks: Vendor WSDL change breaks adapter parsing; VPN tunnel down isolates adapter; cache serves stale stock during vendor outage.

    • Vendor schema change: Adapter returns 500 to domain — circuit breaker + alert adapter team, not inventory team.
    • VPN failure: Adapter cannot reach vendor — domain gets UNAVAILABLE; inventory uses last-known cache with staleness flag.
    • Stale cache: Vendor stock zero but cache shows available — TTL + event-driven cache invalidation on vendor webhook.

    Staff engineer insights

    • Adapter is an anti-corruption layer in a container — domain code never imports vendor SDKs.
    • Separate deployment over sidecar when vendor failure must not crash domain pods.
    • Internal gRPC contract is the platform API — version it like any public API.
    • NetworkPolicy to restrict vendor access to adapter pods only — smallest blast radius.

    Interview questions

    Interview Prep

    Practice concise answers, then expand each card for the explanation.

    5 questions
    1AdvancedQuestionCloud-native adapter vs anti-corruption layer — same pattern?+

    Answer

    Same intent. ACL is the DDD name; cloud-native adapter is the containerized implementation. Both translate external model to internal contract, isolating domain from vendor quirks. Adapter adds network boundary and independent deploy.

    Follow-up

    When is in-process ACL better than container adapter?
    2AdvancedQuestionAdapter as separate deployment vs sidecar — trade-offs?+

    Answer

    Deployment: isolated failure domain, independent scaling, extra network hop. Sidecar: shared pod lifecycle, lower latency, vendor failure can OOM pod. Choose deployment when vendor integration is failure-prone or batch-heavy.

    Follow-up

    Amazon chose deployment for vendor SOAP — why?
    3AdvancedQuestionHow do you version the internal adapter API contract?+

    Answer

    Protobuf with backward-compatible evolution. Adapter v2 supports new vendor fields while domain still calls v1 contract. Deprecate v1 with migration timeline. Contract tests between domain client and adapter server in CI.

    Follow-up

    Who owns the internal contract — platform or domain team?
    4IntermediateQuestionVendor API goes down. What does domain service see?+

    Answer

    Adapter circuit breaker returns gRPC UNAVAILABLE after timeout. Domain uses cached data with staleness metadata or degrades feature (show "availability uncertain"). Alert adapter team, not domain on-call.

    Follow-up

    How do you test adapter failure modes?
    5AdvancedQuestionDesign PCI scope reduction with payment adapter.+

    Answer

    Payment adapter is only pod with card data access and payment gateway credentials. Domain passes tokenized payment_id. NetworkPolicy blocks domain pods from payment gateway egress. PCI audit scope = adapter deployment only.

    Follow-up

    How does adapter handle 3DS redirect flows?

    Architecture review questions

    • Domain service imports no vendor-specific SDKs or protocols?
    • Internal gRPC/REST contract versioned with compatibility tests?
    • NetworkPolicy restricts vendor access to adapter pods only?
    • Vendor credentials isolated in adapter — not in domain service?
    • Circuit breaker and error mapping from vendor codes to internal status?
    • Cache TTL and invalidation strategy documented per vendor?

    Summary

    The adapter pattern at Amazon scale means one container per external system, a stable internal gRPC contract, NetworkPolicy-isolated vendor access, and domain services that never import vendor SDKs. Adapters absorb external churn so domain teams ship features, not SOAP patches.

    Ready to mark this lesson complete?Track your journey across the entire course.