Skip to content

Clean Architecture in Python: nexusx vs. Mainstream Frameworks

Updated 2026-06

What Is Clean Architecture

Clean Architecture's central claim: business logic depends on no framework, database, UI, or any external agency. Through dependency inversion, high-level policy (use cases) is decoupled from low-level detail (database, API delivery), so the same business logic can be reused across multiple delivery modes.

Three things determine a framework's Clean Architecture maturity:

  1. Entity definition — is the domain model decoupled from infrastructure (ORM, serialization framework)?
  2. DTO / response assembly — is there an independent response-construction layer that auto-generates external contracts from entity models?
  3. Multi-API surface — can the same business logic serve REST, GraphQL, MCP, and other delivery modes simultaneously?

Framework Overview

Framework Entity definition DTO mechanism Business logic API generation Multi-API surface MCP DI
nexusx SQLModel + Relationship DefineSubset metaclass auto-gen UseCaseService (@query / @mutation) Auto (SDL + REST + MCP) GraphQL + REST + MCP Native Loader injection + FromContext
Litestar SQLAlchemy / Pydantic DTO factory auto-gen Controller + Service + DI Semi-auto (Controller decorators) REST + GraphQL (plugin) + WebSocket None Built-in DI (layered scopes)
Django + DRF Django Model ModelSerializer auto-gen Fat Model / Service Layer Auto (ViewSet + Router) REST + GraphQL (third-party) + Admin None No DI container
Strawberry @strawberry.type Pydantic integration bridge Resolver + DataLoader Auto (SDL from code) GraphQL only None Extensions
FastAPI + SQLModel SQLModel(table=True) Manual inheritance variants Manual Service / Repository Manual (per-endpoint) REST only None Depends
Ariadne External ORM SDL as DTO Manual resolver binding Manual (hand-written SDL) GraphQL only None None
Tartiflette External ORM SDL as DTO Manual resolver binding Manual (hand-written SDL) GraphQL only None Directives
Temporalio dataclass / Pydantic Parameters / return values Workflow + Activity separation N/A (workflow engine) N/A N/A N/A (Worker registration, not traditional DI)

nexusx's Architecture

                    ┌──────────────────────────────┐
                    │   Delivery Layer              │
                    │   GraphQL / REST / MCP / CLI │
                    └──────────┬───────────────────┘
                               │ Auto-generated by protocol builders
                    ┌──────────▼──────────────────┐
                    │   Use Case / Business Logic  │
                    │   UseCaseService            │
                    │   @query / @mutation        │
                    │   Method body = logic entry │
                    └──────────┬──────────────────┘
                               │ Method may call Resolver to assemble DTO
                    ┌──────────▼───────────────────┐
                    │   Response Assembly (DTO)     │
                    │   DefineSubset → Resolver    │
                    │   BFS traversal + post_*     │
                    └──────────┬───────────────────┘
                               │ Load relationships
                    ┌──────────▼──────────────────┐
                    │   Data Access Layer           │
                    │   ErManager + DataLoader    │
                    │   SQLModel Entity           │
                    └─────────────────────────────┘

Key design decisions:

  • UseCaseService methods are the business-logic entry point: the classmethods decorated with @query / @mutation are simultaneously the external contract (translated by each protocol builder into GraphQL fields / REST endpoints / MCP tools) and the executable body of business logic. The method body can be the full business logic or a thin wrapper calling external pure functions (a project-structure choice, not enforced by the library).
  • DefineSubset is a declarative DTO: auto-generates a Pydantic model from SQLModel entity metadata; the field set is declared explicitly by the developer via __subset__ (including whether FK columns are included).
  • Resolver is a model-driven response builder: walks the object tree via BFS, automatically batch-loading relational data.
  • The same UseCaseService method serves multiple APIs: translated to different protocols via builders like create_use_case_graphql_mcp_server / create_use_case_router / build_cli, with zero changes to the business method.

Detailed Framework Analysis

Litestar — Closest to nexusx's Philosophy

Litestar's DTO factory pattern is conceptually similar to nexusx's DefineSubset: auto-generates request/response schemas from SQLAlchemy/Pydantic models, supporting field exclusion and renaming. But Litestar's DTO only serves REST — no unified GraphQL or MCP generation.

