python-cqrs 4.15.1__tar.gz → 5.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. {python_cqrs-4.15.1/src/python_cqrs.egg-info → python_cqrs-5.1.0}/PKG-INFO +44 -29
  2. {python_cqrs-4.15.1 → python_cqrs-5.1.0}/README.md +30 -24
  3. {python_cqrs-4.15.1 → python_cqrs-5.1.0}/pyproject.toml +16 -4
  4. python_cqrs-5.1.0/src/cqrs/__init__.py +218 -0
  5. python_cqrs-5.1.0/src/cqrs/_dataclass_utils.py +99 -0
  6. python_cqrs-5.1.0/src/cqrs/bootstrap/__init__.py +5 -0
  7. python_cqrs-4.15.1/src/cqrs/requests/bootstrap.py → python_cqrs-5.1.0/src/cqrs/bootstrap/requests.py +1 -1
  8. python_cqrs-4.15.1/src/cqrs/saga/bootstrap.py → python_cqrs-5.1.0/src/cqrs/bootstrap/saga.py +4 -4
  9. {python_cqrs-4.15.1 → python_cqrs-5.1.0}/src/cqrs/dispatcher/event.py +3 -3
  10. {python_cqrs-4.15.1 → python_cqrs-5.1.0}/src/cqrs/dispatcher/models.py +3 -3
  11. {python_cqrs-4.15.1 → python_cqrs-5.1.0}/src/cqrs/dispatcher/request.py +4 -4
  12. {python_cqrs-4.15.1 → python_cqrs-5.1.0}/src/cqrs/dispatcher/saga.py +1 -1
  13. {python_cqrs-4.15.1 → python_cqrs-5.1.0}/src/cqrs/dispatcher/streaming.py +3 -3
  14. python_cqrs-5.1.0/src/cqrs/events/__init__.py +63 -0
  15. {python_cqrs-4.15.1 → python_cqrs-5.1.0}/src/cqrs/events/event_emitter.py +3 -2
  16. {python_cqrs-4.15.1 → python_cqrs-5.1.0}/src/cqrs/events/event_processor.py +2 -2
  17. {python_cqrs-4.15.1 → python_cqrs-5.1.0}/src/cqrs/events/fallback.py +1 -1
  18. python_cqrs-5.1.0/src/cqrs/handlers/__init__.py +14 -0
  19. python_cqrs-4.15.1/src/cqrs/requests/cor_request_handler.py → python_cqrs-5.1.0/src/cqrs/handlers/cor.py +2 -2
  20. python_cqrs-4.15.1/src/cqrs/events/event_handler.py → python_cqrs-5.1.0/src/cqrs/handlers/event.py +1 -1
  21. python_cqrs-4.15.1/src/cqrs/requests/request_handler.py → python_cqrs-5.1.0/src/cqrs/handlers/request.py +2 -2
  22. python_cqrs-4.15.1/src/cqrs/saga/step.py → python_cqrs-5.1.0/src/cqrs/handlers/saga.py +2 -2
  23. python_cqrs-5.1.0/src/cqrs/mapping/__init__.py +5 -0
  24. python_cqrs-4.15.1/src/cqrs/events/map.py → python_cqrs-5.1.0/src/cqrs/mapping/events.py +2 -2
  25. python_cqrs-4.15.1/src/cqrs/outbox/map.py → python_cqrs-5.1.0/src/cqrs/mapping/outbox.py +1 -1
  26. python_cqrs-4.15.1/src/cqrs/requests/map.py → python_cqrs-5.1.0/src/cqrs/mapping/requests.py +3 -3
  27. python_cqrs-5.1.0/src/cqrs/mediators/__init__.py +11 -0
  28. python_cqrs-5.1.0/src/cqrs/mediators/_common.py +26 -0
  29. python_cqrs-5.1.0/src/cqrs/mediators/event.py +50 -0
  30. python_cqrs-5.1.0/src/cqrs/mediators/request.py +109 -0
  31. python_cqrs-5.1.0/src/cqrs/mediators/saga.py +152 -0
  32. python_cqrs-5.1.0/src/cqrs/mediators/streaming.py +128 -0
  33. python_cqrs-5.1.0/src/cqrs/mermaid/__init__.py +4 -0
  34. python_cqrs-4.15.1/src/cqrs/requests/mermaid.py → python_cqrs-5.1.0/src/cqrs/mermaid/cor.py +1 -1
  35. python_cqrs-4.15.1/src/cqrs/saga/mermaid.py → python_cqrs-5.1.0/src/cqrs/mermaid/saga.py +1 -1
  36. {python_cqrs-4.15.1/src/cqrs → python_cqrs-5.1.0/src/cqrs/message_brokers}/producer.py +7 -4
  37. {python_cqrs-4.15.1 → python_cqrs-5.1.0}/src/cqrs/middlewares/base.py +1 -1
  38. {python_cqrs-4.15.1 → python_cqrs-5.1.0}/src/cqrs/middlewares/logging.py +2 -2
  39. python_cqrs-5.1.0/src/cqrs/models/__init__.py +57 -0
  40. python_cqrs-5.1.0/src/cqrs/models/event.py +208 -0
  41. python_cqrs-5.1.0/src/cqrs/models/pydantic.py +130 -0
  42. python_cqrs-5.1.0/src/cqrs/models/request.py +81 -0
  43. python_cqrs-5.1.0/src/cqrs/models/response.py +74 -0
  44. {python_cqrs-4.15.1 → python_cqrs-5.1.0}/src/cqrs/outbox/mock.py +2 -1
  45. {python_cqrs-4.15.1 → python_cqrs-5.1.0}/src/cqrs/outbox/repository.py +1 -1
  46. {python_cqrs-4.15.1 → python_cqrs-5.1.0}/src/cqrs/outbox/sqlalchemy.py +22 -8
  47. python_cqrs-5.1.0/src/cqrs/outbox/sqlmodel.py +189 -0
  48. {python_cqrs-4.15.1 → python_cqrs-5.1.0}/src/cqrs/requests/fallback.py +1 -1
  49. {python_cqrs-4.15.1 → python_cqrs-5.1.0}/src/cqrs/saga/circuit_breaker.py +1 -1
  50. {python_cqrs-4.15.1 → python_cqrs-5.1.0}/src/cqrs/saga/compensation.py +1 -1
  51. {python_cqrs-4.15.1 → python_cqrs-5.1.0}/src/cqrs/saga/execution.py +1 -1
  52. {python_cqrs-4.15.1 → python_cqrs-5.1.0}/src/cqrs/saga/fallback.py +1 -1
  53. {python_cqrs-4.15.1 → python_cqrs-5.1.0}/src/cqrs/saga/saga.py +1 -1
  54. {python_cqrs-4.15.1 → python_cqrs-5.1.0}/src/cqrs/saga/storage/sqlalchemy.py +72 -45
  55. python_cqrs-5.1.0/src/cqrs/saga/storage/sqlmodel.py +203 -0
  56. {python_cqrs-4.15.1 → python_cqrs-5.1.0}/src/cqrs/saga/validation.py +1 -1
  57. {python_cqrs-4.15.1 → python_cqrs-5.1.0}/src/cqrs/serializers/default.py +1 -1
  58. {python_cqrs-4.15.1 → python_cqrs-5.1.0}/src/cqrs/serializers/json.py +1 -1
  59. {python_cqrs-4.15.1 → python_cqrs-5.1.0}/src/cqrs/serializers/protobuf.py +1 -1
  60. {python_cqrs-4.15.1 → python_cqrs-5.1.0}/src/cqrs/serializers/protocol.py +1 -1
  61. python_cqrs-5.1.0/src/cqrs/sqlmodel_base.py +45 -0
  62. {python_cqrs-4.15.1 → python_cqrs-5.1.0}/src/cqrs/types.py +1 -1
  63. {python_cqrs-4.15.1 → python_cqrs-5.1.0/src/python_cqrs.egg-info}/PKG-INFO +44 -29
  64. {python_cqrs-4.15.1 → python_cqrs-5.1.0}/src/python_cqrs.egg-info/SOURCES.txt +32 -18
  65. {python_cqrs-4.15.1 → python_cqrs-5.1.0}/src/python_cqrs.egg-info/requires.txt +13 -2
  66. python_cqrs-4.15.1/src/cqrs/__init__.py +0 -154
  67. python_cqrs-4.15.1/src/cqrs/_eol.py +0 -30
  68. python_cqrs-4.15.1/src/cqrs/events/__init__.py +0 -50
  69. python_cqrs-4.15.1/src/cqrs/events/event.py +0 -393
  70. python_cqrs-4.15.1/src/cqrs/mediator.py +0 -423
  71. python_cqrs-4.15.1/src/cqrs/requests/request.py +0 -152
  72. python_cqrs-4.15.1/src/cqrs/response.py +0 -146
  73. {python_cqrs-4.15.1 → python_cqrs-5.1.0}/LICENSE +0 -0
  74. {python_cqrs-4.15.1 → python_cqrs-5.1.0}/setup.cfg +0 -0
  75. {python_cqrs-4.15.1 → python_cqrs-5.1.0}/src/cqrs/adapters/__init__.py +0 -0
  76. {python_cqrs-4.15.1 → python_cqrs-5.1.0}/src/cqrs/adapters/amqp.py +0 -0
  77. {python_cqrs-4.15.1 → python_cqrs-5.1.0}/src/cqrs/adapters/circuit_breaker.py +0 -0
  78. {python_cqrs-4.15.1 → python_cqrs-5.1.0}/src/cqrs/adapters/kafka.py +0 -0
  79. {python_cqrs-4.15.1 → python_cqrs-5.1.0}/src/cqrs/adapters/protocol.py +0 -0
  80. /python_cqrs-4.15.1/src/cqrs/events/bootstrap.py → /python_cqrs-5.1.0/src/cqrs/bootstrap/events.py +0 -0
  81. {python_cqrs-4.15.1 → python_cqrs-5.1.0}/src/cqrs/circuit_breaker.py +0 -0
  82. {python_cqrs-4.15.1 → python_cqrs-5.1.0}/src/cqrs/compressors/__init__.py +0 -0
  83. {python_cqrs-4.15.1 → python_cqrs-5.1.0}/src/cqrs/compressors/protocol.py +0 -0
  84. {python_cqrs-4.15.1 → python_cqrs-5.1.0}/src/cqrs/compressors/zlib.py +0 -0
  85. {python_cqrs-4.15.1 → python_cqrs-5.1.0}/src/cqrs/container/__init__.py +0 -0
  86. {python_cqrs-4.15.1 → python_cqrs-5.1.0}/src/cqrs/container/dependency_injector.py +0 -0
  87. {python_cqrs-4.15.1 → python_cqrs-5.1.0}/src/cqrs/container/di.py +0 -0
  88. {python_cqrs-4.15.1 → python_cqrs-5.1.0}/src/cqrs/container/dishka.py +0 -0
  89. {python_cqrs-4.15.1 → python_cqrs-5.1.0}/src/cqrs/container/protocol.py +0 -0
  90. {python_cqrs-4.15.1 → python_cqrs-5.1.0}/src/cqrs/container/scope.py +0 -0
  91. {python_cqrs-4.15.1 → python_cqrs-5.1.0}/src/cqrs/deserializers/__init__.py +0 -0
  92. {python_cqrs-4.15.1 → python_cqrs-5.1.0}/src/cqrs/deserializers/exceptions.py +0 -0
  93. {python_cqrs-4.15.1 → python_cqrs-5.1.0}/src/cqrs/deserializers/json.py +0 -0
  94. {python_cqrs-4.15.1 → python_cqrs-5.1.0}/src/cqrs/deserializers/protobuf.py +0 -0
  95. {python_cqrs-4.15.1 → python_cqrs-5.1.0}/src/cqrs/dispatcher/__init__.py +0 -0
  96. {python_cqrs-4.15.1 → python_cqrs-5.1.0}/src/cqrs/dispatcher/exceptions.py +0 -0
  97. {python_cqrs-4.15.1 → python_cqrs-5.1.0}/src/cqrs/generic_utils.py +0 -0
  98. {python_cqrs-4.15.1 → python_cqrs-5.1.0}/src/cqrs/message_brokers/__init__.py +0 -0
  99. {python_cqrs-4.15.1 → python_cqrs-5.1.0}/src/cqrs/message_brokers/amqp.py +0 -0
  100. {python_cqrs-4.15.1 → python_cqrs-5.1.0}/src/cqrs/message_brokers/devnull.py +0 -0
  101. {python_cqrs-4.15.1 → python_cqrs-5.1.0}/src/cqrs/message_brokers/kafka.py +0 -0
  102. {python_cqrs-4.15.1 → python_cqrs-5.1.0}/src/cqrs/message_brokers/protocol.py +0 -0
  103. {python_cqrs-4.15.1 → python_cqrs-5.1.0}/src/cqrs/middlewares/__init__.py +0 -0
  104. {python_cqrs-4.15.1 → python_cqrs-5.1.0}/src/cqrs/outbox/__init__.py +0 -0
  105. {python_cqrs-4.15.1 → python_cqrs-5.1.0}/src/cqrs/requests/__init__.py +0 -0
  106. {python_cqrs-4.15.1 → python_cqrs-5.1.0}/src/cqrs/saga/__init__.py +0 -0
  107. {python_cqrs-4.15.1 → python_cqrs-5.1.0}/src/cqrs/saga/models.py +0 -0
  108. {python_cqrs-4.15.1 → python_cqrs-5.1.0}/src/cqrs/saga/recovery.py +0 -0
  109. {python_cqrs-4.15.1 → python_cqrs-5.1.0}/src/cqrs/saga/storage/__init__.py +0 -0
  110. {python_cqrs-4.15.1 → python_cqrs-5.1.0}/src/cqrs/saga/storage/enums.py +0 -0
  111. {python_cqrs-4.15.1 → python_cqrs-5.1.0}/src/cqrs/saga/storage/memory.py +0 -0
  112. {python_cqrs-4.15.1 → python_cqrs-5.1.0}/src/cqrs/saga/storage/models.py +0 -0
  113. {python_cqrs-4.15.1 → python_cqrs-5.1.0}/src/cqrs/saga/storage/protocol.py +0 -0
  114. {python_cqrs-4.15.1 → python_cqrs-5.1.0}/src/cqrs/serializers/__init__.py +0 -0
  115. {python_cqrs-4.15.1 → python_cqrs-5.1.0}/src/cqrs/sqlalchemy_types.py +0 -0
  116. {python_cqrs-4.15.1 → python_cqrs-5.1.0}/src/python_cqrs.egg-info/dependency_links.txt +0 -0
  117. {python_cqrs-4.15.1 → python_cqrs-5.1.0}/src/python_cqrs.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-cqrs
