Skip to content

Latest commit

 

History

History
70 lines (41 loc) · 5.14 KB

File metadata and controls

70 lines (41 loc) · 5.14 KB

Adapter — Real-World Applications


1. Payment Gateway Integration (this implementation)

Problem: Each payment provider (Razorpay, Paytm, Stripe) exposes a completely different SDK. Razorpay expects integer paise and a single synchronous call. Paytm is async — initiate a payment, then poll for status. Stripe uses floats and mirrors your interface by coincidence. Business logic cannot afford to know which provider it is talking to.

How Adapter helps: Each provider gets one adapter struct that implements PaymentProcessor. The business logic calls Checkout(processor, amount, currency) — it has no knowledge of paise conversion, retry loops, or SDK method names. Swapping providers at runtime is an injection concern, not a code-change concern.

Special case — async providers: PaytmAdapter hides a retry loop behind ProcessPayment. From the caller's perspective, every PaymentProcessor is synchronous and returns (error). The polling complexity — including back-off and ErrMaxRetriesReached — is entirely contained inside the adapter.


2. Third-Party Logging Libraries

Problem: Your application defines a Logger interface with Info, Warn, and Error. You start with zerolog, then need to switch to zap in one service. Both libraries have different method names, different field attachment APIs, and different initialization patterns.

How Adapter helps: A ZapAdapter and a ZerologAdapter each implement your Logger interface. Call sites never import zap or zerolog directly — they depend only on your interface. Switching the logger for a service is a one-line change at the injection point.

Special case — field attachment: If zap requires zap.String("key", val) while your interface uses WithField(key, val) Logger, the adapter owns the translation. The rest of the application never learns the difference.


3. Cloud Storage Providers

Problem: You build against AWS S3. Six months later, a client requires Azure Blob Storage. The SDKs have different method names (PutObject vs UploadBlockBlob), different authentication flows, different error types, and different streaming APIs.

How Adapter helps: Define a StorageClient interface with Upload, Download, and Delete. Wrap each SDK in an adapter. The application's file-handling code is written once against the interface; adapters handle SDK-specific details like multipart upload thresholds, SAS token generation, or S3 presigned URLs.

Special case — error normalization: SDK-specific errors (s3.NoSuchKeyError, azblob.StorageError) are translated into your domain errors (ErrNotFound, ErrPermissionDenied) inside the adapter. Business logic branches on your errors, not SDK internals.


4. Legacy System Integration

Problem: A company has a 15-year-old billing service with a SOAP XML API. New services are built with gRPC. Writing gRPC-to-SOAP translation logic in every new service that needs billing data is error-prone and duplicates the integration concern.

How Adapter helps: A BillingAdapter implements a modern BillingService gRPC interface while internally constructing SOAP envelopes, calling the legacy endpoint, and parsing the XML response. New services call clean gRPC methods; the legacy protocol is invisible.

Special case — protocol version pinning: The adapter can pin the legacy API version and absorb schema changes from the legacy side without touching any downstream service.


5. Database Driver Abstraction

Problem: Your application uses PostgreSQL. Tests should run against SQLite for speed. A customer deployment requires MySQL. Each driver has a different Go package, different DSN format, and subtly different SQL dialect support.

How Adapter helps: A DB interface exposes Query, Exec, and Transaction. Each driver gets an adapter that wraps its specific client. Tests inject an SQLiteAdapter; production injects a PostgresAdapter. The repository layer never imports a driver package directly.

Special case — transaction semantics: Drivers implement transactions differently. The adapter owns the mapping between your Transaction interface and the driver's BeginTx / Commit / Rollback calls, ensuring consistent semantics regardless of which driver is active.


Summary: When Adapter earns its complexity

Application What Adapter prevents
Payment Gateways Business logic knowing about paise, polling, or provider SDKs
Logging Libraries Call sites importing or branching on specific logger packages
Cloud Storage SDK-specific methods and error types leaking into application code
Legacy Systems SOAP/XML or other protocol details spreading across new services
Database Drivers Driver packages imported directly by repository or service layers

The interview insight: Adapter is the right choice when you own one side of an interface boundary and cannot change the other. Its value is that it localises all the translation, conversion, and protocol-bridging work to one struct — every other part of the system sees a clean, consistent interface and remains blissfully unaware of what sits behind it.