Strengths: - DTOs are first-class citizens, auto-generated from models, with field-level control - Built-in DI container with layered scopes (transient / scoped / singleton) - Plugin system for extensibility (SQLAlchemy, Redis, etc.) - Supports multiple serialization backends (Pydantic, msgspec, attrs)

Weaknesses: - GraphQL requires the Strawberry plugin — no unified generation - No MCP support - DTO factory is oriented around OpenAPI schema, not multi-API response assembly - Smaller community, limited production cases

Best for: REST-only projects that value DTO automation and DI.

Django + DRF — Highest Automation

Django's Model → ModelSerializer → ModelViewSet → Router pipeline generates a complete CRUD REST API + Admin UI in four steps. Among all frameworks, this is the highest level of automation.

Strengths: - ModelSerializer auto-generates serializers from Django Models - ModelViewSet + Router auto-generates CRUD endpoints - Django Admin auto-generates an admin UI (an irreplaceable productivity tool) - graphene-django auto-generates GraphQL schemas from Models - Mature ecosystem, many production cases

Weaknesses: - Django Model is tightly coupled with ORM — domain models cannot be independent of the database - No built-in DI container, relies on module-level imports (anti-Clean-Architecture) - Clean Architecture refactoring is expensive, requires manually introducing a Service Layer - No MCP support - Fat Model pattern mixes business logic with data access

Best for: Projects with mostly CRUD delivery needs, where strict layering is not a priority.

Strawberry — Best GraphQL Ecosystem

Strawberry is the most mature code-first GraphQL solution in Python. Pydantic integration auto-bridges models, and DataLoader is built-in to prevent N+1.

Strengths: - Code-first, SDL auto-generated from Python code - Pydantic integration (strawberry.experimental.pydantic.type) auto-bridges models - Built-in DataLoader abstraction, but each resolver must be wired manually (unlike nexusx, which is fully automatic from ORM metadata) - Supports Relay spec (cursor pagination), Subscription, Federation 2.x - Active community, high-quality docs

Weaknesses: - GraphQL only — no REST or MCP support - Pydantic bridging requires manually defining strawberry.type (unlike nexusx's fully automatic DefineSubset) - No built-in DI (relies on FastAPI's Depends) - Response assembly requires hand-written resolver logic

Best for: GraphQL-only projects that value type safety and developer experience.

FastAPI + SQLModel — Minimal Framework Constraint

This is the most "frameworkless" option: a single SQLModel class serves as both ORM model and Pydantic validation model, and FastAPI auto-generates OpenAPI docs from type annotations. Clean Architecture is entirely up to the developer.

Strengths: - Low learning curve, rich FastAPI documentation - SQLModel single-class-multi-role reduces boilerplate - FastAPI's Depends provides lightweight DI - OpenAPI docs auto-generated - Largest community, richest ecosystem

Weaknesses: - No DTO auto-generation mechanism (must manually define Create/Update/Read variants) - No business-logic organization pattern (must manually introduce Service/Repository layers) - Each endpoint defined manually — high repetition - No GraphQL or MCP support - Clean Architecture requires substantial manual implementation

Best for: Small-to-medium projects where the team already has FastAPI experience and strict layering is not a priority.

Ariadne / Tartiflette — Schema-first GraphQL

Both are Schema-first (SDL-first) solutions: write the GraphQL schema first, then bind resolvers. The opposite of nexusx's model-driven (code-first) philosophy.

Strengths (Ariadne): - SDL is the front-end/back-end contract — clear collaboration - Ariadne Codegen generates client code (Pydantic models) from the schema - Framework-agnostic, integrates with Django, Flask, FastAPI

Weaknesses: - SDL must be hand-written — high maintenance cost - Manual resolver binding — lots of boilerplate - No DTO conversion layer; resolvers operate directly on Python objects - No REST or MCP support - Tartiflette community activity is questionable

Best for: GraphQL projects where front-end and back-end teams need SDL as a collaboration contract.

Temporalio — Clean Architecture for Workflow Orchestration

Temporalio's Workflow/Activity separation is a natural implementation of dependency inversion: Workflows are the deterministic orchestration layer (similar to Use Cases), Activities are the side-effectful business layer (similar to Repositories/Gateways).

