-
Notifications
You must be signed in to change notification settings - Fork 4
Description
Proposal: Extract Transactional Outbox Pattern into a Separate Library
I propose extracting the Transactional Outbox implementation (currently in
src/cqrs/outbox) into a standalone library (e.g.,python-transactional-outbox). Below are the key architectural and practical reasons for this decision.1. Single Responsibility Principle (SRP) Violation
The core responsibility of
python-cqrsis to provide abstractions for the Mediator pattern, Command/Query handling, and Event Dispatching.
The Transactional Outbox pattern is a persistence and reliability mechanism. While it complements CQRS, it is a distinct architectural concern. Coupling them tightly forces the core library to manage database schemas, ORM integrations (SQLAlchemy), and background workers, which dilutes its primary focus.2. Hard Coupling to Infrastructure
Currently, the Outbox implementation is tightly coupled with:
- SQLAlchemy: Users who want to use
python-cqrswith MongoDB, Tortoise ORM, or raw SQL drivers are forced to pull in SQLAlchemy dependencies or deal with dead code.- Specific Database Dialects: The current implementation contains MySQL-specific optimizations (e.g.,
UUID_TO_BIN), which hinders portability to PostgreSQL or SQLite.By extracting it,
python-cqrsbecomes infrastructure-agnostic, while the new library can focus on providing robust, database-specific implementations (adapters) for the Outbox pattern.3. Independent Lifecycle and Scalability
The Outbox pattern often requires complex features that are irrelevant to the core CQRS mediator:
- Debezium Integration (CDC): Advanced Outbox implementations often rely on Change Data Capture rather than polling.
- Message Relay/Worker: The logic for polling, locking, retrying, and publishing messages to a broker (Kafka/RabbitMQ) is complex and deserves its own lifecycle, configuration, and scaling strategy.
- Observability: Metrics for outbox lag, publishing rates, and failure handling are specific to the Outbox domain.
Developing these features within
python-cqrsbloats the library. A separate library allows for independent versioning and faster iteration on these specific reliability features.4. Reusability
The Transactional Outbox pattern is useful even for applications that do not use CQRS. A developer might want to ensure atomic event publishing in a standard CRUD application.
Currently, they cannot use your Outbox implementation without installing the entirepython-cqrsframework. Extracting it makes the solution reusable across the entire Python ecosystem.5. Dependency Management
The current setup forces a heavy dependency tree (
sqlalchemy, drivers,pydantic, etc.) on all users. Separating the libraries allows for a cleaner dependency graph:
python-cqrs: Lightweight, focuses on dispatching logic.python-outbox: Focuses on persistence guarantees.python-outbox-sqlalchemy: Optional adapter.Conclusion
Extracting the Outbox pattern aligns
python-cqrswith the Hexagonal Architecture (Ports and Adapters) approach. It reduces the core library's complexity, increases flexibility for users to choose their persistence stack, and allows the Outbox implementation to evolve into a mature, standalone product-grade reliability tool independent of the CQRS framework.