3
- Version: 4.15.1
3
+ Version: 5.1.0
4
4
  Summary: Event-Driven Architecture Framework for Distributed Systems
5
5
  Author-email: Vadim Kozyrevskiy <vadikko2@mail.ru>, Dmitry Kutlubaev <kutlubaev00@mail.ru>
6
6
  Maintainer-email: Vadim Kozyrevskiy <vadikko2@mail.ru>
@@ -21,12 +21,17 @@ Requires-Dist: dataclass-wizard==0.*
21
21
  Requires-Dist: di[anyio]==0.*
22
22
  Requires-Dist: dependency-injector>=4.0
23
23
  Requires-Dist: orjson==3.*
24
- Requires-Dist: pydantic==2.*
25
- Requires-Dist: sqlalchemy[asyncio]==2.0.*
26
24
  Requires-Dist: python-dotenv==1.*
27
25
  Requires-Dist: typing-extensions>=4.0
28
26
  Provides-Extra: aiobreaker
29
27
  Requires-Dist: aiobreaker>=0.3.0; extra == "aiobreaker"
28
+ Provides-Extra: pydantic
29
+ Requires-Dist: pydantic==2.*; extra == "pydantic"
30
+ Provides-Extra: sqlalchemy
31
+ Requires-Dist: sqlalchemy[asyncio]==2.0.*; extra == "sqlalchemy"
32
+ Provides-Extra: sqlmodel
33
+ Requires-Dist: sqlmodel>=0.0.22; extra == "sqlmodel"
34
+ Requires-Dist: sqlalchemy[asyncio]==2.0.*; extra == "sqlmodel"
30
35
  Provides-Extra: dev
