Gateway Pattern
Also Known As
service gateway
API wrapper
external service adapter
TL;DR
A class that encapsulates access to an external system or resource — wrapping HTTP APIs, queues, or legacy systems behind a clean interface that the rest of the application uses.
Explanation
A Gateway (Martin Fowler, PoEAA) isolates external dependencies behind an interface. PaymentGateway, EmailGateway, SMSGateway — each wraps one external system. Benefits: the application code never calls the HTTP client or SDK directly; the gateway can be mocked in tests; switching providers means only changing the gateway implementation. Similar to Adapter but typically wraps external systems rather than incompatible interfaces. Distinguished from Repository (data storage) by focusing on external service calls.
Common Misconception
✗ Gateways and adapters are the same — both wrap external interfaces, but a Gateway specifically wraps access to external systems (APIs, queues); an Adapter translates between two existing interfaces.
Why It Matters
Without a gateway, Stripe's SDK is called directly throughout the codebase — switching to Braintree requires hunting down every Stripe call; with a gateway, only one class changes.
Common Mistakes
- Gateway that leaks provider-specific exceptions — wrap them in domain exceptions.
- Fat gateway with too much logic — gateways translate calls, not implement business rules.
- Not defining a gateway interface — without an interface, mocking in tests requires more work.
- One giant ExternalServiceGateway — one gateway per external system.
Code Examples
✗ Vulnerable
// No gateway — Stripe SDK scattered everywhere:
class OrderService {
public function charge(Order $order): void {
$stripe = new \Stripe\StripeClient(getenv('STRIPE_KEY'));
$charge = $stripe->charges->create([
'amount' => $order->total->cents(),
'currency' => 'gbp',
'source' => $order->paymentToken,
]);
// Stripe-specific code in domain service
}
}
✓ Fixed
// Gateway isolates the provider:
interface PaymentGateway {
public function charge(Money $amount, string $token): PaymentResult;
}
class StripeGateway implements PaymentGateway {
public function charge(Money $amount, string $token): PaymentResult {
$charge = $this->stripe->charges->create([...]);
return new PaymentResult($charge->id, $charge->status);
}
}
// Domain service depends on interface:
class OrderService {
public function __construct(private PaymentGateway $payments) {}
public function charge(Order $order): void {
$result = $this->payments->charge($order->total, $order->token);
}
}
Tags
🤝 Adopt this term
£79/year · your link shown here
Added
16 Mar 2026
Edited
22 Mar 2026
Views
20
🤖 AI Guestbook educational data only
|
|
Last 30 days
Agents 0
No pings yet today
No pings yesterday
Amazonbot 6
Perplexity 4
Google 2
Unknown AI 2
Majestic 1
Ahrefs 1
Also referenced
How they use it
crawler 15
crawler_json 1
Related categories
⚡
DEV INTEL
Tools & Severity
🟡 Medium
⚙ Fix effort: Medium
⚡ Quick Fix
Create a Gateway class as the single entry point to an external system — all HTTP calls, data mapping, and error handling live in the gateway, keeping your domain code clean
📦 Applies To
any
web
cli
queue-worker
🔗 Prerequisites
🔍 Detection Hints
Direct Guzzle or cURL calls scattered across service classes; external API error handling duplicated in multiple places; no single abstraction for a third-party service
Auto-detectable:
✗ No
phpstan
deptrac
⚠ Related Problems
🤖 AI Agent
Confidence: Low
False Positives: High
✗ Manual fix
Fix: Medium
Context: Class
Tests: Update