Message Composition and Decomposition

If there are multiple command handlers (i.e. in different modules) for the same Command, all handlers for that command will be executed (decomposition), and the results of command handlers will be merged into single response (composition).

from lato import Application, ApplicationModule, Command

class GetItemDetails(Command):
    item_id: str

pricing_module = ApplicationModule("pricing")

def get_item_price(command: GetItemDetails):
    prices = {"pencil": 1, "pen": 2}
    return {"price": prices[command.item_id]}

warehouse_module = ApplicationModule("warehouse")

def get_item_stock(command: GetItemDetails):
    stocks = {"pencil": 100, "pen": 80}
    return {"stock": stocks[command.item_id]}

app = Application()

assert app.execute(GetItemDetails(item_id="pen")) == {"price": 2, "stock": 80}

By default, handler responses of type dict will be recursively merged into a single dict, and responses of type list will be merged a into single list.

You can use Application.compose decorator to declare a custom composer. A custom composer will receive kwargs with names of the modules handling the response.

from lato import Application, ApplicationModule, Command, TransactionContext
from lato.compositon import compose

class GetAllItemDetails(Command):

pricing_module = ApplicationModule("pricing")

def get_item_price(command: GetAllItemDetails):
    prices = {'pencil': 1, 'pen': 2}
    return prices

warehouse_module = ApplicationModule("warehouse")

def get_item_stock(command: GetAllItemDetails):
    stocks = {'pencil': 100, 'pen': 80}
    return stocks

app = Application()

def compose_item_details(pricing, warehouse):
    assert pricing == {'pencil': 1, 'pen': 2}
    assert warehouse == {'pencil': 100, 'pen': 80}

    details = [dict(item_id=x, price=pricing[x], stock=warehouse[x]) for x in pricing.keys()]
    return details

assert app.execute(GetAllItemDetails()) == [
    {'item_id': 'pencil', 'price': 1, 'stock': 100}, 
    {'item_id': 'pen', 'price': 2, 'stock': 80}