31
36
  Requires-Dist: pycln==2.5.0; extra == "dev"
32
37
  Requires-Dist: pre-commit==3.8.0; extra == "dev"
@@ -50,6 +55,10 @@ Requires-Dist: redis>=5.0.0; extra == "dev"
50
55
  Requires-Dist: aiobreaker>=0.3.0; extra == "dev"
51
56
  Requires-Dist: dishka>=1.0; extra == "dev"
52
57
  Requires-Dist: protobuf>=4.25.8; extra == "dev"
58
+ Requires-Dist: pydantic==2.*; extra == "dev"
59
+ Requires-Dist: sqlalchemy[asyncio]==2.0.*; extra == "dev"
60
+ Requires-Dist: sqlmodel>=0.0.22; extra == "dev"
61
+ Requires-Dist: aiosqlite>=0.20.0; extra == "dev"
53
62
  Provides-Extra: examples
54
63
  Requires-Dist: fastapi==0.109.*; extra == "examples"
55
64
  Requires-Dist: faststream[kafka]==0.5.28; extra == "examples"
@@ -59,6 +68,8 @@ Requires-Dist: aiohttp==3.14.3; extra == "examples"
59
68
  Requires-Dist: protobuf>=4.25.8; extra == "examples"
60
69
  Requires-Dist: aiosqlite>=0.20.0; extra == "examples"
61
70
  Requires-Dist: dishka>=1.0; extra == "examples"
71
+ Requires-Dist: pydantic==2.*; extra == "examples"
72
+ Requires-Dist: sqlalchemy[asyncio]==2.0.*; extra == "examples"
62
73
  Provides-Extra: kafka
63
74
  Requires-Dist: aiokafka==0.10.0; extra == "kafka"
64
75
  Requires-Dist: retry-async==0.1.*; extra == "kafka"
@@ -66,8 +77,6 @@ Provides-Extra: protobuf
66
77
  Requires-Dist: protobuf>=4.25.8; extra == "protobuf"
67
78
  Provides-Extra: rabbit
68
79
  Requires-Dist: aio-pika==9.3.0; extra == "rabbit"
