Skip to main content

Fractal Commands

Fractal Commands is a minimal command bus for building SOLID logic in your Python applications.

PyPI Version Build Status

Installation

pip install fractal-commands

Background

A command is a plain data object describing an intent: add this road, approve this member. A handler carries it out. The bus is the thin thing in between — it knows which handler answers which command, and nothing else.

Keeping the two apart is what makes the pattern useful. The caller states what it wants and stays ignorant of how it happens; the handler owns the how and never has to know who asked. Commands cross that seam as data, so they are easy to log, queue, replay, or map onto from events.

Usage

from dataclasses import dataclass

from fractal_commands import Command, CommandBus, CommandHandler


@dataclass
class Greet(Command):
    name: str


class GreetHandler(CommandHandler[Greet]):
    command = Greet

    def handle(self, command: Greet):
        return f"hello {command.name}"


bus = CommandBus()
bus.add_handler(GreetHandler())

bus.handle(Greet("world"))
# {'GreetHandler': 'hello world'}

Every handler registered for a command runs. handle collects the return values of the ones that produced something, keyed by handler class name; handlers that return None — most write handlers — simply do not appear.

await bus.handle_async(command) is the same thing for async handlers.

A command with no handler is an error

Dispatching a command nobody handles raises NoCommandHandlerError:

bus = CommandBus()
bus.handle(Greet("world"))
# NoCommandHandlerError: no handler is registered for Greet, so the command was
# dropped without being executed

This is deliberate, and it is worth explaining, because the obvious alternative — shrug and return an empty result — is what this library was extracted to stop doing.

Handlers usually register themselves through a decorator at import time. That makes registration a side effect of the import graph, and import graphs lose edges: a module stops being imported, and every command of that type silently stops being handled. Nothing raises. No event is published, no row is written, and the call returns normally. Tests that assert on the service layer see a perfectly healthy no-op, so the failure surfaces days later somewhere else entirely, as missing data.

Raising turns that into a one-line stack trace at the first dispatch.

When silence is legitimate

A bus fed by fan-out is the honest exception. An event projector that maps events onto commands will produce commands this particular deployment has no handler for, and that is normal rather than broken. Pass strict=False to log the miss at ERROR and carry on:

bus = CommandBus(strict=False)
bus.handle(Greet("world"))
# ERROR:fractal_commands.command_bus:no handler is registered for Greet, so the
# command was dropped without being executed
# {}

It is also the migration path if you are adopting this library in an application that has been relying on the old silence: start with strict=False, fix what the logs show, then turn it on.

Entity commands

The three commands generic CRUD needs are included, built on fractal-specifications and fractal-repositories:

from fractal_commands import AddEntityCommand, UpdateEntityCommand, DeleteEntityCommand

Each carries the specification the handler checks the entity against, so the business rule travels with the intent instead of living in the handler.

Development

make dev-install
make test
make lint
make format

Metadata

Release files for fractal-commands 1.0.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for fractal-commands 1.0.1
File Size Uploaded
fractal_commands-1.0.1.tar.gz 10.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for fractal-commands 1.0.1
File Interpreter ABI Platform
fractal_commands-1.0.1-py3-none-any.whl Python 3 none any Details

Total release size: 17.7 kB

Release files / fractal_commands-1.0.1.tar.gz

Download URL fractal_commands-1.0.1.tar.gz
Size 10.4 kB
Tags Source
SHA-256 checksum
How to use checksums
068e2484c32a8411d83aaf732b3a8c50a58ce611cb8730f95d5335da6ab018c6
BLAKE2b-256 checksum
How to use checksums
f3ce5c67572d1606f798bb5e9c5e9159111827a57955d0b6ce64e618dae179e3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via python-requests/2.34.2

Release files / fractal_commands-1.0.1-py3-none-any.whl

Download URL fractal_commands-1.0.1-py3-none-any.whl
Size 7.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
f545d152ec353b41463bac3a399836e4e3c3ed1c1e5d010edddc986e9c9ba409
BLAKE2b-256 checksum
How to use checksums
faefb01f3232066eaa2ec2f68498bd725fb9237af93f9447dc785e8a941ce6ba
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via python-requests/2.34.2

Release history Release notifications | RSS feed

This release

1.0.1 This release

2 release files

1.0.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page