From design-patterns
Use when external system types, vocabulary, or conventions bleed into your domain model, a single provider change would require touching many internal files, or a legacy system must keep running while a new one is built alongside it. Apply before writing any integration with an external API, legacy codebase, or third-party service. Covers Anti-Corruption Layer, Gateway, Facade, and Strangler Fig.
How this skill is triggered — by the user, by Claude, or both
Slash command
/design-patterns:anti-corruptionThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
*Sources: Eric Evans — Domain-Driven Design (2003); Martin Fowler — PoEAA (2002), Refactoring (1999)*
Sources: Eric Evans — Domain-Driven Design (2003); Martin Fowler — PoEAA (2002), Refactoring (1999)
External systems have their own models, terminology, and constraints. Without an explicit translation layer, external concepts leak into your domain — corrupting it with foreign vocabulary, shapes, and assumptions. These patterns prevent that leakage.
An explicit translation layer between your domain model and an external system's model. Your domain never sees the external model directly.
Pain it removes: external types and vocabulary bleeding into domain code; domain logic breaking when an external API changes its response shape.
When to use: integrating with a legacy system, third-party API, or another bounded context using different concepts for the same things.
# External payment API — their vocabulary, their types
class StripeClient:
def create_payment_intent(self, amount_cents: int, currency: str) -> dict:
... # returns Stripe's response structure
# ACL — translates between domains; lives in infrastructure layer
class PaymentGatewayACL:
def __init__(self, stripe: StripeClient):
self._stripe = stripe
def authorize(self, money: Money) -> PaymentAuthorization:
# Translate YOUR domain type → external format
response = self._stripe.create_payment_intent(
amount_cents=int(money.amount * 100),
currency=money.currency.code.lower()
)
# Translate external response → YOUR domain type
return PaymentAuthorization(
id=PaymentId(response['id']),
status=self._map_status(response['status']),
authorized_at=datetime.utcnow()
)
def _map_status(self, stripe_status: str) -> PaymentStatus:
mapping = {
'requires_capture': PaymentStatus.AUTHORIZED,
'succeeded': PaymentStatus.CAPTURED,
'canceled': PaymentStatus.VOIDED,
}
return mapping.get(stripe_status, PaymentStatus.UNKNOWN)
Rules:
Encapsulates access to an external system or resource behind an interface that your domain defines. Hides the external system entirely, not just translating its model.
Pain it removes: business logic coupled to the specifics of an external service; inability to test business logic without calling the external system; difficulty swapping providers.
When to use: you want to decouple your application from external service specifics; you want swappable implementations (real, stub, test double); you want to prevent provider-specific concerns from leaking into business logic.
# Your domain defines the interface — it owns the vocabulary
class EmailGateway:
def send_order_confirmation(self, order: Order) -> None: ...
def send_password_reset(self, user: User, token: ResetToken) -> None: ...
def send_payment_failed_notice(self, subscription: Subscription) -> None: ...
# Infrastructure satisfies it — using SendGrid, SES, Mailgun, etc.
class SendGridEmailGateway(EmailGateway):
def send_order_confirmation(self, order: Order) -> None:
self._client.send(
to=str(order.customer_email),
subject=f"Order #{order.id} confirmed",
template_id=self._config.order_confirmation_template,
data={'order_number': str(order.id), 'total': str(order.total)}
)
# Test double — no external calls, full verification
class RecordingEmailGateway(EmailGateway):
def __init__(self):
self.sent: list[tuple] = []
def send_order_confirmation(self, order: Order) -> None:
self.sent.append(('order_confirmation', order.id))
Testing benefit: swap the real gateway for a recording double in tests — no external calls, full verification of what was sent and to whom.
Provides a simplified interface to a complex external subsystem or integration. Reduces the knowledge required to use the integration correctly.
(See also: structural skill for Facade in internal subsystem contexts)
When to use: an external integration requires multiple steps that must be called in sequence; you want to expose a single, safe entry point for the common cases.
class ReportingFacade:
"""Hides the complexity of the reporting pipeline from callers."""
def generate_monthly_summary(
self, tenant_id: str, month: YearMonth
) -> ReportUrl:
dataset = self._loader.load(tenant_id, month.start, month.end)
normalised = self._normaliser.normalise(dataset)
rendered = self._renderer.render(normalised, template='monthly_summary')
return self._uploader.upload(rendered)
Incrementally replaces a legacy system by routing traffic through a new implementation, gradually expanding the new surface until the legacy system can be retired.
Pain it removes: a legacy system that cannot be rewritten in one go; need to deliver value during migration; legacy must keep running throughout.
When to use: you cannot stop the world for a full rewrite; the legacy system must keep serving some traffic during migration; you want to prove the new system in production before fully committing.
# Phase 1 — proxy routes all requests to legacy
class OrderServiceProxy:
def get_order(self, id: str) -> Order:
return self._legacy.get_order(id) # 100% legacy
# Phase 2 — new system handles new-format IDs
class OrderServiceProxy:
def get_order(self, id: str) -> Order:
if id.startswith('ORD-'):
return self._new.get_order(id) # new format → new system
return self._legacy.get_order(id) # old format → legacy
# Phase N — legacy retired, proxy removed
Critical rule: the strangler proxy must be transparent to callers — same interface, same contract, same error semantics. Callers must not know which implementation served them.
Migration checklist:
npx claudepluginhub entelligentsia/skillforge --plugin design-patternsReplaces a legacy system incrementally by intercepting calls and routing traffic to a new implementation slice by slice, avoiding big-bang rewrites.
Designs incremental migration strategies for legacy systems using strangler fig pattern, dependency mapping, and API facades. Useful for modernizing monoliths or reducing technical debt.
Designs incremental migration strategies for legacy codebases including strangler fig pattern, service decomposition, dependency mapping, and API facades. Use when modernizing systems or reducing technical debt.