Commands represent an explicit intent to change the state of the system. In HexCore’s CQRS layer, every state-mutating operation starts as an immutableDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/Indroic/HexCore/llms.txt
Use this file to discover all available pages before exploring further.
Command object that is dispatched through InMemoryCommandBus, routed through the middleware pipeline, and finally delegated to a single registered AbstractCommandHandler. This page covers how to define commands, write handlers, wire everything together in HandlerRegistry, and dispatch safely.
The Command base class
Every command in HexCore inherits from hexcore.domain.cqrs.commands.Command. It is a frozen Pydantic model, meaning instances are immutable after construction and are safe to pass across layers without risk of accidental mutation.
Built-in fields
Universally unique identifier for this command instance. Auto-generated via
uuid4() — you rarely need to set this manually.UTC datetime of when the command was created. Auto-generated; useful for auditing and idempotency checks.
Defining a command
Add only the fields needed to express the intent. BecauseCommand is a Pydantic model, you get automatic validation for free.
AbstractCommandHandler
Every command type is handled by exactly one handler that inherits from AbstractCommandHandler[TCommand, TCommandResult].
The concrete
Command subclass this handler processes. The bus enforces a 1-to-1 mapping at dispatch time.The return type. Use
None for fire-and-forget commands that produce no meaningful return value.Writing a handler
Registering and dispatching
from hexcore.application.cqrs.registry import HandlerRegistry
from app.commands import CreateOrderCommand
from app.handlers import CreateOrderHandler
from app.repositories import SqlOrderRepository
registry = HandlerRegistry()
registry.register_command_handler(
CreateOrderCommand,
CreateOrderHandler(SqlOrderRepository(session)),
)
registry = (
HandlerRegistry()
.register_command_handler(CreateOrderCommand, CreateOrderHandler(order_repo))
.register_command_handler(CancelOrderCommand, CancelOrderHandler(order_repo))
)
from hexcore.application.cqrs.in_memory_buses import InMemoryCommandBus
from hexcore.application.cqrs.pipeline import MiddlewarePipeline
from hexcore.infrastructure.cqrs.middlewares import TransactionMiddleware
pipeline = MiddlewarePipeline().add(TransactionMiddleware())
command_bus = InMemoryCommandBus(registry=registry, pipeline=pipeline)
Complete example
TransactionMiddleware and commands
CQRSConfig enables TransactionMiddleware on the command bus by default:
commit() is called after the handler returns successfully; on any exception the UoW rolls back and re-raises. You do not need to manage transactions manually inside handlers.
TransactionMiddleware uses uow_factory to obtain a UoW context manager. If you inject a custom factory, it takes precedence over the default SQL/NoSQL auto-detection heuristic.Error handling
HandlerNotFoundError
Raised by the registry when
dispatch() is called for a command type with no registered handler. Always register all command types before starting the application.DuplicateHandlerError
Raised when you try to register a second handler for the same command type. Pass
allow_override=True to the registry constructor to suppress this in tests.