Courses/Python/Architecture Patterns for Python Apps (MVC, Clean Architecture)
    Back to Course

    Lesson 39 โ€ข Advanced

    Architecture Patterns for Python Apps (MVC, Clean Architecture)

    Master architectural patterns that separate concerns, enforce dependency rules, and build maintainable Python applications that scale from prototype to production

    What You'll Learn

    • What architecture means in Python applications
    • MVC pattern and its modern interpretations
    • Clean Architecture principles and dependency rules
    • Ports and Adapters (Hexagonal Architecture)
    • Layer separation: Domain, Application, Infrastructure, Interface
    • Dependency inversion and abstraction
    • Building testable, modular systems
    • Real project structures for production apps
    • Anti-patterns to avoid
    • Refactoring strategies for existing codebases
    • When to use complex architecture vs. simple patterns
    • Testing strategies for each layer

    What Is Architecture?

    Architecture is how you organize code and responsibilities in an application. It determines where business rules live, how modules depend on each other, and how easy it is to change, test, and extend your system.

    โŒ Bad Architecture Symptomsโœ… Good Architecture Signs
    "God files" with 1000+ linesSmall, focused modules
    Business logic mixed with SQL/HTTPClear layer boundaries
    Can't test without starting serversEach component testable in isolation
    Changing DB breaks half the appCan swap DB without major rewrites
    New features cause ripple effectsFeatures added without breaking others

    MVC Pattern Basics

    Model-View-Controller separates an application into three interconnected components:

    Model

    Represents data and business rules:

    • Domain entities (User, Order, Product)
    • Validation rules (email format, stock limits)
    • Business operations (apply discount, cancel order)
    • Domain logic that exists regardless of UI

    View

    Responsible for presenting data:

    • HTML templates (Django, Flask + Jinja2)
    • JSON responses (FastAPI, Flask-RESTful)
    • CLI output formatting
    • GUI windows

    Views should be thin: format and present data, don't implement business rules

    Controller

    Coordinates between Model and View:

    1. Accept request/input
    2. Validate and parse data
    3. Call appropriate business logic
    4. Select and render the view

    Clean Architecture Core Principle

    "Dependencies point inward toward business rules, never outward."

    This single rule prevents most long-term maintainability problems. Business logic should never depend on frameworks, databases, or UI code.

    The Four Layers

    1. Domain / Entities (Core)

    Pure business objects and rules. Zero framework or database imports. Contains entities, value objects, and domain services.

    2. Application / Use Cases

    Orchestrates domain actions. Implements "what happens when..." logic. Defines interfaces (ports) for external dependencies.

    3. Infrastructure / Adapters

    Implements interfaces defined by inner layers. Contains DB, HTTP clients, file storage, external API wrappers, caching, queues.

    4. Interface / Presentation

    Entry points: web routes, CLI commands, event handlers, cron jobs. Thin layer that delegates to use cases.

    Dependency Inversion Principle

    Key Principle: High-level modules should not depend on low-level modules. Both should depend on abstractions (interfaces).

    โœ— Bad: Direct Dependency

    # use_case.py
    from infrastructure.postgres import PostgresUserRepo
    
    def register_user(email: str):
        repo = PostgresUserRepo()  # Tightly coupled!
        repo.save(user)

    Problem: Use case knows about PostgreSQL. Can't test without DB. Can't switch to MongoDB.

    โœ“ Good: Depend on Abstraction

    # interfaces.py
    class UserRepository(Protocol):
        def save(self, user: User) -> None: ...
    
    # use_case.py
    def register_user(email: str, repo: UserRepository):
        repo.save(user)  # Works with any implementation!

    Benefit: Use case depends on interface. Can test with fakes. Infrastructure is swappable.

    Ports and Adapters (Hexagonal)

    Hexagonal Architecture formalizes Clean Architecture with two key concepts:

    Ports

    Abstract interfaces defining what the application needs:

    • โ€ข UserRepository
    • โ€ข EmailSender
    • โ€ข PaymentGateway
    • โ€ข NotificationService

    Adapters

    Concrete implementations of ports:

    • โ€ข PostgresUserRepository
    • โ€ข SMTPEmailSender
    • โ€ข StripePaymentGateway
    • โ€ข SlackNotificationService

    Key Benefits

    • โ€ข Switch PostgreSQL โ†’ SQLite without touching business logic
    • โ€ข Test workflows with fake implementations (no network/DB)
    • โ€ข Change from CLI โ†’ Web UI โ†’ Mobile without rewriting core logic
    • โ€ข Each adapter is isolated - easier to maintain and replace

    Production Project Structure

    A professional Python application typically follows this structure:

    project/
    โ”‚
    โ”œโ”€โ”€ domain/                   # Core business logic
    โ”‚   โ”œโ”€โ”€ entities/            # Business objects
    โ”‚   โ”‚   โ”œโ”€โ”€ user.py
    โ”‚   โ”‚   โ”œโ”€โ”€ order.py
    โ”‚   โ”‚   โ””โ”€โ”€ product.py
    โ”‚   โ”œโ”€โ”€ value_objects/       # Immutable values
    โ”‚   โ”‚   โ”œโ”€โ”€ email.py
    โ”‚   โ”‚   โ””โ”€โ”€ money.py
    โ”‚   โ””โ”€โ”€ services/            # Domain services
    โ”‚       โ””โ”€โ”€ pricing.py
    โ”‚
    โ”œโ”€โ”€ application/             # Use cases / workflows
    โ”‚   โ”œโ”€โ”€ use_cases/
    โ”‚   โ”‚   โ”œโ”€โ”€ create_order.py
    โ”‚   โ”‚   โ”œโ”€โ”€ pay_order.py
    โ”‚   โ”‚   โ””โ”€โ”€ cancel_order.py
    โ”‚   โ”œโ”€โ”€ dto/                 # Data transfer objects
    โ”‚   โ”‚   โ””โ”€โ”€ order_dto.py
    โ”‚   โ””โ”€โ”€ interfaces/          # Ports (abstractions)
    โ”‚       โ”œโ”€โ”€ repositories.py
    โ”‚       โ””โ”€โ”€ gateways.py
    โ”‚
    โ”œโ”€โ”€ infrastructure/          # Technical implementations
    โ”‚   โ”œโ”€โ”€ database/
    โ”‚   โ”‚   โ”œโ”€โ”€ sql_repository.py
    โ”‚   โ”‚   โ””โ”€โ”€ models.py       # ORM models
    โ”‚   โ”œโ”€โ”€ cache/
    โ”‚   โ”‚   โ””โ”€โ”€ redis_cache.py
    โ”‚   โ”œโ”€โ”€ email/
    โ”‚   โ”‚   โ””โ”€โ”€ smtp_sender.py
    โ”‚   โ””โ”€โ”€ external/
    โ”‚       โ””โ”€โ”€ payment_api.py
    โ”‚
    โ”œโ”€โ”€ interface/               # Entry points
    โ”‚   โ”œโ”€โ”€ api/                 # REST API
    โ”‚   โ”‚   โ”œโ”€โ”€ routes.py
    โ”‚   โ”‚   โ””โ”€โ”€ schemas.py
    โ”‚   โ”œโ”€โ”€ cli/                 # Command line
    โ”‚   โ”‚   โ””โ”€โ”€ commands.py
    โ”‚   โ””โ”€โ”€ events/              # Event handlers
    โ”‚       โ””โ”€โ”€ consumers.py
    โ”‚
    โ”œโ”€โ”€ tests/
    โ”‚   โ”œโ”€โ”€ unit/                # Domain & use case tests
    โ”‚   โ”œโ”€โ”€ integration/         # Infrastructure tests
    โ”‚   โ””โ”€โ”€ e2e/                 # Full system tests
    โ”‚
    โ””โ”€โ”€ config/                  # Configuration
        โ”œโ”€โ”€ settings.py
        โ””โ”€โ”€ container.py         # DI container

    Testing Different Layers

    Test TypeWhat It TestsSpeed
    UnitDomain entities, value objectsโšก Milliseconds
    IntegrationUse cases with fake repositoriesโšก Milliseconds
    InfrastructureAdapters with real DB/APIs๐Ÿข Seconds
    E2EFull system, HTTP โ†’ DB๐ŸŒ Minutes

    Common Anti-Patterns

    Anti-PatternThe ProblemThe Fix
    Fat ControllerRoute handler does EVERYTHINGExtract logic to use cases
    Anemic DomainEntities are just data bagsPut behavior where data is
    DB-Driven DesignModels mirror DB tablesDesign domain first, map later
    Framework-FirstLogic coupled to Django/FlaskCore logic is framework-agnostic
    God ObjectOne class knows everythingBreak into focused modules

    Refactoring Toward Clean Architecture

    Follow these steps in order:

    1. 1.

      Identify Core Business Rules

      Extract pure logic into separate modules with no framework/DB imports

    2. 2.

      Introduce Interfaces (Ports)

      Define Protocol interfaces for repositories and external services

    3. 3.

      Wrap External Code in Adapters

      Move SQL, HTTP, cache logic into adapter modules implementing interfaces

    4. 4.

      Create Use Case Classes

      Extract workflow orchestration into dedicated use case modules

    5. 5.

      Thin Out Controllers

      Controllers become: parse input โ†’ call use case โ†’ format response

    When to Use Clean Architecture

    โœ“ Use Clean Architecture When:

    • โ€ข Building production applications
    • โ€ข Multiple developers on the team
    • โ€ข Long-term maintenance expected
    • โ€ข Multiple interfaces (web + CLI + mobile)
    • โ€ข Business logic is complex
    • โ€ข High refactoring risk
    • โ€ข Need comprehensive testing
    • โ€ข Compliance/audit requirements

    โš  Simple Structure Is Better For:

    • โ€ข Small personal projects
    • โ€ข One-off scripts or tools
    • โ€ข MVPs with unclear requirements
    • โ€ข Solo developer, low complexity
    • โ€ข Short-lived applications
    • โ€ข Prototypes and experiments
    • โ€ข Simple CRUD applications
    • โ€ข When speed matters more than structure

    Choose architecture proportional to your project's expected lifespan and complexity.

    Validating Your Architecture

    Ask yourself these questions to evaluate your architecture:

    Can I change the database without rewriting core logic?

    โœ“ Good architecture isolates DB behind interfaces

    Can I test workflows without starting servers or databases?

    โœ“ Use cases should work with fake implementations

    Does business logic import framework modules?

    โœ— Red flag - domain should be framework-agnostic

    Is complexity growing linearly or exponentially?

    โœ“ Good architecture scales linearly as features are added

    Can I add a CLI interface without touching existing code?

    โœ“ Multiple interfaces should share the same core logic

    Complete Architecture Example

    Domain entities, use cases, and repository pattern in action

    Try it Yourself ยป
    Python
    # Python Architecture Patterns Examples
    
    # ============================================
    # 1. DOMAIN ENTITY WITH BUSINESS RULES
    # ============================================
    
    from dataclasses import dataclass, field
    from decimal import Decimal
    from typing import List
    from datetime import datetime
    
    @dataclass
    class OrderItem:
        """Pure domain entity - no dependencies"""
        product_id: str
        product_name: str
        quantity: int
        unit_price: Decimal
        
        def subtotal(self) -> Decimal:
      
    ...

    Key Takeaways

    • Architecture determines how easy your app is to change, test, and grow
    • MVC separates presentation, business logic, and control flow
    • Clean Architecture enforces dependency inversion: core depends on nothing
    • Layers: Domain (core) โ†’ Application (use cases) โ†’ Infrastructure (adapters) โ†’ Interface (API/CLI)
    • Ports and Adapters make external dependencies swappable
    • Test domain logic without databases or networks using fakes
    • Avoid anti-patterns: fat controllers, anemic models, database-driven design
    • Refactor incrementally - you don't need to rewrite everything at once
    • Choose architecture complexity proportional to project lifespan
    • Good architecture makes growth linear, not exponential in complexity

    ๐Ÿ“‹ Quick Reference โ€” Architecture Patterns

    PatternWhat it achieves
    MVCSeparates data, display, and logic
    Clean ArchitectureDomain core independent of frameworks
    Repository PatternAbstract data access behind an interface
    Dependency InjectionDecouple components for testability
    Event-Driven ArchitectureDecouple services via events/messages

    ๐ŸŽ‰ Great work! You've completed this lesson.

    You can now apply Clean Architecture, MVC, and the Repository Pattern to structure Python apps that scale across teams and years.

    Up next: Final Project โ€” bring everything together and build a portfolio-worthy Python project.

    Sign up for free to track which lessons you've completed and get learning reminders.

    Previous

    Cookie & Privacy Settings

    We use cookies to improve your experience, analyze traffic, and show personalized ads. You can manage your preferences below.

    By clicking "Accept All", you consent to our use of cookies for analytics and personalized advertising. You can customize your preferences or reject non-essential cookies.

    Privacy Policy โ€ข Terms of Service