Clean Architecture (and similar patterns like Hexagonal/Ports-and-Adapters architecture) organizes code so business logic doesn’t depend on frameworks, databases, or UI details — a genuinely valuable discipline once an application outgrows a simple CRUD script.
# Business logic (the "core") knows NOTHING about the database or web framework
class OrderService:
def __init__(self, order_repository, payment_gateway):
self.orders = order_repository # an INTERFACE, not a concrete database class
self.payments = payment_gateway # same - an interface
def place_order(self, items, customer_id):
total = sum(item.price for item in items)
if not self.payments.charge(customer_id, total):
raise PaymentFailedError()
return self.orders.save(Order(items, customer_id, total))
# Concrete implementations live at the OUTER layer, plugged in via dependency injection
class PostgresOrderRepository:
def save(self, order): ... # actual SQL here
class StripePaymentGateway:
def charge(self, customer_id, amount): ... # actual Stripe API calls here
Why This Separation Matters
OrderService can be tested completely without a real database or payment provider — just pass in fake/mock implementations of the interfaces. It can also be unit-tested in milliseconds rather than requiring a real database connection, and swapping Stripe for a different payment provider later touches only the outer layer, never the core business logic.