Skip to content

Protean

Your whiteboard, shipped.

Python Release Build Status Coverage Tests Maintainability

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.

Read more -- Why Protean?


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
  1. Domain -- The central registry that wires all elements together.
  2. Aggregate -- The core building block encapsulating fields and business logic.
  3. Raising an Event -- raise_() emits a domain event to notify the rest of the system.
  4. Event -- An immutable record of something that happened in the domain.
  5. Command -- An intent to change state, carrying just the needed data.
  6. Command Handler -- Receives a command, creates/updates aggregates, and persists them.
  7. 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.

Full Quality Report


Explore the documentation

  • Hello, Protean!


    Define, save, and load your first aggregate in under 20 lines.

    Hello, Protean!

  • Quickstart


    Commands, events, and handlers in 5 minutes.

    Quickstart

  • Tutorial


    10-chapter tutorial from your first aggregate to production.

    Tutorial

  • How Do I...?


    Task-oriented index -- look up what you're trying to do and jump straight to the right guide.

    How Do I...?

  • Guides


    Step-by-step instructions for every task Protean supports.

    Guides

  • Core Concepts


    DDD, CQRS, and Event Sourcing explained.

    Core Concepts

  • Adapters


    PostgreSQL, Redis, Elasticsearch, MessageDB, and more.

    Adapters

  • Patterns & Recipes


    Battle-tested solutions for common challenges.

    Patterns

  • Upgrading to 0.15?


    Migration guide with required changes, behavioral differences, and what's new.

    Migration Guide