Skip to content
Open Source · Pydantic v2 · Python 3.10+

Clean Architecture for Python

Define business entities, declare relationships, let the framework assemble your data.

pip install pydantic-resolve

Provides the Missing Layer

Components map 1:1 to Clean Architecture — dependency direction always points inward.

Enterprise: Entity + ER Diagram

Define business entities and relationships independent of any framework or database — the stable core everything else derives from.

Application: Resolver + resolve/post

Automate data assembly orchestration — resolve loads missing data with batching, post computes derived fields after the tree is ready.

Adapters: Loader (data access)

Abstract data sources behind a unified interface. Swap SQLAlchemy for Django or Tortoise ORM — Entity and Response stay untouched.

N+1 solved automatically

Replace manual loops with declarative data loading.

Before — N+1 problem
# Sprint has 10 tasks, each needs owner
for task in sprint.tasks:
    task.owner = await get_user(task.owner_id)
 # 10 queries!
After — pydantic-resolve
class TaskView(BaseModel):
    owner_id: int
    owner: Optional[UserView] = None

    def resolve_owner(self, loader=Loader(user_loader)):
        return loader.load(self.owner_id)
 # 1 query!

From ER Model to Use Case

1
Define ER Diagram — Enterprise Business Rules
class UserEntity(BaseModel, BaseEntity):
    id: int
    name: str

class TaskEntity(BaseModel, BaseEntity):
    __relationships__ = [
        Relationship(fk='owner_id', target=UserEntity, loader=user_loader),
    ]
    id: int
    name: str
    owner_id: int

class StoryEntity(BaseModel, BaseEntity):
    __relationships__ = [
        Relationship(fk='id', target=list[TaskEntity], loader=task_loader),
    ]
    id: int
    name: str
2
Pick fields & hide FK — Use Case Composition
class TaskSummary(DefineSubset):
    __subset__ = (TaskEntity, ('id', 'name'))
    owner: Optional[UserEntity] = None  # implicit match, no AutoLoad

class StoryView(DefineSubset):
    __subset__ = (StoryEntity, ('id', 'name'))
    tasks: list[TaskSummary] = []      # implicit match
3
Query root data & resolve — Application Business Rules
@router.get("/stories")
async def get_stories():
    stories = [StoryView.model_validate(s)
               for s in await get_stories_from_db()]
    return await Resolver().resolve(stories)
4
Result — fully assembled response
[
  {
    "id": 1,
    "name": "Sprint 1",
    "tasks": [
      {
        "id": 101,
        "name": "Implement login",
        "owner": { "id": 1, "name": "Alice" }
      }
    ]
  }
]

Progressive learning path

Start simple, scale as needed. Each concept builds on the last.

1 resolve_*
2 Nested Structure
3 post_*
4 Cross-Layer
5 ERD + AutoLoad
6 GraphQL / MCP / FastAPI

Everything you need for data assembly

From loading related data to generating GraphQL APIs — all in one library.

Batch Loading

Auto-batch queries via DataLoader pattern. 100 lookups become 1 query.

Post Processing

Compute derived fields after all nested data is resolved. Counts, aggregates, formatting.

Cross-Layer Data Flow

ExposeAs, SendTo, Collector — pass context down or aggregate data up without traversal code.

ER Diagram + AutoLoad

Centralize relationship declarations. Reuse across models, GraphQL, and MCP services.

GraphQL Generation

Auto-generate GraphQL schema and resolvers from your ER Diagram.

MCP Service

Expose GraphQL APIs to AI agents via Model Context Protocol.

Built for your stack

Works with your existing frameworks and ORMs.

Thoroughly Tested, Production Ready

849+ Test Cases

Every feature is covered — from core resolution to ER Diagram, cross-layer data flow, and edge cases.

Stable API Contract

Entity-First architecture keeps your API independent of database changes. No more cascade refactoring.

CI / CD Verified

Every commit passes automated CI. Python 3.10–3.13 compatible with continuous integration.

Ready to build with Clean Architecture?

Define entities, declare relationships, let the framework assemble. Start from resolve_*, scale to ER Diagram when ready.