
Inheritance Isn't Dead - Your Payment Gateway Proves It
Why composition over inheritance dogma fails in real payment systems, and how to blend both approaches for practical software design.
The “composition over inheritance” mantra has been beaten into developers’ heads so thoroughly that we’ve forgotten inheritance actually solves real problems. While everyone’s busy arguing about cats and dogs, payment gateways are quietly proving that inheritance isn’t the villain, dogmatic thinking is.
When Inheritance Actually Makes Sense
Payment gateways don’t care about your ideological purity. They care about consistency, authentication, and not getting sued. When every API call to Opayo requires the same HTTP headers, authentication, and request structure, inheritance isn’t just convenient, it’s responsible design.
Consider the AbstractPaymentRequest
pattern: a base class that handles authentication headers, content types, and the actual HTTP request sending. Child classes only need to specify their endpoint and payload. This isn’t academic nonsense, it’s the Template Method Pattern in production, enforcing consistency across dozens of payment operations.
The irony? This inheritance approach actually supports the Open-Closed Principle better than many composition-heavy alternatives. New payment operations become a matter of extending one class rather than configuring multiple components.
Compare this to the composition alternative where you’d need separate classes for authentication, request building, header management, and HTTP execution. When you need to add SSL certificate pinning or request retry logic, inheritance lets you modify one place. Composition forces you to coordinate changes across multiple classes.
Where Composition Saves Your Architecture
Here’s where the purists get it right: payment method handling. When your gateway needs to support cards, PayPal, Apple Pay, and Google Pay, with more coming next quarter, inheritance becomes a nightmare.
This composition approach lets you swap payment methods without touching the request logic. New payment method? Implement the PaymentMethodBuilder
interface. No regression testing on the entire payment request stack. No fragile inheritance hierarchy that breaks when Visa decides to change their API.
The Blended Reality of Production Systems
The real world doesn’t care about your purity tests. Payment systems use inheritance for consistency where it makes sense (HTTP communication, authentication) and composition for flexibility where it’s needed (payment methods, fraud checks, currency handling).
The most effective architects understand that “prefer composition over inheritance” means exactly that: prefer, not always use. They recognize that:
- Inheritance excels at enforcing consistency across related operations
- Composition shines when dealing with variable behaviors that change independently
- The template method pattern is inheritance’s secret weapon for real-world systems
- Payment gateways, unlike animals, have concrete business constraints that dictate design choices
The Dogma Costs More Than You Think
Teams that religiously avoid inheritance often end up with:
- Boilerplate code that manually delegates to components
- Configuration complexity that rivals their actual business logic
- Missed opportunities for compile-time safety through type hierarchies
- Architecture that’s flexible in theory but brittle in practice
Meanwhile, teams that understand both approaches build systems that can handle Stripe’s API changes, PayPal’s quirks, and Apple Pay’s requirements without rewriting their entire payment stack.
The next time someone tells you “inheritance is bad”, ask them how they’d build a payment gateway that needs to maintain consistency across 20+ API endpoints while supporting an ever-growing list of payment methods. The answer probably involves both inheritance and composition, because real engineering isn’t about purity, it’s about solving problems.