Strengths: - Workflow/Activity separation enforces dependency inversion - Event sourcing auto-persists state - Native support for long-running executions, retries, timeouts - Idempotency is a design constraint, not an optional practice

Weaknesses: - Not an API framework — cannot directly generate REST/GraphQL/MCP - Workflows must be deterministic (no randomness, network calls, or time retrieval inside) - Steep learning curve - Requires Temporal Server infrastructure

Best for: Long-running workflow orchestration (order processing, approval flows, data pipelines); must be used alongside an API framework.

Core-Dimension Comparison

1. Entity-DTO Decoupling

Framework Entity→DTO approach Automation level
nexusx DefineSubset metaclass auto-generates Pydantic DTOs from SQLModel metadata High (declarative)
Litestar DTO factory auto-generates from SQLAlchemy/Pydantic models High (factory pattern)
Django ModelSerializer auto-generates from Django Model High (reflection)
Strawberry Pydantic integration bridge, requires manually defining @strawberry.type Medium (semi-auto)
FastAPI Manually define Create/Update/Read inheritance variants Low (pure manual)
Ariadne Hand-written SDL, no DTO layer None

nexusx and Litestar lead on DTO automation. nexusx's DefineSubset goes further — it controls both DTO generation and relationship-loading strategy (via Resolver), forming a complete response-assembly pipeline.

2. Multi-API Surface Unification

Framework REST GraphQL MCP Unification level
nexusx create_use_case_router() GraphQLHandler create_use_case_graphql_mcp_server() Three-way unified
Litestar Native Strawberry plugin None Two-way (not unified)
Django DRF graphene-django None Two-way (not unified)
Strawberry None (needs FastAPI) Native None Single surface
FastAPI Native Third-party required None Single surface

nexusx is the only framework that achieves unified auto-generation across three surfaces (REST + GraphQL + MCP). Others require writing adapter code per surface when targeting multiple APIs.

3. Response-Assembly Complexity

When API responses need to assemble nested relational data (e.g. "return a Sprint with its Task list, each Task including its owner"):

Framework Approach N+1 resolution
nexusx Resolver BFS traversal + DataLoader batch loading Automatic (ErManager)
Litestar SQLAlchemy eager load / manual DTO control Manual (selectinload)
Django select_related / prefetch_related Manual (must remember to add)
Strawberry Manual DataLoader integration Built-in DataLoader
FastAPI Manual query + Pydantic serialization Manual

nexusx's Resolver + ErManager combination is the only solution that achieves "declarative DTO definition + automatic batch relationship loading." Other frameworks either require manual loading-strategy control (Django, Litestar, FastAPI) or manual DataLoader logic (Strawberry).

4. Business Logic vs. Delivery Separation

Framework Separation approach Reuse level
nexusx UseCaseService methods (business-logic entry) → translated by per-protocol builders High (one method, four surfaces: GraphQL/REST/MCP/CLI)
Litestar Service injected into Controller via DI Medium (Service reusable, but Controllers must be written separately)
Django Fat Model / Service Layer Low (logic mixed into Views/Serializers)
Strawberry Resolver binding Medium (Resolver reusable, but GraphQL-only)
FastAPI Manually organized Service layer Medium (Service reusable, but manual wiring required)
Temporalio Activity implemented independently High (Workflow orchestrates Activities)

nexusx's UseCaseService pattern achieves complete decoupling of business logic from delivery: business methods focus purely on business, protocol translation is handled by per-protocol builders. Methods are independently testable (no dependency on any protocol runtime), and adding a new protocol only requires adding a new builder — zero changes to business code.

Extension: nexusx's official 4-phase skill recommends a stricter project structure (methods.py pure functions + user-defined _mount() to mount onto Entities), splitting "business logic" and "protocol entry" into separate layers. This is a project-organization convention, not a library requirement — see the "Extension: 4-phase skill's recommended project structure" section at the end.

nexusx's Unique Position

