This release is a pre-release and may not be stable for production use.
Buz
Buz is a lightweight, simple, and extensible Python library that provides implementations of Event, Command, and Query buses following CQRS and Event-Driven Architecture patterns.
📋 Table of Contents
- Buz
✨ Key Features
- 🚌 Bus Types: Event, Command, and Query buses for clean architecture
- 🔄 Sync & Async Support: Both synchronous and asynchronous implementations
- 🔧 Middleware System: Extensible middleware for cross-cutting concerns
- 📦 Message Brokers: Support for Kafka, RabbitMQ (via Kombu), and in-memory
- 🔒 Transactional Outbox: Reliable event publishing with transactional guarantees
- 🎯 Dependency Injection: Built-in locator pattern for handler resolution
- 📝 Type Safety: Fully typed with mypy support
- 🪶 Lightweight: Minimal dependencies, maximum flexibility
🚀 Quick Start
Installation
# Basic installation
pip install buz
# With Kafka support
pip install buz[aiokafka]
# With RabbitMQ support
pip install buz[kombu]
# With dependency injection
pip install buz[pypendency]
Basic Usage
Event Bus Example
from dataclasses import dataclass
from buz import Message
from buz.event import Event, BaseSubscriber
from buz.event.sync import SyncEventBus
from buz.locator.sync import InstanceLocator
@dataclass(frozen=True)
class UserCreated(Event):
user_id: str
email: str
class EmailSubscriber(BaseSubscriber):
def consume(self, event: UserCreated) -> None:
print(f"Sending welcome email to {event.email}")
class AnalyticsSubscriber(BaseSubscriber):
def consume(self, event: UserCreated) -> None:
print(f"Tracking user creation: {event.user_id}")
# Setup
locator: InstanceLocator = InstanceLocator()
locator.register(EmailSubscriber())
locator.register(AnalyticsSubscriber())
event_bus = SyncEventBus(locator)
# Usage
event = UserCreated(user_id="123", email="user@example.com")
event_bus.publish(event)
Command Bus Example
from dataclasses import dataclass
from buz.command import Command
from buz.command.synchronous import BaseCommandHandler
from buz.command.synchronous.self_process import SelfProcessCommandBus
from buz.locator.sync import InstanceLocator
@dataclass(frozen=True)
class CreateUser(Command):
email: str
name: str
class CreateUserCommandHandler(BaseCommandHandler):
def handle(self, command: CreateUser) -> None:
# Business logic here
print(f"Creating user: {command.name} ({command.email})")
# Setup
locator = InstanceLocator()
locator.register(CreateUserCommandHandler())
command_bus = SelfProcessCommandBus(locator)
# Usage
command = CreateUser(email="user@example.com", name="John Doe")
command_bus.handle(command)
Query Bus Example
from dataclasses import dataclass
from buz.query import Query, QueryResponse
from buz.query.synchronous import BaseQueryHandler
from buz.query.synchronous.self_process import SelfProcessQueryBus
from buz.locator.sync import InstanceLocator
@dataclass(frozen=True)
class GetUser(Query):
user_id: str
@dataclass(frozen=True)
class User:
user_id: str
name: str
email: str
class GetUserQueryHandler(BaseQueryHandler):
def handle(self, query: GetUser) -> QueryResponse:
# Business logic here
return QueryResponse(
content=User(
user_id=query.user_id,
name="John Doe",
email="john@example.com"
)
)
# Setup
locator = InstanceLocator()
locator.register(GetUserQueryHandler())
query_bus = SelfProcessQueryBus(locator)
# Usage
query = GetUser(user_id="123")
query_response = query_bus.handle(query)
user = query_response.content
print(f"User: {user.name}")
🏗️ Architecture
Buz implements the Command Query Responsibility Segregation (CQRS) pattern with distinct buses:
Event Bus
- Purpose: Publish domain events and notify multiple subscribers
- Pattern: Pub/Sub with multiple handlers per event
- Use Cases: Domain event broadcasting, eventual consistency, integration events
Command Bus
- Purpose: Execute business operations and commands
- Pattern: Single handler per command
- Use Cases: Business logic execution, write operations, state changes
Query Bus
- Purpose: Retrieve data and execute queries
- Pattern: Single handler per query with typed responses
- Use Cases: Data retrieval, read operations, projections
🔧 Advanced Features
Middleware System
Add cross-cutting concerns like logging, validation, and metrics:
from datetime import datetime
from buz.event import Event, Subscriber
from buz.event.middleware import BasePublishMiddleware, BaseConsumeMiddleware
from buz.event.infrastructure.models.execution_context import ExecutionContext
class LoggingPublishMiddleware(BasePublishMiddleware):
def _before_on_publish(self, event: Event) -> None:
print(f"Publishing event {event}")
def _after_on_publish(self, event: Event) -> None:
return
class MetricsConsumeMiddleware(BaseConsumeMiddleware):
def __init__(self) -> None:
self.__consumption_start_time: datetime = datetime.now()
def _before_on_consume(
self,
event: Event,
subscriber: Subscriber,
execution_context: ExecutionContext,
) -> None:
self.__consumption_start_time = datetime.now()
def _after_on_consume(
self,
event: Event,
subscriber: Subscriber,
execution_context: ExecutionContext,
) -> None:
consumption_time_ms = int((datetime.now() - self.__consumption_start_time).total_seconds() * 1000)
print(
f"Subscriber {subscriber.fqn()} consumed event {event.id} successfully in {consumption_time_ms} ms"
)
# Apply middleware
event_bus = SyncEventBus(
locator=locator,
publish_middlewares=[LoggingPublishMiddleware()],
consume_middlewares=[MetricsConsumeMiddleware()]
)
# Usage
event = UserCreated(user_id="123", email="user@example.com")
event_bus.publish(event)
Transactional Outbox Pattern
Ensure reliable event publishing with database transactions:
from buz.event.transactional_outbox import TransactionalOutboxEventBus
# Configure with your database and event bus
transactional_outbox_bus = TransactionalOutboxEventBus(
outbox_repository=your_outbox_repository,
event_to_outbox_record_translator=your_outbox_record_translator,
...
)
# Events are stored in database, published later by worker
transactional_outbox_bus.publish(event)
RabbitMQ
from buz.event.infrastructure.kombu.kombu_event_bus import KombuEventBus
kombu_event_bus = KombuEventBus(
connection=your_connection,
publish_strategy=your_publish_strategy,
publish_retry_policy=you_publish_retry_policy,
...
)
# Published and consumed in RabbitMQ
kombu_event_bus.publish(event)
Kafka Integration
from buz.kafka import BuzKafkaEventBus
kafka_bus = KafkaEventBus(
publish_strategy=your_publish_strategy,
producer=your_producer,
logger=your_logger,
...
)
# Published and consumed in Kafka
kafka_bus.publish(event)
Kafka Consumer Metrics
Kafka workers (BuzAIOKafkaAsyncConsumer and BuzAIOKafkaMultiThreadedConsumer) emit metrics through a MetricEmitter you pass at construction. Implement that interface in your application (for example with DogStatsD) and query occupancy per minute as sum:messaging.sink_relay.busy_ms over a 1-minute rollup.
| Metric | Type | Tags | Description |
|---|---|---|---|
messaging.sink_relay.log_wait_ms |
timing | subscriber_fqn |
Time taken to poll a set of records |
messaging.sink_relay.poll_delivery_lag_ms |
timing | subscriber_fqn, event_fqn |
Time from when the record was produced until it was polled |
messaging.sink_relay.commit_ms |
timing | subscriber_fqn, event_fqn |
Time taken to commit a message |
messaging.sink_relay.deserialize_ms |
timing | subscriber_fqn, event_fqn |
Time taken to deserialize a message |
messaging.sink_relay.middleware_ms |
timing | subscriber_fqn, event_fqn |
Time taken to execute the consume middleware chain |
messaging.sink_relay.delivery_lag_ms |
timing | subscriber_fqn, event_fqn |
Time from when the message was produced until the first handler execution |
messaging.sink_relay.consume_attempts |
set | subscriber_fqn, event_fqn |
Retry count for a message |
messaging.sink_relay.rejections |
increment | subscriber_fqn, event_fqn |
Rejections (discard, DLQ, or other on-fail strategy) |
messaging.sink_relay.busy_ms |
increment | subscriber_fqn, event_fqn |
Milliseconds spent executing a subscriber consume for a polled record |
messaging.sink_relay.events_processed |
increment | subscriber_fqn, event_fqn |
Records successfully consumed by a subscriber |
messaging.handler.duration_ms |
timing | subscriber_fqn, event_fqn |
Duration of a successful subscriber handler execution |
messaging.handler.unsuccessful_duration_ms |
timing | subscriber_fqn, event_fqn |
Duration of a failed subscriber handler execution |
Async Support
from buz.event.async_event_bus import AsyncEventBus
from buz.query.asynchronous import QueryBus as AsyncQueryBus
from buz.command.asynchronous import CommandHandler as AsyncCommandHandler
# Async event bus
async_event_bus = AsyncEventBus(locator)
await async_event_bus.publish(event)
# Async query bus
async_query_bus = AsyncQueryBus(locator)
await async_query_bus.handle(event)
# Async command bus
async_command_bus = AsyncCommandBus(locator)
await async_command_bus.handle(command)
📦 Message Brokers
Supported Brokers
| Broker | Sync | Async | Installation |
|---|---|---|---|
| In-Memory | ✅ | ✅ | Built-in |
| Kafka | ✅ | ✅ | pip install buz[aiokafka] |
| RabbitMQ | ✅ | ❌ | pip install buz[kombu] |
🧪 Testing
Buz includes testing utilities for unit and integration tests:
from buz.event.sync import SyncEventBus
from buz.locator.sync import InstanceLocator
test_locator = InstanceLocator()
test_bus = SyncEventBus(test_locator)
test_locator.register(EmailSubscriber())
test_bus.publish(UserCreated(user_id="123", email="test@example.com"))
🔗 Related Projects
- buz-fever-shared: Opinionated utilities and standards for Buz
- buz-basic-example: Complete example project with Docker setup
📋 Requirements
- Python 3.9+
- Optional dependencies based on features used
🤝 Contributing
We welcome contributions! Please see our Contributing Guidelines for details.
Development Setup
# Clone the repository
git clone https://github.com/Feverup/buz.git
cd buz
# Install with development dependencies
make build
# Run tests
make test
# Run linting
make lint
# Format code
make format
📄 License
This project is licensed under the MIT License - see the LICENSE file for details.
📚 Documentation
- Changelog - Release notes and version history
🙋♀️ Support
- Create an Issue for bug reports or feature requests
Made with ❤️ by the Fever Platform Team
Metadata
Release files for buz 7.1.0rc2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| buz-7.1.0rc2.tar.gz | 75.6 kB | Without provenance | Details for buz-7.1.0rc2.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| buz-7.1.0rc2-py3-none-any.whl | Python 3 | none | any | Details for buz-7.1.0rc2-py3-none-any.whl |
Total release size: 251.7 kB
Release files / buz-7.1.0rc2.tar.gz
| Download URL | buz-7.1.0rc2.tar.gz |
|---|---|
| Size | 75.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
80aaf2058d491b7684b10abb974f381b2b0865334bfdb19bfacf69e50aaa43f9
|
|
BLAKE2b-256 checksum How to use checksums |
7b3a85a9bc1edf1d5698c0eb0357d8a0de5d8121b68350ea826ef9a2ee2a88db
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
poetry/1.8.3 CPython/3.10.22 Linux/7.0.0-1017-azure
|
Release files / buz-7.1.0rc2-py3-none-any.whl
| Download URL | buz-7.1.0rc2-py3-none-any.whl |
|---|---|
| Size | 176.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d576df92e4f591f845c984cd4ea77fbc3fbd1950524fdd565d4ddffaa3b85ebc
|
|
BLAKE2b-256 checksum How to use checksums |
d1a84e6121a7c6884da3ed6b1aea77dbf0ddbd8d7ad859cb8d1ad05b47b2a29b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
poetry/1.8.3 CPython/3.10.22 Linux/7.0.0-1017-azure
|
Security for buz 7.1.0rc2
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.