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

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

fractal_commands-1.0.1.tar.gz (10.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

fractal_commands-1.0.1-py3-none-any.whl (7.2 kB view details)

Uploaded Python 3

File details

Details for the file fractal_commands-1.0.1.tar.gz.

File metadata

  • Download URL: fractal_commands-1.0.1.tar.gz
  • Upload date:
  • Size: 10.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: python-requests/2.34.2

File hashes

Hashes for fractal_commands-1.0.1.tar.gz
Algorithm Hash digest
SHA256 068e2484c32a8411d83aaf732b3a8c50a58ce611cb8730f95d5335da6ab018c6
MD5 66f913006cb7647dd4e91657fcf889c8
BLAKE2b-256 f3ce5c67572d1606f798bb5e9c5e9159111827a57955d0b6ce64e618dae179e3

See more details on using hashes here.

File details

Details for the file fractal_commands-1.0.1-py3-none-any.whl.

File metadata

File hashes

Hashes for fractal_commands-1.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 f545d152ec353b41463bac3a399836e4e3c3ed1c1e5d010edddc986e9c9ba409
MD5 0b12fc0af51ae166ee8d538642e0bb48
BLAKE2b-256 faefb01f3232066eaa2ec2f68498bd725fb9237af93f9447dc785e8a941ce6ba

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page