python-cqrs 4.15.0__tar.gz → 5.0.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 (113) hide show
  1. {python_cqrs-4.15.0/src/python_cqrs.egg-info → python_cqrs-5.0.0}/PKG-INFO +70 -27
  2. {python_cqrs-4.15.0 → python_cqrs-5.0.0}/README.md +61 -22
  3. {python_cqrs-4.15.0 → python_cqrs-5.0.0}/pyproject.toml +12 -4
  4. python_cqrs-5.0.0/src/cqrs/__init__.py +204 -0
  5. python_cqrs-5.0.0/src/cqrs/_dataclass_utils.py +99 -0
  6. python_cqrs-5.0.0/src/cqrs/bootstrap/__init__.py +5 -0
  7. python_cqrs-4.15.0/src/cqrs/requests/bootstrap.py → python_cqrs-5.0.0/src/cqrs/bootstrap/requests.py +1 -1
  8. python_cqrs-4.15.0/src/cqrs/saga/bootstrap.py → python_cqrs-5.0.0/src/cqrs/bootstrap/saga.py +4 -4
  9. {python_cqrs-4.15.0 → python_cqrs-5.0.0}/src/cqrs/dispatcher/event.py +3 -3
  10. {python_cqrs-4.15.0 → python_cqrs-5.0.0}/src/cqrs/dispatcher/models.py +3 -3
  11. {python_cqrs-4.15.0 → python_cqrs-5.0.0}/src/cqrs/dispatcher/request.py +4 -4
  12. {python_cqrs-4.15.0 → python_cqrs-5.0.0}/src/cqrs/dispatcher/saga.py +1 -1
  13. {python_cqrs-4.15.0 → python_cqrs-5.0.0}/src/cqrs/dispatcher/streaming.py +3 -3
  14. python_cqrs-5.0.0/src/cqrs/events/__init__.py +63 -0
  15. {python_cqrs-4.15.0 → python_cqrs-5.0.0}/src/cqrs/events/event_emitter.py +3 -2
  16. {python_cqrs-4.15.0 → python_cqrs-5.0.0}/src/cqrs/events/event_processor.py +2 -2
  17. {python_cqrs-4.15.0 → python_cqrs-5.0.0}/src/cqrs/events/fallback.py +1 -1
  18. python_cqrs-5.0.0/src/cqrs/handlers/__init__.py +14 -0
  19. python_cqrs-4.15.0/src/cqrs/requests/cor_request_handler.py → python_cqrs-5.0.0/src/cqrs/handlers/cor.py +2 -2
  20. python_cqrs-4.15.0/src/cqrs/events/event_handler.py → python_cqrs-5.0.0/src/cqrs/handlers/event.py +1 -1
  21. python_cqrs-4.15.0/src/cqrs/requests/request_handler.py → python_cqrs-5.0.0/src/cqrs/handlers/request.py +2 -2
  22. python_cqrs-4.15.0/src/cqrs/saga/step.py → python_cqrs-5.0.0/src/cqrs/handlers/saga.py +2 -2
  23. python_cqrs-5.0.0/src/cqrs/mapping/__init__.py +5 -0
  24. python_cqrs-4.15.0/src/cqrs/events/map.py → python_cqrs-5.0.0/src/cqrs/mapping/events.py +2 -2
  25. python_cqrs-4.15.0/src/cqrs/outbox/map.py → python_cqrs-5.0.0/src/cqrs/mapping/outbox.py +1 -1
  26. python_cqrs-4.15.0/src/cqrs/requests/map.py → python_cqrs-5.0.0/src/cqrs/mapping/requests.py +3 -3
  27. python_cqrs-5.0.0/src/cqrs/mediators/__init__.py +11 -0
  28. python_cqrs-5.0.0/src/cqrs/mediators/_common.py +26 -0
  29. python_cqrs-5.0.0/src/cqrs/mediators/event.py +50 -0
  30. python_cqrs-5.0.0/src/cqrs/mediators/request.py +109 -0
  31. python_cqrs-5.0.0/src/cqrs/mediators/saga.py +152 -0
  32. python_cqrs-5.0.0/src/cqrs/mediators/streaming.py +128 -0
  33. python_cqrs-5.0.0/src/cqrs/mermaid/__init__.py +4 -0
  34. python_cqrs-4.15.0/src/cqrs/requests/mermaid.py → python_cqrs-5.0.0/src/cqrs/mermaid/cor.py +1 -1
  35. python_cqrs-4.15.0/src/cqrs/saga/mermaid.py → python_cqrs-5.0.0/src/cqrs/mermaid/saga.py +1 -1
  36. {python_cqrs-4.15.0/src/cqrs → python_cqrs-5.0.0/src/cqrs/message_brokers}/producer.py +7 -4
  37. {python_cqrs-4.15.0 → python_cqrs-5.0.0}/src/cqrs/middlewares/base.py +1 -1
  38. {python_cqrs-4.15.0 → python_cqrs-5.0.0}/src/cqrs/middlewares/logging.py +2 -2
  39. python_cqrs-5.0.0/src/cqrs/models/__init__.py +57 -0
  40. python_cqrs-5.0.0/src/cqrs/models/event.py +208 -0
  41. python_cqrs-5.0.0/src/cqrs/models/pydantic.py +130 -0
  42. python_cqrs-5.0.0/src/cqrs/models/request.py +81 -0
  43. python_cqrs-5.0.0/src/cqrs/models/response.py +74 -0
  44. {python_cqrs-4.15.0 → python_cqrs-5.0.0}/src/cqrs/outbox/mock.py +2 -1
  45. {python_cqrs-4.15.0 → python_cqrs-5.0.0}/src/cqrs/outbox/repository.py +1 -1
  46. {python_cqrs-4.15.0 → python_cqrs-5.0.0}/src/cqrs/outbox/sqlalchemy.py +15 -3
  47. {python_cqrs-4.15.0 → python_cqrs-5.0.0}/src/cqrs/requests/fallback.py +1 -1
  48. {python_cqrs-4.15.0 → python_cqrs-5.0.0}/src/cqrs/saga/circuit_breaker.py +1 -1
  49. {python_cqrs-4.15.0 → python_cqrs-5.0.0}/src/cqrs/saga/compensation.py +1 -1
  50. {python_cqrs-4.15.0 → python_cqrs-5.0.0}/src/cqrs/saga/execution.py +1 -1
  51. {python_cqrs-4.15.0 → python_cqrs-5.0.0}/src/cqrs/saga/fallback.py +1 -1
  52. {python_cqrs-4.15.0 → python_cqrs-5.0.0}/src/cqrs/saga/saga.py +1 -1
  53. {python_cqrs-4.15.0 → python_cqrs-5.0.0}/src/cqrs/saga/validation.py +1 -1
  54. {python_cqrs-4.15.0 → python_cqrs-5.0.0}/src/cqrs/serializers/default.py +1 -1
  55. {python_cqrs-4.15.0 → python_cqrs-5.0.0}/src/cqrs/serializers/json.py +1 -1
  56. {python_cqrs-4.15.0 → python_cqrs-5.0.0}/src/cqrs/serializers/protobuf.py +1 -1
  57. {python_cqrs-4.15.0 → python_cqrs-5.0.0}/src/cqrs/serializers/protocol.py +1 -1
  58. {python_cqrs-4.15.0 → python_cqrs-5.0.0}/src/cqrs/types.py +1 -1
  59. {python_cqrs-4.15.0 → python_cqrs-5.0.0/src/python_cqrs.egg-info}/PKG-INFO +70 -27
  60. {python_cqrs-4.15.0 → python_cqrs-5.0.0}/src/python_cqrs.egg-info/SOURCES.txt +29 -17
  61. {python_cqrs-4.15.0 → python_cqrs-5.0.0}/src/python_cqrs.egg-info/requires.txt +7 -2
  62. python_cqrs-4.15.0/src/cqrs/__init__.py +0 -151
  63. python_cqrs-4.15.0/src/cqrs/events/__init__.py +0 -50
  64. python_cqrs-4.15.0/src/cqrs/events/event.py +0 -393
  65. python_cqrs-4.15.0/src/cqrs/mediator.py +0 -423
  66. python_cqrs-4.15.0/src/cqrs/requests/request.py +0 -152
  67. python_cqrs-4.15.0/src/cqrs/response.py +0 -146
  68. {python_cqrs-4.15.0 → python_cqrs-5.0.0}/LICENSE +0 -0
  69. {python_cqrs-4.15.0 → python_cqrs-5.0.0}/setup.cfg +0 -0
  70. {python_cqrs-4.15.0 → python_cqrs-5.0.0}/src/cqrs/adapters/__init__.py +0 -0
  71. {python_cqrs-4.15.0 → python_cqrs-5.0.0}/src/cqrs/adapters/amqp.py +0 -0
  72. {python_cqrs-4.15.0 → python_cqrs-5.0.0}/src/cqrs/adapters/circuit_breaker.py +0 -0
  73. {python_cqrs-4.15.0 → python_cqrs-5.0.0}/src/cqrs/adapters/kafka.py +0 -0
  74. {python_cqrs-4.15.0 → python_cqrs-5.0.0}/src/cqrs/adapters/protocol.py +0 -0
  75. /python_cqrs-4.15.0/src/cqrs/events/bootstrap.py → /python_cqrs-5.0.0/src/cqrs/bootstrap/events.py +0 -0
  76. {python_cqrs-4.15.0 → python_cqrs-5.0.0}/src/cqrs/circuit_breaker.py +0 -0
  77. {python_cqrs-4.15.0 → python_cqrs-5.0.0}/src/cqrs/compressors/__init__.py +0 -0
  78. {python_cqrs-4.15.0 → python_cqrs-5.0.0}/src/cqrs/compressors/protocol.py +0 -0
  79. {python_cqrs-4.15.0 → python_cqrs-5.0.0}/src/cqrs/compressors/zlib.py +0 -0
  80. {python_cqrs-4.15.0 → python_cqrs-5.0.0}/src/cqrs/container/__init__.py +0 -0
  81. {python_cqrs-4.15.0 → python_cqrs-5.0.0}/src/cqrs/container/dependency_injector.py +0 -0
  82. {python_cqrs-4.15.0 → python_cqrs-5.0.0}/src/cqrs/container/di.py +0 -0
  83. {python_cqrs-4.15.0 → python_cqrs-5.0.0}/src/cqrs/container/dishka.py +0 -0
  84. {python_cqrs-4.15.0 → python_cqrs-5.0.0}/src/cqrs/container/protocol.py +0 -0
  85. {python_cqrs-4.15.0 → python_cqrs-5.0.0}/src/cqrs/container/scope.py +0 -0
  86. {python_cqrs-4.15.0 → python_cqrs-5.0.0}/src/cqrs/deserializers/__init__.py +0 -0
  87. {python_cqrs-4.15.0 → python_cqrs-5.0.0}/src/cqrs/deserializers/exceptions.py +0 -0
  88. {python_cqrs-4.15.0 → python_cqrs-5.0.0}/src/cqrs/deserializers/json.py +0 -0
  89. {python_cqrs-4.15.0 → python_cqrs-5.0.0}/src/cqrs/deserializers/protobuf.py +0 -0
  90. {python_cqrs-4.15.0 → python_cqrs-5.0.0}/src/cqrs/dispatcher/__init__.py +0 -0
  91. {python_cqrs-4.15.0 → python_cqrs-5.0.0}/src/cqrs/dispatcher/exceptions.py +0 -0
  92. {python_cqrs-4.15.0 → python_cqrs-5.0.0}/src/cqrs/generic_utils.py +0 -0
  93. {python_cqrs-4.15.0 → python_cqrs-5.0.0}/src/cqrs/message_brokers/__init__.py +0 -0
  94. {python_cqrs-4.15.0 → python_cqrs-5.0.0}/src/cqrs/message_brokers/amqp.py +0 -0
  95. {python_cqrs-4.15.0 → python_cqrs-5.0.0}/src/cqrs/message_brokers/devnull.py +0 -0
  96. {python_cqrs-4.15.0 → python_cqrs-5.0.0}/src/cqrs/message_brokers/kafka.py +0 -0
  97. {python_cqrs-4.15.0 → python_cqrs-5.0.0}/src/cqrs/message_brokers/protocol.py +0 -0
  98. {python_cqrs-4.15.0 → python_cqrs-5.0.0}/src/cqrs/middlewares/__init__.py +0 -0
  99. {python_cqrs-4.15.0 → python_cqrs-5.0.0}/src/cqrs/outbox/__init__.py +0 -0
  100. {python_cqrs-4.15.0 → python_cqrs-5.0.0}/src/cqrs/requests/__init__.py +0 -0
  101. {python_cqrs-4.15.0 → python_cqrs-5.0.0}/src/cqrs/saga/__init__.py +0 -0
  102. {python_cqrs-4.15.0 → python_cqrs-5.0.0}/src/cqrs/saga/models.py +0 -0
  103. {python_cqrs-4.15.0 → python_cqrs-5.0.0}/src/cqrs/saga/recovery.py +0 -0
  104. {python_cqrs-4.15.0 → python_cqrs-5.0.0}/src/cqrs/saga/storage/__init__.py +0 -0
  105. {python_cqrs-4.15.0 → python_cqrs-5.0.0}/src/cqrs/saga/storage/enums.py +0 -0
  106. {python_cqrs-4.15.0 → python_cqrs-5.0.0}/src/cqrs/saga/storage/memory.py +0 -0
  107. {python_cqrs-4.15.0 → python_cqrs-5.0.0}/src/cqrs/saga/storage/models.py +0 -0
  108. {python_cqrs-4.15.0 → python_cqrs-5.0.0}/src/cqrs/saga/storage/protocol.py +0 -0
  109. {python_cqrs-4.15.0 → python_cqrs-5.0.0}/src/cqrs/saga/storage/sqlalchemy.py +0 -0
  110. {python_cqrs-4.15.0 → python_cqrs-5.0.0}/src/cqrs/serializers/__init__.py +0 -0
  111. {python_cqrs-4.15.0 → python_cqrs-5.0.0}/src/cqrs/sqlalchemy_types.py +0 -0
  112. {python_cqrs-4.15.0 → python_cqrs-5.0.0}/src/python_cqrs.egg-info/dependency_links.txt +0 -0
  113. {python_cqrs-4.15.0 → python_cqrs-5.0.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.0
3
+ Version: 5.0.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,14 @@ 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"
30
32
  Provides-Extra: dev
31
33
  Requires-Dist: pycln==2.5.0; extra == "dev"
32
34
  Requires-Dist: pre-commit==3.8.0; extra == "dev"
@@ -50,6 +52,8 @@ Requires-Dist: redis>=5.0.0; extra == "dev"
50
52
  Requires-Dist: aiobreaker>=0.3.0; extra == "dev"
51
53
  Requires-Dist: dishka>=1.0; extra == "dev"
52
54
  Requires-Dist: protobuf>=4.25.8; extra == "dev"
55
+ Requires-Dist: pydantic==2.*; extra == "dev"
56
+ Requires-Dist: sqlalchemy[asyncio]==2.0.*; extra == "dev"
53
57
  Provides-Extra: examples
54
58
  Requires-Dist: fastapi==0.109.*; extra == "examples"
55
59
  Requires-Dist: faststream[kafka]==0.5.28; extra == "examples"
@@ -59,6 +63,8 @@ Requires-Dist: aiohttp==3.14.3; extra == "examples"
59
63
  Requires-Dist: protobuf>=4.25.8; extra == "examples"
60
64
  Requires-Dist: aiosqlite>=0.20.0; extra == "examples"
61
65
  Requires-Dist: dishka>=1.0; extra == "examples"
66
+ Requires-Dist: pydantic==2.*; extra == "examples"
67
+ Requires-Dist: sqlalchemy[asyncio]==2.0.*; extra == "examples"
62
68
  Provides-Extra: kafka
63
69
  Requires-Dist: aiokafka==0.10.0; extra == "kafka"
64
70
  Requires-Dist: retry-async==0.1.*; extra == "kafka"
@@ -66,8 +72,6 @@ Provides-Extra: protobuf
66
72
  Requires-Dist: protobuf>=4.25.8; extra == "protobuf"
67
73
  Provides-Extra: rabbit
68
74
  Requires-Dist: aio-pika==9.3.0; extra == "rabbit"
69
- Provides-Extra: sqlalchemy
70
- Requires-Dist: sqlalchemy[asyncio]==2.0.*; extra == "sqlalchemy"
71
75
  Provides-Extra: dishka
72
76
  Requires-Dist: dishka>=1.0; extra == "dishka"
73
77
  Dynamic: license-file
@@ -119,6 +123,7 @@ Dynamic: license-file
119
123
  - [Overview](#overview)
120
124
  - [When NOT to use python-cqrs](#when-not-to-use-python-cqrs)
121
125
  - [Installation](#installation)
126
+ - [Version Support](#version-support)
122
127
  - [Quick Start](#quick-start)
123
128
  - [Request and Response Types](#request-and-response-types)
124
129
  - [Request Handlers](#request-handlers)
@@ -184,14 +189,52 @@ Do not add a Saga for a single `UPDATE` in one service, and do not use a Saga as
184
189
  pip install python-cqrs
185
190
  ```
186
191
 
187
- Optional dependencies (see [pyproject.toml](https://github.com/vadikko2/python-cqrs/blob/master/pyproject.toml) for full list):
192
+ Optional extras (5.0+ — pydantic and SQLAlchemy are no longer core dependencies):
193
+
194
+ ```bash
195
+ pip install "python-cqrs[pydantic]" # PydanticRequest / Pydantic* events
196
+ pip install "python-cqrs[sqlalchemy]" # Outbox / Saga SQLAlchemy storage
197
+ pip install "python-cqrs[kafka]" # Kafka broker (aiokafka)
198
+ pip install "python-cqrs[examples]" # FastAPI, FastStream, uvicorn, etc.
199
+ pip install "python-cqrs[aiobreaker]" # Circuit breaker for saga fallbacks
200
+ ```
201
+
202
+ Default `Request` / `Response` / `Event` / `DomainEvent` / `NotificationEvent` aliases
203
+ are dataclass-based (`DC*`). Use `Pydantic*` types with the pydantic extra when you
204
+ need validation.
205
+
206
+ ### Import paths (5.0+)
207
+
208
+ | Area | Import |
209
+ |------|--------|
210
+ | Bootstrap | `from cqrs.bootstrap import requests, events, saga` |
211
+ | Mediators | `from cqrs.mediators import RequestMediator, SagaMediator, …` |
212
+ | Handlers | `from cqrs.handlers import RequestHandler, EventHandler, …` |
213
+ | Models | `from cqrs.models import Request, Event, …` / `cqrs.models.pydantic` |
214
+ | Maps | `from cqrs.mapping import RequestMap, EventMap, OutboxedEventMap` |
215
+ | Mermaid | `from cqrs.mermaid import CoRMermaid, SagaMermaid` |
216
+ | Outbox producer | `from cqrs.message_brokers.producer import EventProducer` |
217
+
218
+ `SagaMediator.execute(...)` replaces the former `SagaMediator.stream(...)`
219
+ (StreamingRequestMediator.stream is unchanged).
220
+
221
+ ## Version Support
222
+
223
+ | Line | Git branch | What changes |
224
+ |------|------------|--------------|
225
+ | **4.x** | `4.x` | Bug fixes and security only (~12 months after 5.0.0) |
226
+ | **5.x** | `master` | Features and breaking changes |
227
+
228
+ Stay on 4.x until you migrate:
188
229
 
189
230
  ```bash
190
- pip install python-cqrs[kafka] # Kafka broker (aiokafka)
191
- pip install python-cqrs[examples] # FastAPI, FastStream, uvicorn, etc.
192
- pip install python-cqrs[aiobreaker] # Circuit breaker for saga fallbacks
231
+ pip install "python-cqrs>=4,<5"
193
232
  ```
194
233
 
234
+ Docs: [4.x](https://mkdocs.python-cqrs.dev/4.0/) and [5.x / latest](https://mkdocs.python-cqrs.dev/latest/) (version switcher after mike deploy). See [SECURITY.md](SECURITY.md) for the support window and vulnerability reporting, and [CONTRIBUTING.md](CONTRIBUTING.md#8-maintaining--fixing-4x) for how to contribute 4.x bugfixes.
235
+
236
+ A package-level EOL `DeprecationWarning` toward 5.x will be added on the **first 4.x patch after 5.0.0** — not before.
237
+
195
238
  ## Quick Start
196
239
 
197
240
  Define a command, a handler, bind them, and run via the mediator:
@@ -199,7 +242,7 @@ Define a command, a handler, bind them, and run via the mediator:
199
242
  ```python
200
243
  import di
201
244
  import cqrs
202
- from cqrs.requests import bootstrap
245
+ from cqrs.bootstrap import requests as bootstrap
203
246
 
204
247
  class CreateOrderCommand(cqrs.Request):
205
248
  order_id: str
@@ -287,8 +330,8 @@ As a result of executing the command, an event may be produced to the broker.
287
330
  > By default, the command handler does not return any result, but it is not mandatory.
288
331
 
289
332
  ```python
290
- from cqrs.requests.request_handler import RequestHandler
291
- from cqrs.events.event import Event
333
+ from cqrs.handlers.request import RequestHandler
334
+ from cqrs.models.event import Event
292
335
 
293
336
  class JoinMeetingCommandHandler(RequestHandler[JoinMeetingCommand, None]):
294
337
 
@@ -315,8 +358,8 @@ the [read model](https://radekmaziarka.pl/2018/01/08/cqrs-third-step-simple-read
315
358
  > The read model can be constructed based on domain events produced by the `Command Handler`.
316
359
 
317
360
  ```python
318
- from cqrs.requests.request_handler import RequestHandler
319
- from cqrs.events.event import Event
361
+ from cqrs.handlers.request import RequestHandler
362
+ from cqrs.models.event import Event
320
363
 
321
364
  class ReadMeetingQueryHandler(RequestHandler[ReadMeetingQuery, ReadMeetingQueryResult]):
322
365
 
@@ -347,8 +390,8 @@ real-time progress updates.
347
390
 
348
391
  ```python
349
392
  import typing
350
- from cqrs.requests.request_handler import StreamingRequestHandler
351
- from cqrs.events.event import Event
393
+ from cqrs.handlers.request import StreamingRequestHandler
394
+ from cqrs.models.event import Event
352
395
 
353
396
  class ProcessFilesCommandHandler(StreamingRequestHandler[ProcessFilesCommand, FileProcessedResult]):
354
397
  def __init__(self):
@@ -384,8 +427,8 @@ when a handler successfully processes the request or when all handlers have been
384
427
 
385
428
  ```python
386
429
  import typing
387
- from cqrs.requests.cor_request_handler import CORRequestHandler
388
- from cqrs.events.event import Event
430
+ from cqrs.handlers.cor import CORRequestHandler
431
+ from cqrs.models.event import Event
389
432
 
390
433
  class CreditCardPaymentHandler(CORRequestHandler[ProcessPaymentCommand, PaymentResult]):
391
434
  def __init__(self, payment_service: PaymentServiceProtocol) -> None:
@@ -441,7 +484,7 @@ the [documentation](https://github.com/vadikko2/python-cqrs/blob/master/examples
441
484
  The package includes built-in support for generating Mermaid diagrams from Chain of Responsibility handler chains.
442
485
 
443
486
  ```python
444
- from cqrs.requests.mermaid import CoRMermaid
487
+ from cqrs.mermaid.cor import CoRMermaid
445
488
 
446
489
  # Create Mermaid generator from handler chain
447
490
  handlers = [CreditCardHandler, PayPalHandler, DefaultHandler]
@@ -611,8 +654,8 @@ application.
611
654
  ```python
612
655
  import functools
613
656
 
614
- from cqrs.events import bootstrap as event_bootstrap
615
- from cqrs.requests import bootstrap as request_bootstrap
657
+ from cqrs.bootstrap import events as event_bootstrap
658
+ from cqrs.bootstrap import requests as bootstrap as request_bootstrap
616
659
 
617
660
  from app import dependencies, mapping, orm
618
661
 
@@ -668,7 +711,7 @@ import dataclasses
668
711
  import uuid
669
712
  from cqrs.saga.models import SagaContext
670
713
  from cqrs.saga.saga import Saga
671
- from cqrs.saga.step import SagaStepHandler
714
+ from cqrs.handlers.saga import SagaStepHandler
672
715
 
673
716
  @dataclasses.dataclass
674
717
  class OrderContext(SagaContext):
@@ -690,7 +733,7 @@ class OrderSaga(Saga[OrderContext]):
690
733
  context = OrderContext(order_id="123", user_id="user_1", items=["item_1"], total_amount=100.0)
691
734
  saga_id = uuid.uuid4()
692
735
 
693
- async for step_result in mediator.stream(context, saga_id=saga_id):
736
+ async for step_result in mediator.execute(context, saga_id=saga_id):
694
737
  print(f"Step completed: {step_result.step_type.__name__}")
695
738
  # If any step fails, compensation happens automatically
696
739
  ```
@@ -703,7 +746,7 @@ The saga pattern supports fallback steps that execute automatically when primary
703
746
  from cqrs.saga.fallback import Fallback
704
747
  from cqrs.adapters.circuit_breaker import AioBreakerAdapter
705
748
  from cqrs.response import Response
706
- from cqrs.saga.step import SagaStepHandler, SagaStepResult
749
+ from cqrs.handlers.saga import SagaStepHandler, SagaStepResult
707
750
 
708
751
  class ReserveInventoryResponse(Response):
709
752
  reservation_id: str
@@ -786,7 +829,7 @@ The recovery mechanism ensures eventual consistency by:
786
829
  The package includes built-in support for generating Mermaid diagrams from Saga instances.
787
830
 
788
831
  ```python
789
- from cqrs.saga.mermaid import SagaMermaid
832
+ from cqrs.mermaid.saga import SagaMermaid
790
833
 
791
834
  # Create Mermaid generator from saga class
792
835
  saga = OrderSaga()
@@ -1026,7 +1069,7 @@ This feature is especially useful when:
1026
1069
  **Configuration:**
1027
1070
 
1028
1071
  ```python
1029
- from cqrs.requests import bootstrap
1072
+ from cqrs.bootstrap import requests as bootstrap
1030
1073
 
1031
1074
  mediator = bootstrap.bootstrap_streaming(
1032
1075
  di_container=container,
@@ -1143,7 +1186,7 @@ This allows you to stream results to clients in real-time as they are processed.
1143
1186
  ```python
1144
1187
  import fastapi
1145
1188
  import json
1146
- from cqrs.requests import bootstrap
1189
+ from cqrs.bootstrap import requests as bootstrap
1147
1190
 
1148
1191
  def streaming_mediator_factory() -> cqrs.StreamingRequestMediator:
1149
1192
  return bootstrap.bootstrap_streaming(
@@ -45,6 +45,7 @@
45
45
  - [Overview](#overview)
46
46
  - [When NOT to use python-cqrs](#when-not-to-use-python-cqrs)
47
47
  - [Installation](#installation)
48
+ - [Version Support](#version-support)
48
49
  - [Quick Start](#quick-start)
49
50
  - [Request and Response Types](#request-and-response-types)
50
51
  - [Request Handlers](#request-handlers)
@@ -110,14 +111,52 @@ Do not add a Saga for a single `UPDATE` in one service, and do not use a Saga as
110
111
  pip install python-cqrs
111
112
  ```
112
113
 
113
- Optional dependencies (see [pyproject.toml](https://github.com/vadikko2/python-cqrs/blob/master/pyproject.toml) for full list):
114
+ Optional extras (5.0+ — pydantic and SQLAlchemy are no longer core dependencies):
114
115
 
115
116
  ```bash
116
- pip install python-cqrs[kafka] # Kafka broker (aiokafka)
117
- pip install python-cqrs[examples] # FastAPI, FastStream, uvicorn, etc.
118
- pip install python-cqrs[aiobreaker] # Circuit breaker for saga fallbacks
117
+ pip install "python-cqrs[pydantic]" # PydanticRequest / Pydantic* events
118
+ pip install "python-cqrs[sqlalchemy]" # Outbox / Saga SQLAlchemy storage
119
+ pip install "python-cqrs[kafka]" # Kafka broker (aiokafka)
120
+ pip install "python-cqrs[examples]" # FastAPI, FastStream, uvicorn, etc.
121
+ pip install "python-cqrs[aiobreaker]" # Circuit breaker for saga fallbacks
119
122
  ```
120
123
 
124
+ Default `Request` / `Response` / `Event` / `DomainEvent` / `NotificationEvent` aliases
125
+ are dataclass-based (`DC*`). Use `Pydantic*` types with the pydantic extra when you
126
+ need validation.
127
+
128
+ ### Import paths (5.0+)
129
+
130
+ | Area | Import |
131
+ |------|--------|
132
+ | Bootstrap | `from cqrs.bootstrap import requests, events, saga` |
133
+ | Mediators | `from cqrs.mediators import RequestMediator, SagaMediator, …` |
134
+ | Handlers | `from cqrs.handlers import RequestHandler, EventHandler, …` |
135
+ | Models | `from cqrs.models import Request, Event, …` / `cqrs.models.pydantic` |
136
+ | Maps | `from cqrs.mapping import RequestMap, EventMap, OutboxedEventMap` |
137
+ | Mermaid | `from cqrs.mermaid import CoRMermaid, SagaMermaid` |
138
+ | Outbox producer | `from cqrs.message_brokers.producer import EventProducer` |
139
+
140
+ `SagaMediator.execute(...)` replaces the former `SagaMediator.stream(...)`
141
+ (StreamingRequestMediator.stream is unchanged).
142
+
143
+ ## Version Support
144
+
145
+ | Line | Git branch | What changes |
146
+ |------|------------|--------------|
147
+ | **4.x** | `4.x` | Bug fixes and security only (~12 months after 5.0.0) |
148
+ | **5.x** | `master` | Features and breaking changes |
149
+
150
+ Stay on 4.x until you migrate:
151
+
152
+ ```bash
153
+ pip install "python-cqrs>=4,<5"
154
+ ```
155
+
156
+ Docs: [4.x](https://mkdocs.python-cqrs.dev/4.0/) and [5.x / latest](https://mkdocs.python-cqrs.dev/latest/) (version switcher after mike deploy). See [SECURITY.md](SECURITY.md) for the support window and vulnerability reporting, and [CONTRIBUTING.md](CONTRIBUTING.md#8-maintaining--fixing-4x) for how to contribute 4.x bugfixes.
157
+
158
+ A package-level EOL `DeprecationWarning` toward 5.x will be added on the **first 4.x patch after 5.0.0** — not before.
159
+
121
160
  ## Quick Start
122
161
 
123
162
  Define a command, a handler, bind them, and run via the mediator:
@@ -125,7 +164,7 @@ Define a command, a handler, bind them, and run via the mediator:
125
164
  ```python
126
165
  import di
127
166
  import cqrs
128
- from cqrs.requests import bootstrap
167
+ from cqrs.bootstrap import requests as bootstrap
129
168
 
130
169
  class CreateOrderCommand(cqrs.Request):
131
170
  order_id: str
@@ -213,8 +252,8 @@ As a result of executing the command, an event may be produced to the broker.
213
252
  > By default, the command handler does not return any result, but it is not mandatory.
214
253
 
215
254
  ```python
216
- from cqrs.requests.request_handler import RequestHandler
217
- from cqrs.events.event import Event
255
+ from cqrs.handlers.request import RequestHandler
256
+ from cqrs.models.event import Event
218
257
 
219
258
  class JoinMeetingCommandHandler(RequestHandler[JoinMeetingCommand, None]):
220
259
 
@@ -241,8 +280,8 @@ the [read model](https://radekmaziarka.pl/2018/01/08/cqrs-third-step-simple-read
241
280
  > The read model can be constructed based on domain events produced by the `Command Handler`.
242
281
 
243
282
  ```python
244
- from cqrs.requests.request_handler import RequestHandler
245
- from cqrs.events.event import Event
283
+ from cqrs.handlers.request import RequestHandler
284
+ from cqrs.models.event import Event
246
285
 
247
286
  class ReadMeetingQueryHandler(RequestHandler[ReadMeetingQuery, ReadMeetingQueryResult]):
248
287
 
@@ -273,8 +312,8 @@ real-time progress updates.
273
312
 
274
313
  ```python
275
314
  import typing
276
- from cqrs.requests.request_handler import StreamingRequestHandler
277
- from cqrs.events.event import Event
315
+ from cqrs.handlers.request import StreamingRequestHandler
316
+ from cqrs.models.event import Event
278
317
 
279
318
  class ProcessFilesCommandHandler(StreamingRequestHandler[ProcessFilesCommand, FileProcessedResult]):
280
319
  def __init__(self):
@@ -310,8 +349,8 @@ when a handler successfully processes the request or when all handlers have been
310
349
 
311
350
  ```python
312
351
  import typing
313
- from cqrs.requests.cor_request_handler import CORRequestHandler
314
- from cqrs.events.event import Event
352
+ from cqrs.handlers.cor import CORRequestHandler
353
+ from cqrs.models.event import Event
315
354
 
316
355
  class CreditCardPaymentHandler(CORRequestHandler[ProcessPaymentCommand, PaymentResult]):
317
356
  def __init__(self, payment_service: PaymentServiceProtocol) -> None:
@@ -367,7 +406,7 @@ the [documentation](https://github.com/vadikko2/python-cqrs/blob/master/examples
367
406
  The package includes built-in support for generating Mermaid diagrams from Chain of Responsibility handler chains.
368
407
 
369
408
  ```python
370
- from cqrs.requests.mermaid import CoRMermaid
409
+ from cqrs.mermaid.cor import CoRMermaid
371
410
 
372
411
  # Create Mermaid generator from handler chain
373
412
  handlers = [CreditCardHandler, PayPalHandler, DefaultHandler]
@@ -537,8 +576,8 @@ application.
537
576
  ```python
538
577
  import functools
539
578
 
540
- from cqrs.events import bootstrap as event_bootstrap
541
- from cqrs.requests import bootstrap as request_bootstrap
579
+ from cqrs.bootstrap import events as event_bootstrap
580
+ from cqrs.bootstrap import requests as bootstrap as request_bootstrap
542
581
 
543
582
  from app import dependencies, mapping, orm
544
583
 
@@ -594,7 +633,7 @@ import dataclasses
594
633
  import uuid
595
634
  from cqrs.saga.models import SagaContext
596
635
  from cqrs.saga.saga import Saga
597
- from cqrs.saga.step import SagaStepHandler
636
+ from cqrs.handlers.saga import SagaStepHandler
598
637
 
599
638
  @dataclasses.dataclass
600
639
  class OrderContext(SagaContext):
@@ -616,7 +655,7 @@ class OrderSaga(Saga[OrderContext]):
616
655
  context = OrderContext(order_id="123", user_id="user_1", items=["item_1"], total_amount=100.0)
617
656
  saga_id = uuid.uuid4()
618
657
 
619
- async for step_result in mediator.stream(context, saga_id=saga_id):
658
+ async for step_result in mediator.execute(context, saga_id=saga_id):
620
659
  print(f"Step completed: {step_result.step_type.__name__}")
621
660
  # If any step fails, compensation happens automatically
622
661
  ```
@@ -629,7 +668,7 @@ The saga pattern supports fallback steps that execute automatically when primary
629
668
  from cqrs.saga.fallback import Fallback
630
669
  from cqrs.adapters.circuit_breaker import AioBreakerAdapter
631
670
  from cqrs.response import Response
632
- from cqrs.saga.step import SagaStepHandler, SagaStepResult
671
+ from cqrs.handlers.saga import SagaStepHandler, SagaStepResult
633
672
 
634
673
  class ReserveInventoryResponse(Response):
635
674
  reservation_id: str
@@ -712,7 +751,7 @@ The recovery mechanism ensures eventual consistency by:
712
751
  The package includes built-in support for generating Mermaid diagrams from Saga instances.
713
752
 
714
753
  ```python
715
- from cqrs.saga.mermaid import SagaMermaid
754
+ from cqrs.mermaid.saga import SagaMermaid
716
755
 
717
756
  # Create Mermaid generator from saga class
718
757
  saga = OrderSaga()
@@ -952,7 +991,7 @@ This feature is especially useful when:
952
991
  **Configuration:**
953
992
 
954
993
  ```python
955
- from cqrs.requests import bootstrap
994
+ from cqrs.bootstrap import requests as bootstrap
956
995
 
957
996
  mediator = bootstrap.bootstrap_streaming(
958
997
  di_container=container,
@@ -1069,7 +1108,7 @@ This allows you to stream results to clients in real-time as they are processed.
1069
1108
  ```python
1070
1109
  import fastapi
1071
1110
  import json
1072
- from cqrs.requests import bootstrap
1111
+ from cqrs.bootstrap import requests as bootstrap
1073
1112
 
1074
1113
  def streaming_mediator_factory() -> cqrs.StreamingRequestMediator:
1075
1114
  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,12 @@ 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.0"
32
+ version = "5.0.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
38
  dev = [
39
39
  # Develope tools
40
40
  "pycln==2.5.0",
@@ -61,6 +61,9 @@ dev = [
61
61
  "aiobreaker>=0.3.0", # from aiobreaker
62
62
  "dishka>=1.0", # scoped-deps dishka adapter tests / examples
63
63
  "protobuf>=4.25.8", # protobuf codec / deserializer unit tests
64
+ # Full suite extras
65
+ "pydantic==2.*",
66
+ "sqlalchemy[asyncio]==2.0.*",
64
67
  ]
65
68
  examples = [
66
69
  "fastapi==0.109.*",
@@ -71,11 +74,12 @@ examples = [
71
74
  "protobuf>=4.25.8",
72
75
  "aiosqlite>=0.20.0",
73
76
  "dishka>=1.0",
77
+ "pydantic==2.*",
78
+ "sqlalchemy[asyncio]==2.0.*",
74
79
  ]
75
80
  kafka = ["aiokafka==0.10.0", "retry-async==0.1.*"]
76
81
  protobuf = ["protobuf>=4.25.8"]
77
82
  rabbit = ["aio-pika==9.3.0"]
78
- sqlalchemy = ["sqlalchemy[asyncio]==2.0.*"]
79
83
  dishka = ["dishka>=1.0"]
80
84
 
81
85
  [project.urls]
@@ -88,6 +92,10 @@ addopts = "--junit-xml=report.xml"
88
92
  asyncio_mode = "auto"
89
93
  junit_family = "xunit1"
90
94
  testpaths = ["tests"]
95
+ markers = [
96
+ "pydantic: tests that require the pydantic optional extra",
97
+ "sqlalchemy: tests that require the sqlalchemy optional extra",
98
+ ]
91
99
 
92
100
  [tool.setuptools.packages.find]
93
101
  where = ["src"]
@@ -0,0 +1,204 @@
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.sqlalchemy_types import (
16
+ Binary16,
17
+ DialectAwareType,
18
+ DialectTypeHandler,
19
+ JSONType,
20
+ PayloadBinary,
21
+ UUIDBinary,
22
+ )
23
+
24
+
25
+ from cqrs.compressors import Compressor, ZlibCompressor
26
+ from cqrs.container.di import DIContainer
27
+ from cqrs.container.protocol import Container, SupportsScope
28
+ from cqrs.container.scope import (
29
+ ScopeAwareContainer,
30
+ ScopeStrategy,
31
+ bind_scope,
32
+ current_container,
33
+ enter_scope,
34
+ )
35
+ from cqrs.circuit_breaker import ICircuitBreaker
36
+ from cqrs.events import EventMap
37
+ from cqrs.events.fallback import EventHandlerFallback
38
+ from cqrs.models.event import (
39
+ DCEvent,
40
+ DCDomainEvent,
41
+ DCNotificationEvent,
42
+ DomainEvent,
43
+ Event,
44
+ IDomainEvent,
45
+ IEvent,
46
+ INotificationEvent,
47
+ NotificationEvent,
48
+ )
49
+ from cqrs.events.event_emitter import EventEmitter
50
+ from cqrs.handlers.event import EventHandler
51
+ from cqrs.mediators import (
52
+ EventMediator,
53
+ RequestMediator,
54
+ SagaMediator,
55
+ StreamingRequestMediator,
56
+ )
57
+ from cqrs.mapping.outbox import OutboxedEventMap
58
+ from cqrs.outbox.repository import (
59
+ EventStatus,
60
+ OutboxedEvent,
61
+ OutboxedEventRepository,
62
+ )
63
+ from cqrs.message_brokers.producer import EventProducer
64
+ from cqrs.deserializers import DeserializeProtobufError, ProtobufDeserializer
65
+ from cqrs.serializers import (
66
+ EventCodec,
67
+ EventSerializer,
68
+ JsonEventSerializer,
69
+ ProtobufEventSerializer,
70
+ )
71
+ from cqrs.requests.fallback import RequestHandlerFallback
72
+ from cqrs.mapping.requests import RequestMap, SagaMap
73
+ from cqrs.mermaid.cor import CoRMermaid
74
+ from cqrs.models.request import DCRequest, IRequest, Request
75
+ from cqrs.handlers.request import (
76
+ RequestHandler,
77
+ StreamingRequestHandler,
78
+ )
79
+ from cqrs.models.response import DCResponse, IResponse, Response
80
+ from cqrs.mermaid.saga import SagaMermaid
81
+ from cqrs.saga.models import ContextT
82
+ from cqrs.saga.saga import Saga
83
+ from cqrs.handlers.saga import (
84
+ Resp,
85
+ SagaStepHandler,
86
+ SagaStepResult,
87
+ )
88
+
89
+
90
+ from cqrs._dataclass_utils import pydantic_extra_error
91
+
92
+ __all__ = (
93
+ "ICircuitBreaker",
94
+ "EventHandlerFallback",
95
+ "RequestHandlerFallback",
96
+ "RequestMediator",
97
+ "SagaMediator",
98
+ "StreamingRequestMediator",
99
+ "EventMediator",
100
+ "DomainEvent",
101
+ "IDomainEvent",
102
+ "DCDomainEvent",
103
+ "PydanticDomainEvent",
104
+ "NotificationEvent",
105
+ "INotificationEvent",
106
+ "DCNotificationEvent",
107
+ "PydanticNotificationEvent",
108
+ "Event",
109
+ "IEvent",
110
+ "DCEvent",
111
+ "PydanticEvent",
112
+ "EventEmitter",
113
+ "EventHandler",
114
+ "EventMap",
115
+ "OutboxedEventMap",
116
+ "EventStatus",
117
+ "OutboxedEvent",
118
+ "Request",
119
+ "IRequest",
120
+ "DCRequest",
121
+ "PydanticRequest",
122
+ "RequestHandler",
123
+ "StreamingRequestHandler",
124
+ "RequestMap",
125
+ "SagaMap",
126
+ "Response",
127
+ "IResponse",
128
+ "DCResponse",
129
+ "PydanticResponse",
130
+ "OutboxedEventRepository",
131
+ "SqlAlchemyOutboxedEventRepository",
132
+ "EventProducer",
133
+ "EventSerializer",
134
+ "EventCodec",
135
+ "JsonEventSerializer",
136
+ "ProtobufEventSerializer",
137
+ "ProtobufDeserializer",
138
+ "DeserializeProtobufError",
139
+ "Container",
140
+ "SupportsScope",
141
+ "DIContainer",
142
+ "ScopeAwareContainer",
143
+ "ScopeStrategy",
144
+ "bind_scope",
145
+ "current_container",
146
+ "enter_scope",
147
+ "Compressor",
148
+ "ZlibCompressor",
149
+ "rebind_outbox_model",
150
+ "Saga",
151
+ "SagaStepHandler",
152
+ "SagaStepResult",
153
+ "Resp",
154
+ "ContextT",
155
+ "SagaMermaid",
156
+ "CoRMermaid",
157
+ "DialectAwareType",
158
+ "DialectTypeHandler",
159
+ "UUIDBinary",
160
+ "Binary16",
161
+ "JSONType",
162
+ "PayloadBinary",
163
+ )
164
+
165
+ _LAZY_PYDANTIC = {
166
+ "PydanticEvent": ("cqrs.models.pydantic", "PydanticEvent"),
167
+ "PydanticDomainEvent": ("cqrs.models.pydantic", "PydanticDomainEvent"),
168
+ "PydanticNotificationEvent": (
169
+ "cqrs.models.pydantic",
170
+ "PydanticNotificationEvent",
171
+ ),
172
+ "PydanticRequest": ("cqrs.models.pydantic", "PydanticRequest"),
173
+ "PydanticResponse": ("cqrs.models.pydantic", "PydanticResponse"),
174
+ }
175
+
176
+ _LAZY_SQLALCHEMY = {
177
+ "SqlAlchemyOutboxedEventRepository": (
178
+ "cqrs.outbox.sqlalchemy",
179
+ "SqlAlchemyOutboxedEventRepository",
180
+ ),
181
+ "rebind_outbox_model": ("cqrs.outbox.sqlalchemy", "rebind_outbox_model"),
182
+ "DialectAwareType": ("cqrs.sqlalchemy_types", "DialectAwareType"),
183
+ "DialectTypeHandler": ("cqrs.sqlalchemy_types", "DialectTypeHandler"),
184
+ "UUIDBinary": ("cqrs.sqlalchemy_types", "UUIDBinary"),
185
+ "Binary16": ("cqrs.sqlalchemy_types", "Binary16"),
186
+ "JSONType": ("cqrs.sqlalchemy_types", "JSONType"),
187
+ "PayloadBinary": ("cqrs.sqlalchemy_types", "PayloadBinary"),
188
+ }
189
+
190
+
191
+ def __getattr__(name: str) -> typing.Any:
192
+ if name in _LAZY_PYDANTIC:
193
+ module_name, attr = _LAZY_PYDANTIC[name]
194
+ try:
195
+ module = __import__(module_name, fromlist=[attr])
196
+ except ImportError as exc:
197
+ raise pydantic_extra_error(name) from exc
198
+ return getattr(module, attr)
199
+ if name in _LAZY_SQLALCHEMY:
200
+ module_name, attr = _LAZY_SQLALCHEMY[name]
201
+ # ImportError message is raised by the sqlalchemy modules themselves.
202
+ module = __import__(module_name, fromlist=[attr])
203
+ return getattr(module, attr)
204
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")