
Your whiteboard, shipped.
A Python framework for domain-driven systems. Sketch aggregates, events, and bounded contexts on a whiteboard. Then write them in Python, exactly as you drew them.
Start with DDD, evolve to CQRS or Event Sourcing, swap infrastructure through configuration.
Your domain model is the architecture.
Ship the Whiteboard Tutorial Why Protean? How Do I...?
Why Protean?
-
Domain Compiler
Your domain model is a machine-readable specification. Protean builds an Intermediate Representation (IR) that enables derived docs, API specs, contracts, and visual exploration.
-
Always-Valid Domain
Domain objects are always valid, or they don't exist. Four validation layers -- field constraints, value object invariants, aggregate rules, handler guards -- enforced on every change, automatically.
-
Progressive Architecture
Start with DDD, evolve to CQRS, adopt Event Sourcing -- all within the same framework. Mix patterns per aggregate. Pragmatism over purity.
-
Infrastructure Portability
Start with in-memory adapters -- no database, no broker, no setup. When you're ready, swap in PostgreSQL, Redis, Elasticsearch, or MessageDB through configuration. No code changes.
See it in action
from protean import Domain, handle
from protean.fields import Identifier, String, Text
from protean.utils.globals import current_domain
domain = Domain() # (1)!
@domain.aggregate # (2)!
class Post:
title: String(max_length=100, required=True)
body: Text(required=True)
status: String(max_length=20, default="DRAFT")
def publish(self):
self.status = "PUBLISHED"
self.raise_(PostPublished(post_id=self.id, title=self.title)) # (3)!
@domain.event(part_of="Post") # (4)!
class PostPublished:
post_id: Identifier(required=True)
title: String(required=True)
@domain.command(part_of="Post") # (5)!
class CreatePost:
title: String(max_length=100, required=True)
body: Text(required=True)
@domain.command_handler(part_of=Post) # (6)!
class PostCommandHandler:
@handle(CreatePost)
def create_post(self, command: CreatePost):
post = Post(title=command.title, body=command.body)
current_domain.repository_for(Post).add(post) # (7)!
return post.id
- Domain -- The central registry that wires all elements together.
- Aggregate -- The core building block encapsulating fields and business logic.
- Raising an Event --
raise_()emits a domain event to notify the rest of the system. - Event -- An immutable record of something that happened in the domain.
- Command -- An intent to change state, carrying just the needed data.
- Command Handler -- Receives a command, creates/updates aggregates, and persists them.
- Repository -- Built-in persistence abstraction to add, get, or remove aggregates without touching the database directly.
Aggregates, commands, events, and handlers -- all in pure Python, with decorators that wire everything together. No infrastructure required to get started.
Choose your path
Protean supports three architectural approaches. Each builds on the one before it -- start simple and add sophistication as your needs evolve.
| Path | Best for | |
|---|---|---|
| Domain-Driven Design | Clean domain modeling -- the simplest way to start | |
| CQRS | Separate reads from writes with commands and projections | |
| Event Sourcing | Full audit trail, temporal queries, and event replay |
Not sure? Start with DDD -- you can evolve later. See Choose a Path for a detailed comparison.
Built to last
-
3,826 Tests
3.5:1 test-to-code ratio. Every commit validated against PostgreSQL, Redis, Elasticsearch, MessageDB, and MSSQL.
-
Zero Lint Violations
Fully clean Ruff linting and formatting, enforced on every commit via pre-commit hooks.
-
A-Grade Maintainability
97% of source files score in the highest maintainability tier. Average cyclomatic complexity of 2.97.
-
12 Adapters, 5 Ports
Pluggable infrastructure across databases, brokers, event stores, and caches -- tested across 4 Python versions.
Explore the documentation
-
Hello, Protean!
Define, save, and load your first aggregate in under 20 lines.
-
Quickstart
Commands, events, and handlers in 5 minutes.
-
Tutorial
10-chapter tutorial from your first aggregate to production.
-
How Do I...?
Task-oriented index -- look up what you're trying to do and jump straight to the right guide.
-
Guides
Step-by-step instructions for every task Protean supports.
-
Core Concepts
DDD, CQRS, and Event Sourcing explained.
-
Adapters
PostgreSQL, Redis, Elasticsearch, MessageDB, and more.
-
Patterns & Recipes
Battle-tested solutions for common challenges.
-
Upgrading to 0.15?
Migration guide with required changes, behavioral differences, and what's new.