Skip to content

attack-on-domain

attack-on-domain

DDD building blocks for Python 3.14+

Build maintainable domain models with entities, value objects, aggregates, CQRS, and hexagonal architecture — fully typed, immutable, with built-in validation, and designed for real-world applications.

pip install attack-on-domain

Key Features

Domain Building Blocks

Entity, RootEntity, ValueObject, and Aggregate — everything you need to model your business domain with identity, mutation guards, and consistency boundaries.

Domain Events

Immutable, auto-timestamped records of domain occurrences. Automatically collected by use cases.

CQRS

CQRS-based architecture with easy-to-implement commands, queries, and handlers.

Use Cases

Application-layer operations with auto-wired ports, event collection, logging, and transaction management.

Business Invariants

Enforce domain rules at construction time with type-safe field and model-level invariants that raise clear domain exceptions.

Testing

Spy containers, session stubs, fakers, and event assertions — everything you need to test your domain, application, and infrastructure layers.

Quick Example — Use Case with CQRS and Dependency Injection

from aod.domain import RootEntity, ValueObject
from aod.events import Event
from aod.application import UseCase, Command, CommandPort
from aod.infrastructure import CommandHandler, Session, AdapterContainer


class SqlSession(Session):
    def execute(self, operation: object) -> None: ...

class OrderId(ValueObject):
    value: str

class Order(RootEntity):
    id: OrderId
    total: float

class OrderPlaced(Event):
    order_id: str
    total: float

class PlaceOrder(Command[Order, None]):
    order_id: str
    total: float

class PlaceOrderHandler(CommandHandler[PlaceOrder]):
    session: SqlSession

    def handle(self, command: PlaceOrder) -> None:
        order = Order(id=OrderId(value=command.order_id), total=command.total)
        self.session.execute(order)

class PlaceOrderUseCase(UseCase):
    place_order: CommandPort[PlaceOrder]

    def run(self, order_id: str, total: float) -> None:
        self.place_order.handle(PlaceOrder(
            order_id=order_id, total=total,
        ))

container = AdapterContainer(handlers=[PlaceOrderHandler])
use_case = container.adapt(PlaceOrderUseCase)
use_case.run(order_id="1", total=99.99)

Architecture

The library follows hexagonal architecture (ports and adapters) combined with DDD layers:

Layer Components Depends On
Infrastructure Handlers, Session, Container, Projection Application
Application UseCase, Port, Command, Query Domain
Domain Entity, ValueObject, Service, Event None - (pure business logic)

Each layer depends only on the layer below it:

  • Domain — Pure business logic with no infrastructure dependencies
  • Application — Orchestrates domain objects through Port interfaces
  • Infrastructure — Implements ports for databases, APIs, and other external systems

Next Steps

Quick Start

Get up and running in 5 minutes with a complete example.

DDD Concepts

Learn the Domain-Driven Design principles behind the library.

Building Blocks

Entities, Value Objects, Services, and more.

Testing

Utilities for testing your domain, application, and infrastructure.