What It Achieves That Others Don't

  1. Four-surface unified generation: one set of SQLModel entities + UseCaseService business methods → auto-generated GraphQL + REST + MCP + CLI
  2. Declarative response assembly: DefineSubset specifies "what"; Resolver + ErManager automatically handle "how"
  3. MCP as a first-class citizen: AI agents can discover and invoke APIs through four-layer progressive disclosure (list_apps → describe_compose_schema → describe_compose_method → compose_query) — completely absent from other frameworks
  4. Model-driven contract generation: auto-generates GraphQL SDL and OpenAPI spec from SQLModel entities as the single source of truth for front-end/back-end collaboration

Trade-offs to Be Aware Of

  1. SQLModel binding: the entity layer is coupled to SQLModel/SQLAlchemy, less flexible than Litestar's multi-ORM backend support
  2. Python ecosystem niche: nexusx positions itself within the "SQLModel + FastAPI" ecosystem, narrower than Django's "batteries-included" scope
  3. Community size: as an emerging framework, production cases and community resources are smaller than FastAPI's or Django's
  4. Learning curve: DefineSubset + Resolver + ErManager + UseCaseService is a lot of concepts — non-trivial initial barrier

Selection Guide

Scenario Recommended framework Reason
New project needing GraphQL + REST + MCP nexusx Three-surface unified generation, only one with MCP
REST-only, values DI and DTO automation Litestar Most complete DTO factory + built-in DI
Fast CRUD + Admin backend Django + DRF Highest automation, Admin is irreplaceable
GraphQL-only, values type safety Strawberry Best Python GraphQL ecosystem
Lightweight API, team knows FastAPI FastAPI + SQLModel Lowest learning curve, largest community
Long-running workflow orchestration Temporalio Workflow/Activity is natural dependency inversion
Front-end/back-end SDL collaboration Ariadne Schema-first, Codegen supports client generation

The methods.py / _mount() / TS SDK generation mentioned earlier are not built-in features of the nexusx library — they are project scaffolding recommended by the official nexusx-4phase skill. The skill is a set of guidance for AI coding agents (in the repo's skill/ directory), splitting "build a complete project with nexusx" into four phases:

Phase Goal Key artifacts
Phase 1 ER modeling + mock data SQLModel entities + Relationship + ER diagram
Phase 2 Business methods + GraphQL service/<domain>/methods.py pure functions + user-defined _mount() to mount onto Entities
Phase 3 DTOs + multi-protocol dtos.py (DefineSubset) + service.py (UseCaseService) + REST/MCP/CLI
Phase 4 Front-end SDK OpenAPI spec → TypeScript SDK (via @hey-api/openapi-ts)

Core conventions recommended by the skill:

# service/project/methods.py — Phase 2 recommended structure
from sqlmodel import select

async def list_sprints(limit: int = 10) -> list[Sprint]:
    """Pure function: doesn't import nexusx, independently unit-testable."""
    async with get_session() as session:
        return (await session.exec(select(Sprint).limit(limit))).all()

# src/models.py — user defines _mount helper in their own project
from nexusx import query, mutation
from service.project.methods import list_sprints, create_sprint

def _mount(entity, fn, decorator):
    """Mount a methods.py pure function onto an entity as @query / @mutation."""
    setattr(entity, fn.__name__, classmethod(fn))
    # ... decorator registration details provided by the skill template

_mount(Sprint, list_sprints, query)
_mount(Sprint, create_sprint, mutation)

Additional benefits of this structure (not in the library itself, but encouraged by the skill):

  • Business logic is pure functions (no cls dependency, no decorator dependency) — unit tests don't need to start any protocol runtime
  • "Business logic" and "protocol entry" are physically separated — adding a protocol requires zero changes to business files
  • Phase 4 uses @hey-api/openapi-ts to translate the FastAPI-generated OpenAPI spec into a TypeScript SDK, keeping front-end/back-end type contracts in sync

Notes:

  • _mount() / methods.py are part of the skill template, not the nexusx library API. pip install nexusx does not give you these tools — they live in skills/nexusx-4phase/template/src/models.py and are provided by the skill for AI agents or developers to copy.
  • You can completely skip the skill's project structure and write @query methods directly inside a UseCaseService class body (the library's native usage). The skill only offers an optional convention with stricter separation of "business logic / protocol entry."
  • Install the skill: npx skills add KLR-Pattern/nexusx -s nexusx-4phase -a claude-code (see the repo's skills/nexusx-4phase/SKILL.md for details).