69
- Provides-Extra: sqlalchemy
70
- Requires-Dist: sqlalchemy[asyncio]==2.0.*; extra == "sqlalchemy"
71
80
  Provides-Extra: dishka
72
81
  Requires-Dist: dishka>=1.0; extra == "dishka"
73
82
  Dynamic: license-file
@@ -153,7 +162,7 @@ project ([documentation](https://akhundmurad.github.io/diator/)) with several en
153
162
 
154
163
  - **Typing:** Pydantic [v2.*](https://docs.pydantic.dev/2.8/) and `IRequest`/`IResponse` interfaces — use Pydantic-based, dataclass-based, or custom Request/Response implementations.
155
164
  - **Broker:** Kafka via [aiokafka](https://github.com/aio-libs/aiokafka).
156
- - **Storage:** SQLAlchemy-based Outbox and Saga storage on MySQL/MariaDB and PostgreSQL.
165
+ - **Storage:** SQLAlchemy and SQLModel outbox and saga storage on MySQL/MariaDB and PostgreSQL.
157
166
  - **Integration:** Ready for integration with FastAPI and FastStream.
158
167
  - **Documentation:** Built-in Mermaid diagram generation (Sequence and Class diagrams).
159
168
  - **Protobuf:** Interface-level support for converting Notification events to Protobuf and back.
@@ -176,15 +185,21 @@ Do not add a Saga for a single `UPDATE` in one service, and do not use a Saga as
176
185
  pip install python-cqrs
177
186
  ```
178
187
 
179
- Optional dependencies (see [pyproject.toml](https://github.com/vadikko2/python-cqrs/blob/master/pyproject.toml) for full list):
188
+ Optional extras (5.0+ — pydantic and SQLAlchemy are no longer core dependencies; SQLModel storage is 5.1+):
180
189
 
181
190
  ```bash
182
- pip install python-cqrs[kafka] # Kafka broker (aiokafka)
183
- pip install python-cqrs[examples] # FastAPI, FastStream, uvicorn, etc.
184
- pip install python-cqrs[aiobreaker] # Circuit breaker for saga fallbacks
191
+ pip install "python-cqrs[pydantic]" # PydanticRequest / Pydantic* events
192
+ pip install "python-cqrs[sqlalchemy]" # Outbox / Saga SQLAlchemy storage
193
+ pip install "python-cqrs[sqlmodel]" # Outbox / Saga SQLModel storage
194
+ pip install "python-cqrs[kafka]" # Kafka broker (aiokafka)
195
+ pip install "python-cqrs[examples]" # FastAPI, FastStream, uvicorn, etc.
196
+ pip install "python-cqrs[aiobreaker]" # Circuit breaker for saga fallbacks
185
197
  ```
186
198
 
187
- Starting with **4.15.1**, importing `cqrs` emits a package-level EOL `DeprecationWarning` toward 5.x (plus an optional log). Suppress the log with `CQRS_SUPPRESS_V4_EOL_WARNING=1`. See [SECURITY.md](SECURITY.md) for the 4.x support window and pin guidance.
199
+ Default `Request` / `Response` / `Event` / `DomainEvent` / `NotificationEvent` aliases
200
+ are dataclass-based (`DC*`). Use `Pydantic*` types with the pydantic extra when you
201
+ need validation.
202
+
188
203
 
189
204
  ## Quick Start
190
205
 
@@ -193,7 +208,7 @@ Define a command, a handler, bind them, and run via the mediator:
193
208
  ```python
194
209
  import di
195
210
  import cqrs
196
- from cqrs.requests import bootstrap
211
+ from cqrs.bootstrap import requests as bootstrap
197
212
 
198
213
  class CreateOrderCommand(cqrs.Request):
199
214
  order_id: str
@@ -281,8 +296,8 @@ As a result of executing the command, an event may be produced to the broker.
281
296
  > By default, the command handler does not return any result, but it is not mandatory.
282
297
 
283
298
  ```python
284
- from cqrs.requests.request_handler import RequestHandler
285
- from cqrs.events.event import Event
299
+ from cqrs.handlers.request import RequestHandler
300
+ from cqrs.models.event import Event
286
301
 
287
302
  class JoinMeetingCommandHandler(RequestHandler[JoinMeetingCommand, None]):
288
303
 
@@ -309,8 +324,8 @@ the [read model](https://radekmaziarka.pl/2018/01/08/cqrs-third-step-simple-read
309
324
  > The read model can be constructed based on domain events produced by the `Command Handler`.
310
325
 
311
326
  ```python
312
- from cqrs.requests.request_handler import RequestHandler
313
- from cqrs.events.event import Event
327
+ from cqrs.handlers.request import RequestHandler
328
+ from cqrs.models.event import Event
314
329
 
315
330
  class ReadMeetingQueryHandler(RequestHandler[ReadMeetingQuery, ReadMeetingQueryResult]):
316
331
 
@@ -341,8 +356,8 @@ real-time progress updates.
341
356
 
342
357
  ```python
343
358
  import typing
344
- from cqrs.requests.request_handler import StreamingRequestHandler
345
- from cqrs.events.event import Event
359
+ from cqrs.handlers.request import StreamingRequestHandler
360
+ from cqrs.models.event import Event
346
361
 
347
362
  class ProcessFilesCommandHandler(StreamingRequestHandler[ProcessFilesCommand, FileProcessedResult]):
348
363
  def __init__(self):
@@ -378,8 +393,8 @@ when a handler successfully processes the request or when all handlers have been
378
393
 
379
394
  ```python
380
395
  import typing
381
- from cqrs.requests.cor_request_handler import CORRequestHandler
382
- from cqrs.events.event import Event
396
+ from cqrs.handlers.cor import CORRequestHandler
397
+ from cqrs.models.event import Event
383
398
 
384
399
  class CreditCardPaymentHandler(CORRequestHandler[ProcessPaymentCommand, PaymentResult]):
385
400
  def __init__(self, payment_service: PaymentServiceProtocol) -> None:
@@ -435,7 +450,7 @@ the [documentation](https://github.com/vadikko2/python-cqrs/blob/master/examples
435
450
  The package includes built-in support for generating Mermaid diagrams from Chain of Responsibility handler chains.
436
451
 
437
452
  ```python
438
- from cqrs.requests.mermaid import CoRMermaid
453
+ from cqrs.mermaid.cor import CoRMermaid
439
454
 
440
455
  # Create Mermaid generator from handler chain
441
456
  handlers = [CreditCardHandler, PayPalHandler, DefaultHandler]
@@ -605,8 +620,8 @@ application.
605
620
  ```python
606
621
  import functools
607
622
 
608
- from cqrs.events import bootstrap as event_bootstrap
609
- from cqrs.requests import bootstrap as request_bootstrap
623
+ from cqrs.bootstrap import events as event_bootstrap
624
+ from cqrs.bootstrap import requests as bootstrap as request_bootstrap
610
625
 
611
626
  from app import dependencies, mapping, orm
612
627
 
@@ -662,7 +677,7 @@ import dataclasses
662
677
  import uuid
663
678
  from cqrs.saga.models import SagaContext
664
679
  from cqrs.saga.saga import Saga
665
- from cqrs.saga.step import SagaStepHandler
680
+ from cqrs.handlers.saga import SagaStepHandler
666
681
 
667
682
  @dataclasses.dataclass
668
683
  class OrderContext(SagaContext):
@@ -684,7 +699,7 @@ class OrderSaga(Saga[OrderContext]):
684
699
  context = OrderContext(order_id="123", user_id="user_1", items=["item_1"], total_amount=100.0)
685
700
  saga_id = uuid.uuid4()
686
701
 
687
- async for step_result in mediator.stream(context, saga_id=saga_id):
702
+ async for step_result in mediator.execute(context, saga_id=saga_id):
688
703
  print(f"Step completed: {step_result.step_type.__name__}")
689
704
  # If any step fails, compensation happens automatically
690
705
  ```
@@ -697,7 +712,7 @@ The saga pattern supports fallback steps that execute automatically when primary
697
712
  from cqrs.saga.fallback import Fallback
698
713
  from cqrs.adapters.circuit_breaker import AioBreakerAdapter
699
714
  from cqrs.response import Response
700
- from cqrs.saga.step import SagaStepHandler, SagaStepResult
715
+ from cqrs.handlers.saga import SagaStepHandler, SagaStepResult
701
716
 
702
717
  class ReserveInventoryResponse(Response):
703
718
  reservation_id: str
@@ -780,7 +795,7 @@ The recovery mechanism ensures eventual consistency by:
780
795
  The package includes built-in support for generating Mermaid diagrams from Saga instances.
781
796
 
782
797
  ```python
783
- from cqrs.saga.mermaid import SagaMermaid
798
+ from cqrs.mermaid.saga import SagaMermaid
784
799
 
785
800
  # Create Mermaid generator from saga class
786
801
  saga = OrderSaga()
@@ -1020,7 +1035,7 @@ This feature is especially useful when:
1020
1035
  **Configuration:**
1021
1036
 
1022
1037
  ```python
1023
- from cqrs.requests import bootstrap
1038
+ from cqrs.bootstrap import requests as bootstrap
1024
1039
 
1025
1040
  mediator = bootstrap.bootstrap_streaming(
1026
1041
  di_container=container,
@@ -1137,7 +1152,7 @@ This allows you to stream results to clients in real-time as they are processed.
1137
1152
  ```python
1138
1153
  import fastapi
1139
1154
  import json
1140
- from cqrs.requests import bootstrap
1155
+ from cqrs.bootstrap import requests as bootstrap
1141
1156
 
1142
1157
  def streaming_mediator_factory() -> cqrs.StreamingRequestMediator:
1143
1158
  return bootstrap.bootstrap_streaming(
@@ -79,7 +79,7 @@ project ([documentation](https://akhundmurad.github.io/diator/)) with several en
79
79
 
80
80
  - **Typing:** Pydantic [v2.*](https://docs.pydantic.dev/2.8/) and `IRequest`/`IResponse` interfaces — use Pydantic-based, dataclass-based, or custom Request/Response implementations.
81
81
  - **Broker:** Kafka via [aiokafka](https://github.com/aio-libs/aiokafka).
82
- - **Storage:** SQLAlchemy-based Outbox and Saga storage on MySQL/MariaDB and PostgreSQL.
82
+ - **Storage:** SQLAlchemy and SQLModel outbox and saga storage on MySQL/MariaDB and PostgreSQL.
83
83
  - **Integration:** Ready for integration with FastAPI and FastStream.
84
84
  - **Documentation:** Built-in Mermaid diagram generation (Sequence and Class diagrams).
85
85
  - **Protobuf:** Interface-level support for converting Notification events to Protobuf and back.
@@ -102,15 +102,21 @@ Do not add a Saga for a single `UPDATE` in one service, and do not use a Saga as
102
102
  pip install python-cqrs
103
103
  ```
104
104
 
105
- Optional dependencies (see [pyproject.toml](https://github.com/vadikko2/python-cqrs/blob/master/pyproject.toml) for full list):
105
+ Optional extras (5.0+ — pydantic and SQLAlchemy are no longer core dependencies; SQLModel storage is 5.1+):
106
106
 
107
107
  ```bash
108
- pip install python-cqrs[kafka] # Kafka broker (aiokafka)
109
- pip install python-cqrs[examples] # FastAPI, FastStream, uvicorn, etc.
110
- pip install python-cqrs[aiobreaker] # Circuit breaker for saga fallbacks
108
+ pip install "python-cqrs[pydantic]" # PydanticRequest / Pydantic* events
109
+ pip install "python-cqrs[sqlalchemy]" # Outbox / Saga SQLAlchemy storage
110
+ pip install "python-cqrs[sqlmodel]" # Outbox / Saga SQLModel storage
111
+ pip install "python-cqrs[kafka]" # Kafka broker (aiokafka)
112
+ pip install "python-cqrs[examples]" # FastAPI, FastStream, uvicorn, etc.
113
+ pip install "python-cqrs[aiobreaker]" # Circuit breaker for saga fallbacks
111
114
  ```
112
115
 
113
- Starting with **4.15.1**, importing `cqrs` emits a package-level EOL `DeprecationWarning` toward 5.x (plus an optional log). Suppress the log with `CQRS_SUPPRESS_V4_EOL_WARNING=1`. See [SECURITY.md](SECURITY.md) for the 4.x support window and pin guidance.
116
+ Default `Request` / `Response` / `Event` / `DomainEvent` / `NotificationEvent` aliases
117
+ are dataclass-based (`DC*`). Use `Pydantic*` types with the pydantic extra when you
118
+ need validation.
119
+
114
120
 
115
121
  ## Quick Start
116
122
 
@@ -119,7 +125,7 @@ Define a command, a handler, bind them, and run via the mediator:
119
125
  ```python
120
126
  import di
121
127
  import cqrs
122
- from cqrs.requests import bootstrap
128
+ from cqrs.bootstrap import requests as bootstrap
123
129
 
124
130
  class CreateOrderCommand(cqrs.Request):
125
131
  order_id: str
@@ -207,8 +213,8 @@ As a result of executing the command, an event may be produced to the broker.
207
213
  > By default, the command handler does not return any result, but it is not mandatory.
208
214
 
209
215
  ```python
210
- from cqrs.requests.request_handler import RequestHandler
211
- from cqrs.events.event import Event
216
+ from cqrs.handlers.request import RequestHandler
217
+ from cqrs.models.event import Event
212
218
 
213
219
  class JoinMeetingCommandHandler(RequestHandler[JoinMeetingCommand, None]):
214
220
 
@@ -235,8 +241,8 @@ the [read model](https://radekmaziarka.pl/2018/01/08/cqrs-third-step-simple-read
235
241
  > The read model can be constructed based on domain events produced by the `Command Handler`.
236
242
 
237
243
  ```python
238
- from cqrs.requests.request_handler import RequestHandler
239
- from cqrs.events.event import Event
244
+ from cqrs.handlers.request import RequestHandler
245
+ from cqrs.models.event import Event
240
246
 
241
247
  class ReadMeetingQueryHandler(RequestHandler[ReadMeetingQuery, ReadMeetingQueryResult]):
242
248
 
@@ -267,8 +273,8 @@ real-time progress updates.
267
273
 
268
274
  ```python
269
275
  import typing
270
- from cqrs.requests.request_handler import StreamingRequestHandler
271
- from cqrs.events.event import Event
276
+ from cqrs.handlers.request import StreamingRequestHandler
277
+ from cqrs.models.event import Event
272
278
 
273
279
  class ProcessFilesCommandHandler(StreamingRequestHandler[ProcessFilesCommand, FileProcessedResult]):
274
280
  def __init__(self):
@@ -304,8 +310,8 @@ when a handler successfully processes the request or when all handlers have been
304
310
 
305
311
  ```python
306
312
  import typing
307
- from cqrs.requests.cor_request_handler import CORRequestHandler
308
- from cqrs.events.event import Event
313
+ from cqrs.handlers.cor import CORRequestHandler
314
+ from cqrs.models.event import Event
309
315
 
310
316
  class CreditCardPaymentHandler(CORRequestHandler[ProcessPaymentCommand, PaymentResult]):
311
317
  def __init__(self, payment_service: PaymentServiceProtocol) -> None:
@@ -361,7 +367,7 @@ the [documentation](https://github.com/vadikko2/python-cqrs/blob/master/examples
361
367
  The package includes built-in support for generating Mermaid diagrams from Chain of Responsibility handler chains.
362
368
 
363
369
  ```python
364
- from cqrs.requests.mermaid import CoRMermaid
370
+ from cqrs.mermaid.cor import CoRMermaid
365
371
 
366
372
  # Create Mermaid generator from handler chain
367
373
  handlers = [CreditCardHandler, PayPalHandler, DefaultHandler]
@@ -531,8 +537,8 @@ application.
531
537
  ```python
532
538
  import functools
533
539
 
534
- from cqrs.events import bootstrap as event_bootstrap
535
- from cqrs.requests import bootstrap as request_bootstrap
540
+ from cqrs.bootstrap import events as event_bootstrap
541
+ from cqrs.bootstrap import requests as bootstrap as request_bootstrap
536
542
 
537
543
  from app import dependencies, mapping, orm
538
544
 
@@ -588,7 +594,7 @@ import dataclasses
588
594
  import uuid
589
595
  from cqrs.saga.models import SagaContext
590
596
  from cqrs.saga.saga import Saga
591
- from cqrs.saga.step import SagaStepHandler
597
+ from cqrs.handlers.saga import SagaStepHandler
592
598
 
593
599
  @dataclasses.dataclass
594
600
  class OrderContext(SagaContext):
@@ -610,7 +616,7 @@ class OrderSaga(Saga[OrderContext]):
610
616
  context = OrderContext(order_id="123", user_id="user_1", items=["item_1"], total_amount=100.0)
611
617
  saga_id = uuid.uuid4()
612
618
 
613
- async for step_result in mediator.stream(context, saga_id=saga_id):
619
+ async for step_result in mediator.execute(context, saga_id=saga_id):
614
620
  print(f"Step completed: {step_result.step_type.__name__}")
615
621
  # If any step fails, compensation happens automatically
616
622
  ```
@@ -623,7 +629,7 @@ The saga pattern supports fallback steps that execute automatically when primary
623
629
  from cqrs.saga.fallback import Fallback
624
630
  from cqrs.adapters.circuit_breaker import AioBreakerAdapter
625
631
  from cqrs.response import Response
626
- from cqrs.saga.step import SagaStepHandler, SagaStepResult
632
+ from cqrs.handlers.saga import SagaStepHandler, SagaStepResult
627
633
 
628
634
  class ReserveInventoryResponse(Response):
629
635
  reservation_id: str
@@ -706,7 +712,7 @@ The recovery mechanism ensures eventual consistency by:
706
712
  The package includes built-in support for generating Mermaid diagrams from Saga instances.
707
713
 
708
714
  ```python
709
- from cqrs.saga.mermaid import SagaMermaid
715
+ from cqrs.mermaid.saga import SagaMermaid
710
716
 
711
717
  # Create Mermaid generator from saga class
712
718
  saga = OrderSaga()
@@ -946,7 +952,7 @@ This feature is especially useful when:
946
952
  **Configuration:**
947
953
 
948
954
  ```python
949
- from cqrs.requests import bootstrap
955
+ from cqrs.bootstrap import requests as bootstrap
950
956
 
951
957
  mediator = bootstrap.bootstrap_streaming(
952
958
  di_container=container,
@@ -1063,7 +1069,7 @@ This allows you to stream results to clients in real-time as they are processed.
1063
1069
  ```python
1064
1070
  import fastapi
1065
1071
  import json
1066
- from cqrs.requests import bootstrap
1072
+ from cqrs.bootstrap import requests as bootstrap
1067
1073
 
1068
1074
  def streaming_mediator_factory() -> cqrs.StreamingRequestMediator:
1069
1075
  return bootstrap.bootstrap_streaming(
@@ -21,8 +21,6 @@ dependencies = [
21
21
  "di[anyio]==0.*",
22
22
  "dependency-injector>=4.0",
23
23
  "orjson==3.*",
24
- "pydantic==2.*",
25
- "sqlalchemy[asyncio]==2.0.*",
26
24
  "python-dotenv==1.*",
27
25
  "typing-extensions>=4.0",
28
26
  ]
@@ -31,10 +29,13 @@ maintainers = [{ name = "Vadim Kozyrevskiy", email = "vadikko2@mail.ru" }]
31
29
  name = "python-cqrs"
32
30
  readme = "README.md"
33
31
  requires-python = ">=3.10"
34
- version = "4.15.1"
32
+ version = "5.1.0"
35
33
 
36
34
  [project.optional-dependencies]
37
35
  aiobreaker = ["aiobreaker>=0.3.0"]
36
+ pydantic = ["pydantic==2.*"]
37
+ sqlalchemy = ["sqlalchemy[asyncio]==2.0.*"]
38
+ sqlmodel = ["sqlmodel>=0.0.22", "sqlalchemy[asyncio]==2.0.*"]
38
39
  dev = [
39
40
  # Develope tools
40
41
  "pycln==2.5.0",
@@ -61,6 +62,11 @@ dev = [
61
62
  "aiobreaker>=0.3.0", # from aiobreaker
62
63
  "dishka>=1.0", # scoped-deps dishka adapter tests / examples
63
64
  "protobuf>=4.25.8", # protobuf codec / deserializer unit tests
65
+ # Full suite extras
66
+ "pydantic==2.*",
67
+ "sqlalchemy[asyncio]==2.0.*",
68
+ "sqlmodel>=0.0.22",
69
+ "aiosqlite>=0.20.0",
64
70
  ]
65
71
  examples = [
66
72
  "fastapi==0.109.*",
@@ -71,11 +77,12 @@ examples = [
71
77
  "protobuf>=4.25.8",
72
78
  "aiosqlite>=0.20.0",
73
79
  "dishka>=1.0",
80
+ "pydantic==2.*",
81
+ "sqlalchemy[asyncio]==2.0.*",
74
82
  ]
75
83
  kafka = ["aiokafka==0.10.0", "retry-async==0.1.*"]
76
84
  protobuf = ["protobuf>=4.25.8"]
77
85
  rabbit = ["aio-pika==9.3.0"]
78
- sqlalchemy = ["sqlalchemy[asyncio]==2.0.*"]
79
86
  dishka = ["dishka>=1.0"]
80
87
 
81
88
  [project.urls]
@@ -88,6 +95,11 @@ addopts = "--junit-xml=report.xml"
88
95
  asyncio_mode = "auto"
89
96
  junit_family = "xunit1"
90
97
  testpaths = ["tests"]
98
+ markers = [
99
+ "pydantic: tests that require the pydantic optional extra",
100
+ "sqlalchemy: tests that require the sqlalchemy optional extra",
101
+ "sqlmodel: tests that require the sqlmodel optional extra",
102
+ ]
91
103
 
92
104
  [tool.setuptools.packages.find]
93
105
  where = ["src"]
@@ -0,0 +1,218 @@
1
+ import typing
2
+
3
+ if typing.TYPE_CHECKING:
4
+ from cqrs.models.pydantic import (
5
+ PydanticDomainEvent,
6
+ PydanticEvent,
7
+ PydanticNotificationEvent,
8
+ )
9
+ from cqrs.models.pydantic import PydanticRequest
10
+ from cqrs.models.pydantic import PydanticResponse
11
+ from cqrs.outbox.sqlalchemy import (
12
+ SqlAlchemyOutboxedEventRepository,
13
+ rebind_outbox_model,
14
+ )
15
+ from cqrs.outbox.sqlmodel import SqlModelOutboxedEventRepository
16
+ from cqrs.sqlalchemy_types import (
17
+ Binary16,
18
+ DialectAwareType,
19
+ DialectTypeHandler,
20
+ JSONType,
21
+ PayloadBinary,
22
+ UUIDBinary,
23
+ )
24
+
25
+
26
+ from cqrs.compressors import Compressor, ZlibCompressor
27
+ from cqrs.container.di import DIContainer
28
+ from cqrs.container.protocol import Container, SupportsScope
29
+ from cqrs.container.scope import (
30
+ ScopeAwareContainer,
31
+ ScopeStrategy,
32
+ bind_scope,
33
+ current_container,
34
+ enter_scope,
35
+ )
36
+ from cqrs.circuit_breaker import ICircuitBreaker
37
+ from cqrs.events import EventMap
38
+ from cqrs.events.fallback import EventHandlerFallback
39
+ from cqrs.models.event import (
40
+ DCEvent,
41
+ DCDomainEvent,
42
+ DCNotificationEvent,
43
+ DomainEvent,
44
+ Event,
45
+ IDomainEvent,
46
+ IEvent,
47
+ INotificationEvent,
48
+ NotificationEvent,
49
+ )
50
+ from cqrs.events.event_emitter import EventEmitter
51
+ from cqrs.handlers.event import EventHandler
52
+ from cqrs.mediators import (
53
+ EventMediator,
54
+ RequestMediator,
55
+ SagaMediator,
56
+ StreamingRequestMediator,
57
+ )
58
+ from cqrs.mapping.outbox import OutboxedEventMap
59
+ from cqrs.outbox.repository import (
60
+ EventStatus,
61
+ OutboxedEvent,
62
+ OutboxedEventRepository,
63
+ )
64
+ from cqrs.message_brokers.producer import EventProducer
65
+ from cqrs.deserializers import DeserializeProtobufError, ProtobufDeserializer
66
+ from cqrs.serializers import (
67
+ EventCodec,
68
+ EventSerializer,
69
+ JsonEventSerializer,
70
+ ProtobufEventSerializer,
71
+ )
72
+ from cqrs.requests.fallback import RequestHandlerFallback
73
+ from cqrs.mapping.requests import RequestMap, SagaMap
74
+ from cqrs.mermaid.cor import CoRMermaid
75
+ from cqrs.models.request import DCRequest, IRequest, Request
76
+ from cqrs.handlers.request import (
77
+ RequestHandler,
78
+ StreamingRequestHandler,
79
+ )
80
+ from cqrs.models.response import DCResponse, IResponse, Response
81
+ from cqrs.mermaid.saga import SagaMermaid
82
+ from cqrs.saga.models import ContextT
83
+ from cqrs.saga.saga import Saga
84
+ from cqrs.handlers.saga import (
85
+ Resp,
86
+ SagaStepHandler,
87
+ SagaStepResult,
88
+ )
89
+
90
+
91
+ from cqrs._dataclass_utils import pydantic_extra_error
92
+
93
+ __all__ = (
94
+ "ICircuitBreaker",
95
+ "EventHandlerFallback",
96
+ "RequestHandlerFallback",
97
+ "RequestMediator",
98
+ "SagaMediator",
99
+ "StreamingRequestMediator",
100
+ "EventMediator",
101
+ "DomainEvent",
102
+ "IDomainEvent",
103
+ "DCDomainEvent",
104
+ "PydanticDomainEvent",
105
+ "NotificationEvent",
106
+ "INotificationEvent",
107
+ "DCNotificationEvent",
108
+ "PydanticNotificationEvent",
109
+ "Event",
110
+ "IEvent",
111
+ "DCEvent",
112
+ "PydanticEvent",
113
+ "EventEmitter",
114
+ "EventHandler",
115
+ "EventMap",
116
+ "OutboxedEventMap",
117
+ "EventStatus",
118
+ "OutboxedEvent",
119
+ "Request",
120
+ "IRequest",
121
+ "DCRequest",
122
+ "PydanticRequest",
123
+ "RequestHandler",
124
+ "StreamingRequestHandler",
125
+ "RequestMap",
126
+ "SagaMap",
127
+ "Response",
128
+ "IResponse",
129
+ "DCResponse",
130
+ "PydanticResponse",
131
+ "OutboxedEventRepository",
132
+ "SqlAlchemyOutboxedEventRepository",
133
+ "SqlModelOutboxedEventRepository",
134
+ "EventProducer",
135
+ "EventSerializer",
136
+ "EventCodec",
137
+ "JsonEventSerializer",
138
+ "ProtobufEventSerializer",
139
+ "ProtobufDeserializer",
140
+ "DeserializeProtobufError",
141
+ "Container",
142
+ "SupportsScope",
143
+ "DIContainer",
144
+ "ScopeAwareContainer",
145
+ "ScopeStrategy",
146
+ "bind_scope",
147
+ "current_container",
148
+ "enter_scope",
149
+ "Compressor",
150
+ "ZlibCompressor",
151
+ "rebind_outbox_model",
152
+ "Saga",
153
+ "SagaStepHandler",
154
+ "SagaStepResult",
155
+ "Resp",
156
+ "ContextT",
157
+ "SagaMermaid",
158
+ "CoRMermaid",
159
+ "DialectAwareType",
160
+ "DialectTypeHandler",
161
+ "UUIDBinary",
162
+ "Binary16",
163
+ "JSONType",
164
+ "PayloadBinary",
165
+ )
166
+
167
+ _LAZY_PYDANTIC = {
168
+ "PydanticEvent": ("cqrs.models.pydantic", "PydanticEvent"),
169
+ "PydanticDomainEvent": ("cqrs.models.pydantic", "PydanticDomainEvent"),
170
+ "PydanticNotificationEvent": (
171
+ "cqrs.models.pydantic",
172
+ "PydanticNotificationEvent",
173
+ ),
174
+ "PydanticRequest": ("cqrs.models.pydantic", "PydanticRequest"),
175
+ "PydanticResponse": ("cqrs.models.pydantic", "PydanticResponse"),
176
+ }
177
+
178
+ _LAZY_SQLALCHEMY = {
179
+ "SqlAlchemyOutboxedEventRepository": (
180
+ "cqrs.outbox.sqlalchemy",
181
+ "SqlAlchemyOutboxedEventRepository",
182
+ ),
183
+ "rebind_outbox_model": ("cqrs.outbox.sqlalchemy", "rebind_outbox_model"),
184
+ "DialectAwareType": ("cqrs.sqlalchemy_types", "DialectAwareType"),
185
+ "DialectTypeHandler": ("cqrs.sqlalchemy_types", "DialectTypeHandler"),
186
+ "UUIDBinary": ("cqrs.sqlalchemy_types", "UUIDBinary"),
187
+ "Binary16": ("cqrs.sqlalchemy_types", "Binary16"),
188
+ "JSONType": ("cqrs.sqlalchemy_types", "JSONType"),
189
+ "PayloadBinary": ("cqrs.sqlalchemy_types", "PayloadBinary"),
190
+ }
191
+
192
+ _LAZY_SQLMODEL = {
193
+ "SqlModelOutboxedEventRepository": (
194
+ "cqrs.outbox.sqlmodel",
195
+ "SqlModelOutboxedEventRepository",
196
+ ),
197
+ }
198
+
199
+
200
+ def __getattr__(name: str) -> typing.Any:
201
+ if name in _LAZY_PYDANTIC:
202
+ module_name, attr = _LAZY_PYDANTIC[name]
203
+ try:
204
+ module = __import__(module_name, fromlist=[attr])
205
+ except ImportError as exc:
206
+ raise pydantic_extra_error(name) from exc
207
+ return getattr(module, attr)
208
+ if name in _LAZY_SQLALCHEMY:
209
+ module_name, attr = _LAZY_SQLALCHEMY[name]
210
+ # ImportError message is raised by the sqlalchemy modules themselves.
211
+ module = __import__(module_name, fromlist=[attr])
212
+ return getattr(module, attr)
213
+ if name in _LAZY_SQLMODEL:
214
+ module_name, attr = _LAZY_SQLMODEL[name]
215
+ # ImportError message is raised by the sqlmodel modules themselves.
216
+ module = __import__(module_name, fromlist=[attr])
217
+ return getattr(module, attr)
218
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")