Skip to main content

A minimal, lightweight mediator implementation for Python applications supporting CQRS pattern with commands and queries

Project description

livehop-minimal-mediator

A minimal, lightweight mediator implementation for Python applications supporting the CQRS pattern with commands and queries.

Features

  • Minimal and lightweight - Simple implementation with no unnecessary complexity
  • 🚀 Easy to use - Clean, intuitive API
  • 🎯 CQRS support - Perfect for implementing commands and queries
  • Async/Await - Full async support for modern Python applications
  • 🔧 Type hints - Fully typed for better IDE support and type checking
  • 📦 Zero dependencies - No external dependencies required

Installation

pip install livehop-minimal-mediator

Quick Start

1. Create a Query/Command

from livehop_minimal_mediator import IRequest, IRequestHandler

# Define your request
class GetUsersQuery(IRequest[list[dict]]):
    pass

# Define your handler
class GetUsersHandler(IRequestHandler[list[dict]]):
    async def handle(self, request: GetUsersQuery) -> list[dict]:
        # Your logic here
        users = [
            {"id": 1, "name": "John Doe"},
            {"id": 2, "name": "Jane Smith"}
        ]
        return users

2. Register and Use

from livehop_minimal_mediator import Mediator
import asyncio

# Create mediator instance
mediator = Mediator()

# Register handler
mediator.register_handler(GetUsersQuery, lambda: GetUsersHandler())

# Create sender
sender = mediator.create_sender()

# Use it
async def main():
    query = GetUsersQuery()
    users = await sender.send(query)
    print(users)

asyncio.run(main())

Usage Examples

Command with Parameters

from dataclasses import dataclass
from livehop_minimal_mediator import IRequest, IRequestHandler

@dataclass
class CreateUserCommand(IRequest[int]):
    name: str
    email: str

class CreateUserHandler(IRequestHandler[int]):
    def __init__(self, db_connection):
        self.db = db_connection

    async def handle(self, request: CreateUserCommand) -> int:
        # Insert user logic
        user_id = await self.db.insert_user(request.name, request.email)
        return user_id

# Register with dependencies
mediator.register_handler(
    CreateUserCommand,
    lambda: CreateUserHandler(db_connection)
)

# Use it
command = CreateUserCommand(name="Alice", email="alice@example.com")
user_id = await sender.send(command)

Query with Parameters

@dataclass
class GetUserByIdQuery(IRequest[dict | None]):
    user_id: int

class GetUserByIdHandler(IRequestHandler[dict | None]):
    def __init__(self, db_connection):
        self.db = db_connection

    async def handle(self, request: GetUserByIdQuery) -> dict | None:
        user = await self.db.get_user(request.user_id)
        return user

# Register and use
mediator.register_handler(
    GetUserByIdQuery,
    lambda: GetUserByIdHandler(db_connection)
)

query = GetUserByIdQuery(user_id=1)
user = await sender.send(query)

Integration with FastAPI

from fastapi import FastAPI, Depends
from livehop_minimal_mediator import Mediator, Sender

app = FastAPI()

# Create a global mediator instance
_mediator = Mediator()

# Register all your handlers
_mediator.register_handler(GetUsersQuery, lambda: GetUsersHandler())
_mediator.register_handler(CreateUserCommand, lambda: CreateUserHandler(db))

# Create dependency
def get_sender() -> Sender:
    return _mediator.create_sender()

# Use in routes
@app.get("/users")
async def get_users(sender: Sender = Depends(get_sender)):
    query = GetUsersQuery()
    users = await sender.send(query)
    return users

@app.post("/users")
async def create_user(name: str, email: str, sender: Sender = Depends(get_sender)):
    command = CreateUserCommand(name=name, email=email)
    user_id = await sender.send(command)
    return {"id": user_id}

Integration with Django

from django.http import JsonResponse
from livehop_minimal_mediator import Mediator

# In your settings or app config
mediator = Mediator()
mediator.register_handler(GetUsersQuery, lambda: GetUsersHandler())

# In your views
async def get_users_view(request):
    sender = mediator.create_sender()
    query = GetUsersQuery()
    users = await sender.send(query)
    return JsonResponse({"users": users})

Core Interfaces

IRequest[TResponse]

Marker interface for requests that return a response.

class MyQuery(IRequest[MyReturnType]):
    pass

IRequestHandler[TResponse]

Interface for handling requests.

class MyHandler(IRequestHandler[MyReturnType]):
    async def handle(self, request: MyQuery) -> MyReturnType:
        # Your logic here
        pass

Mediator

Manages registration and retrieval of request handlers.

mediator = Mediator()
mediator.register_handler(MyQuery, lambda: MyHandler())
sender = mediator.create_sender()

Sender

Dispatches requests to their registered handlers.

result = await sender.send(MyQuery())

Best Practices

  1. Organize by Feature - Group requests and handlers together in feature modules
  2. Use Dataclasses - Leverage dataclasses for clean, immutable request definitions
  3. Async All the Way - Use async/await consistently throughout your handlers
  4. Dependency Injection - Inject dependencies into handler constructors via factories
  5. Single Responsibility - Keep handlers focused on one task

Why livehop-minimal-mediator?

  • Simplicity: No complex pipeline behaviors or middleware - just requests and handlers
  • Lightweight: No dependencies, minimal code footprint
  • Pythonic: Idiomatic Python with type hints and async support
  • Transparent: Simple, readable implementation you can understand and trust

Requirements

  • Python 3.8 or later
  • No external dependencies

License

MIT

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Support

If you encounter any issues or have questions, please file an issue on GitHub.

Project details


Download files

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

Source Distribution

livehop_minimal_mediator-1.0.0.tar.gz (6.0 kB view details)

Uploaded Source

Built Distribution

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

livehop_minimal_mediator-1.0.0-py3-none-any.whl (6.9 kB view details)

Uploaded Python 3

File details

Details for the file livehop_minimal_mediator-1.0.0.tar.gz.

File metadata

File hashes

Hashes for livehop_minimal_mediator-1.0.0.tar.gz
Algorithm Hash digest
SHA256 0820ce330661a79d7c429a44d9ce4a7e3dbbeb5299738c7147eea5d6a514e528
MD5 060354ed6989598e35bc08a2e81c80ae
BLAKE2b-256 c42af1af61b2f66ba9ad466583c13ca103ad28845af7d350f83a4146d07dc7f5

See more details on using hashes here.

File details

Details for the file livehop_minimal_mediator-1.0.0-py3-none-any.whl.

File metadata

File hashes

Hashes for livehop_minimal_mediator-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 0a4f99e36fe8dbc15bc2e9e8bbd4b6f4330857c4e068dcb3e9ab99d1f51c93e4
MD5 2c6c932cf0a0208037044a96f8f9014a
BLAKE2b-256 1683656441d8e219b99397537a74b3cf017d1f0f8581d9796b22719909bd6c66

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 Pingdom Monitoring Sentry Error logging StatusPage Status page