Modularity

Breaking down applications into separate, self-contained modules that represent distinct bounded contexts is a common practice. These modules encapsulate cohesive components such as entities, repositories, and services, ensuring that each part of the application remains focused on its specific functionality and responsibilities. This modular approach facilitates maintainability, scalability and reusability by drawing clear boundaries between different parts of the system.

Basics

In lato, Application can be decomposed into ApplicationModules like so:

from lato import Application, ApplicationModule

foo_module = ApplicationModule(name="foo")
@foo_module.handler("foo")
def call_foo():
    print("foo")

bar_module = ApplicationModule(name="bar")
@bar_module.handler("bar")
def call_bar():
    print("bar")

foobar = Application()
foobar.include_submodule(foo_module)
foobar.include_submodule(bar_module)

foobar.call("foo")
foobar.call("bar")
foo
bar

Application modules can create hierarchies, i. e. top level module can be further composed into submodules if needed.

Application module structure

The application module can be implemented as a Python package:

|- application.py
|- sample_module/
   |- __init__.py
   |- commands.py
   |- events.py
# sample_module/__init__.py
import importlib
from lato import ApplicationModule

a_module = ApplicationModule("sample")
importlib.import_module("sample_module.commands")
importlib.import_module("sample_module.events")
# sample_module/commands.py
from lato import Command
from sample_module import a_module

class SampleCommand(Command):
    pass

@a_module.handler(SampleCommand)
def handle_sample_command(command: SampleCommand):
    pass
# sample_module/events.py
from lato import Event
from sample_module import a_module

class SampleEvent(Event):
    pass

@a_module.handler(SampleEvent)
def on_sample_event(event: SampleEvent):
    pass
# application.py
from lato import Application
from sample_module import a_module
from sample_module.co

app = Application()
app.include_submodule(a_module)

@a_module.handler(SampleEvent)
def on_sample_event(event: SampleEvent):
    pass