python-cqrs 0.0.8__tar.gz → 0.0.12__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.
- {python_cqrs-0.0.8/src/python_cqrs.egg-info → python_cqrs-0.0.12}/PKG-INFO +33 -33
- {python_cqrs-0.0.8 → python_cqrs-0.0.12}/README.md +27 -32
- {python_cqrs-0.0.8 → python_cqrs-0.0.12}/pyproject.toml +11 -1
- {python_cqrs-0.0.8 → python_cqrs-0.0.12/src/python_cqrs.egg-info}/PKG-INFO +33 -33
- {python_cqrs-0.0.8 → python_cqrs-0.0.12}/LICENSE +0 -0
- {python_cqrs-0.0.8 → python_cqrs-0.0.12}/setup.cfg +0 -0
- {python_cqrs-0.0.8 → python_cqrs-0.0.12}/src/cqrs/__init__.py +0 -0
- {python_cqrs-0.0.8 → python_cqrs-0.0.12}/src/cqrs/adapters/__init__.py +0 -0
- {python_cqrs-0.0.8 → python_cqrs-0.0.12}/src/cqrs/adapters/amqp.py +0 -0
- {python_cqrs-0.0.8 → python_cqrs-0.0.12}/src/cqrs/adapters/kafka.py +0 -0
- {python_cqrs-0.0.8 → python_cqrs-0.0.12}/src/cqrs/compressors/__init__.py +0 -0
- {python_cqrs-0.0.8 → python_cqrs-0.0.12}/src/cqrs/compressors/protocol.py +0 -0
- {python_cqrs-0.0.8 → python_cqrs-0.0.12}/src/cqrs/compressors/zlib.py +0 -0
- {python_cqrs-0.0.8 → python_cqrs-0.0.12}/src/cqrs/container/__init__.py +0 -0
- {python_cqrs-0.0.8 → python_cqrs-0.0.12}/src/cqrs/container/di.py +0 -0
- {python_cqrs-0.0.8 → python_cqrs-0.0.12}/src/cqrs/container/protocol.py +0 -0
- {python_cqrs-0.0.8 → python_cqrs-0.0.12}/src/cqrs/dispatcher/__init__.py +0 -0
- {python_cqrs-0.0.8 → python_cqrs-0.0.12}/src/cqrs/dispatcher/dispatcher.py +0 -0
- {python_cqrs-0.0.8 → python_cqrs-0.0.12}/src/cqrs/events/__init__.py +0 -0
- {python_cqrs-0.0.8 → python_cqrs-0.0.12}/src/cqrs/events/bootstrap.py +0 -0
- {python_cqrs-0.0.8 → python_cqrs-0.0.12}/src/cqrs/events/event.py +0 -0
- {python_cqrs-0.0.8 → python_cqrs-0.0.12}/src/cqrs/events/event_emitter.py +0 -0
- {python_cqrs-0.0.8 → python_cqrs-0.0.12}/src/cqrs/events/event_handler.py +0 -0
- {python_cqrs-0.0.8 → python_cqrs-0.0.12}/src/cqrs/events/map.py +0 -0
- {python_cqrs-0.0.8 → python_cqrs-0.0.12}/src/cqrs/mediator.py +0 -0
- {python_cqrs-0.0.8 → python_cqrs-0.0.12}/src/cqrs/message_brokers/__init__.py +0 -0
- {python_cqrs-0.0.8 → python_cqrs-0.0.12}/src/cqrs/message_brokers/amqp.py +0 -0
- {python_cqrs-0.0.8 → python_cqrs-0.0.12}/src/cqrs/message_brokers/devnull.py +0 -0
- {python_cqrs-0.0.8 → python_cqrs-0.0.12}/src/cqrs/message_brokers/kafka.py +0 -0
- {python_cqrs-0.0.8 → python_cqrs-0.0.12}/src/cqrs/message_brokers/protocol.py +0 -0
- {python_cqrs-0.0.8 → python_cqrs-0.0.12}/src/cqrs/middlewares/__init__.py +0 -0
- {python_cqrs-0.0.8 → python_cqrs-0.0.12}/src/cqrs/middlewares/base.py +0 -0
- {python_cqrs-0.0.8 → python_cqrs-0.0.12}/src/cqrs/middlewares/logging.py +0 -0
- {python_cqrs-0.0.8 → python_cqrs-0.0.12}/src/cqrs/outbox/__init__.py +0 -0
- {python_cqrs-0.0.8 → python_cqrs-0.0.12}/src/cqrs/outbox/producer.py +0 -0
- {python_cqrs-0.0.8 → python_cqrs-0.0.12}/src/cqrs/outbox/protocol.py +0 -0
- {python_cqrs-0.0.8 → python_cqrs-0.0.12}/src/cqrs/outbox/repository.py +0 -0
- {python_cqrs-0.0.8 → python_cqrs-0.0.12}/src/cqrs/outbox/sqlalchemy.py +0 -0
- {python_cqrs-0.0.8 → python_cqrs-0.0.12}/src/cqrs/requests/__init__.py +0 -0
- {python_cqrs-0.0.8 → python_cqrs-0.0.12}/src/cqrs/requests/bootstrap.py +0 -0
- {python_cqrs-0.0.8 → python_cqrs-0.0.12}/src/cqrs/requests/map.py +0 -0
- {python_cqrs-0.0.8 → python_cqrs-0.0.12}/src/cqrs/requests/request.py +0 -0
- {python_cqrs-0.0.8 → python_cqrs-0.0.12}/src/cqrs/requests/request_handler.py +0 -0
- {python_cqrs-0.0.8 → python_cqrs-0.0.12}/src/cqrs/response.py +0 -0
- {python_cqrs-0.0.8 → python_cqrs-0.0.12}/src/python_cqrs.egg-info/SOURCES.txt +0 -0
- {python_cqrs-0.0.8 → python_cqrs-0.0.12}/src/python_cqrs.egg-info/dependency_links.txt +0 -0
- {python_cqrs-0.0.8 → python_cqrs-0.0.12}/src/python_cqrs.egg-info/requires.txt +0 -0
- {python_cqrs-0.0.8 → python_cqrs-0.0.12}/src/python_cqrs.egg-info/top_level.txt +0 -0
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: python-cqrs
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.12
|
|
4
4
|
Summary: Python CQRS pattern implementation
|
|
5
5
|
Author: Nikita Kunov
|
|
6
6
|
Author-email: Dmitriy Kutlubaev <kutlubaev00@mail.ru>, Vadim Kozyrevskiy <vadikko2@mail.ru>
|
|
7
|
+
Maintainer: Nikita Kunov
|
|
8
|
+
Maintainer-email: Dmitriy Kutlubaev <kutlubaev00@mail.ru>, Vadim Kozyrevskiy <vadikko2@mail.ru>
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
7
12
|
Description-Content-Type: text/markdown
|
|
8
13
|
License-File: LICENSE
|
|
9
14
|
Requires-Dist: pydantic==2.*
|
|
@@ -28,25 +33,20 @@ Requires-Dist: aiokafka==0.10.0; extra == "kafka"
|
|
|
28
33
|
|
|
29
34
|
# CQRS
|
|
30
35
|
|
|
31
|
-
Python
|
|
32
|
-
помогут разделить задачи чтения и записи, обеспечивая лучшую масштабируемость, производительность и удобство
|
|
33
|
-
обслуживания приложения.
|
|
36
|
+
A Python library for implementing the CQRS (Command Query Responsibility Segregation) pattern in Python applications. It provides a set of abstractions and utilities to help separate read and write tasks, ensuring better scalability, performance, and maintainability of the application.
|
|
34
37
|
|
|
35
|
-
|
|
36
|
-
библиотеки [diator](https://github.com/akhundMurad/diator) ([документация](https://akhundmurad.github.io/diator/)) с
|
|
37
|
-
рядом улучшений:
|
|
38
|
+
This library is a fork of the [diator](https://github.com/akhundMurad/diator) library ([documentation](https://akhundmurad.github.io/diator/)) with several enhancements:
|
|
38
39
|
|
|
39
|
-
1.
|
|
40
|
-
2.
|
|
41
|
-
3.
|
|
42
|
-
4.
|
|
43
|
-
5.
|
|
44
|
-
6.
|
|
45
|
-
дающего гарантию отправки `Notification` и `ECST` событий в брокера.
|
|
40
|
+
1. Support for Pydantic [v2.*](https://docs.pydantic.dev/2.8/);
|
|
41
|
+
2. Kafka support using [aiokafka](https://github.com/aio-libs/aiokafka);
|
|
42
|
+
3. Added EventMediator for handling Notification and ECST events coming from the bus;
|
|
43
|
+
4. Redesigned the event and request mapping mechanism to handlers;
|
|
44
|
+
5. Added bootstrap for easy setup;
|
|
45
|
+
6. Added support for [Transaction Outbox](https://microservices.io/patterns/data/transactional-outbox.html), ensuring that Notification and ECST events are sent to the broker.
|
|
46
46
|
|
|
47
|
-
##
|
|
47
|
+
## Usage Examples
|
|
48
48
|
|
|
49
|
-
###
|
|
49
|
+
### Event Handlers
|
|
50
50
|
|
|
51
51
|
```python
|
|
52
52
|
from cqrs.events import EventHandler
|
|
@@ -59,9 +59,9 @@ class UserJoinedEventHandler(EventHandler[UserJoinedEventHandler])
|
|
|
59
59
|
await self._meetings_api.notify_room(event.meeting_id, "New user joined!")
|
|
60
60
|
```
|
|
61
61
|
|
|
62
|
-
###
|
|
62
|
+
### Request Handler
|
|
63
63
|
|
|
64
|
-
####
|
|
64
|
+
#### Command Handler
|
|
65
65
|
|
|
66
66
|
```python
|
|
67
67
|
from cqrs.requests.request_handler import RequestHandler
|
|
@@ -76,7 +76,7 @@ class JoinMeetingCommandHandler(RequestHandler[JoinMeetingCommand, None])
|
|
|
76
76
|
await self._meetings_api.join_user(request.user_id, request.meeting_id)
|
|
77
77
|
```
|
|
78
78
|
|
|
79
|
-
####
|
|
79
|
+
#### Query handler
|
|
80
80
|
|
|
81
81
|
```python
|
|
82
82
|
from cqrs.requests.request_handler import RequestHandler
|
|
@@ -93,10 +93,10 @@ class ReadMeetingQueryHandler(RequestHandler[ReadMeetingQuery, ReadMeetingQueryR
|
|
|
93
93
|
|
|
94
94
|
```
|
|
95
95
|
|
|
96
|
-
|
|
96
|
+
### Producing Notification/ECST Events
|
|
97
|
+
|
|
98
|
+
During the processing of a request/command, messages of type cqrs.NotificationEvent or cqrs.ECSTEvent can be generated, which are subsequently produced by the message broker.
|
|
97
99
|
|
|
98
|
-
Во время обработки запроса/команды можно породить сообщения с типом `cqrs.NotificationEvent` или `cqrs.ECSTEvent`,
|
|
99
|
-
которое в дальнейшем продюсируется брокером сообщений
|
|
100
100
|
|
|
101
101
|
```python
|
|
102
102
|
class CloseMeetingRoomCommandHandler(requests.RequestHandler[CloseMeetingRoomCommand, None]):
|
|
@@ -119,10 +119,10 @@ class CloseMeetingRoomCommandHandler(requests.RequestHandler[CloseMeetingRoomCom
|
|
|
119
119
|
self._events.append(event)
|
|
120
120
|
```
|
|
121
121
|
|
|
122
|
-
|
|
123
|
-
|
|
122
|
+
After processing the command/request, if there are any Notification/ECST events,
|
|
123
|
+
the EventEmitter is invoked to produce the events via the message broker.
|
|
124
124
|
|
|
125
|
-
###
|
|
125
|
+
### Mediator
|
|
126
126
|
|
|
127
127
|
```python
|
|
128
128
|
from cqrs.events import EventMap, EventEmitter
|
|
@@ -151,7 +151,7 @@ mediator = RequestMediator(
|
|
|
151
151
|
await mediator.send(join_user_command)
|
|
152
152
|
```
|
|
153
153
|
|
|
154
|
-
### Kafka
|
|
154
|
+
### Kafka broker
|
|
155
155
|
|
|
156
156
|
```python
|
|
157
157
|
from cqrs.adapters import kafka as kafka_adapter
|
|
@@ -168,8 +168,8 @@ await broker.send_message(...)
|
|
|
168
168
|
|
|
169
169
|
### Transactional Outbox
|
|
170
170
|
|
|
171
|
-
|
|
172
|
-
|
|
171
|
+
The package implements the [Transactional Outbox](https://microservices.io/patterns/data/transactional-outbox.html) pattern, which ensures that messages are produced to the broker according to the at-least-once semantics.
|
|
172
|
+
|
|
173
173
|
|
|
174
174
|
```python
|
|
175
175
|
from sqlalchemy.ext.asyncio import session as sql_session
|
|
@@ -177,7 +177,7 @@ from cqrs import events
|
|
|
177
177
|
|
|
178
178
|
def do_some_logic(meeting_room_id: int, session: sql_session.AsyncSession):
|
|
179
179
|
"""
|
|
180
|
-
|
|
180
|
+
Make changes to the database
|
|
181
181
|
"""
|
|
182
182
|
session.add(...)
|
|
183
183
|
|
|
@@ -194,7 +194,7 @@ class CloseMeetingRoomCommandHandler(requests.RequestHandler[CloseMeetingRoomCom
|
|
|
194
194
|
self.repository.add(
|
|
195
195
|
session,
|
|
196
196
|
events.ECSTEvent(
|
|
197
|
-
event_name="
|
|
197
|
+
event_name="MeetingRoomClosed",
|
|
198
198
|
payload=dict(message="foo"),
|
|
199
199
|
),
|
|
200
200
|
)
|
|
@@ -202,10 +202,10 @@ class CloseMeetingRoomCommandHandler(requests.RequestHandler[CloseMeetingRoomCom
|
|
|
202
202
|
```
|
|
203
203
|
|
|
204
204
|
|
|
205
|
-
###
|
|
205
|
+
### Producing Events from Outbox to Kafka
|
|
206
|
+
|
|
207
|
+
As an implementation of the Transactional Outbox pattern, the SqlAlchemyOutboxedEventRepository is available for use as an access repository to the Outbox storage. It can be utilized in conjunction with the KafkaMessageBroker.
|
|
206
208
|
|
|
207
|
-
В качестве имплементации Transaction Outbox доступен для использования репозиторий доступа к `Outbox` хранилищу SqlAlchemyOutboxedEventRepository.
|
|
208
|
-
Его можно использовать в связке с `KafkaMessageBroker`.
|
|
209
209
|
```python
|
|
210
210
|
import asyncio
|
|
211
211
|
import cqrs
|
|
@@ -1,24 +1,19 @@
|
|
|
1
1
|
# CQRS
|
|
2
2
|
|
|
3
|
-
Python
|
|
4
|
-
помогут разделить задачи чтения и записи, обеспечивая лучшую масштабируемость, производительность и удобство
|
|
5
|
-
обслуживания приложения.
|
|
3
|
+
A Python library for implementing the CQRS (Command Query Responsibility Segregation) pattern in Python applications. It provides a set of abstractions and utilities to help separate read and write tasks, ensuring better scalability, performance, and maintainability of the application.
|
|
6
4
|
|
|
7
|
-
|
|
8
|
-
библиотеки [diator](https://github.com/akhundMurad/diator) ([документация](https://akhundmurad.github.io/diator/)) с
|
|
9
|
-
рядом улучшений:
|
|
5
|
+
This library is a fork of the [diator](https://github.com/akhundMurad/diator) library ([documentation](https://akhundmurad.github.io/diator/)) with several enhancements:
|
|
10
6
|
|
|
11
|
-
1.
|
|
12
|
-
2.
|
|
13
|
-
3.
|
|
14
|
-
4.
|
|
15
|
-
5.
|
|
16
|
-
6.
|
|
17
|
-
дающего гарантию отправки `Notification` и `ECST` событий в брокера.
|
|
7
|
+
1. Support for Pydantic [v2.*](https://docs.pydantic.dev/2.8/);
|
|
8
|
+
2. Kafka support using [aiokafka](https://github.com/aio-libs/aiokafka);
|
|
9
|
+
3. Added EventMediator for handling Notification and ECST events coming from the bus;
|
|
10
|
+
4. Redesigned the event and request mapping mechanism to handlers;
|
|
11
|
+
5. Added bootstrap for easy setup;
|
|
12
|
+
6. Added support for [Transaction Outbox](https://microservices.io/patterns/data/transactional-outbox.html), ensuring that Notification and ECST events are sent to the broker.
|
|
18
13
|
|
|
19
|
-
##
|
|
14
|
+
## Usage Examples
|
|
20
15
|
|
|
21
|
-
###
|
|
16
|
+
### Event Handlers
|
|
22
17
|
|
|
23
18
|
```python
|
|
24
19
|
from cqrs.events import EventHandler
|
|
@@ -31,9 +26,9 @@ class UserJoinedEventHandler(EventHandler[UserJoinedEventHandler])
|
|
|
31
26
|
await self._meetings_api.notify_room(event.meeting_id, "New user joined!")
|
|
32
27
|
```
|
|
33
28
|
|
|
34
|
-
###
|
|
29
|
+
### Request Handler
|
|
35
30
|
|
|
36
|
-
####
|
|
31
|
+
#### Command Handler
|
|
37
32
|
|
|
38
33
|
```python
|
|
39
34
|
from cqrs.requests.request_handler import RequestHandler
|
|
@@ -48,7 +43,7 @@ class JoinMeetingCommandHandler(RequestHandler[JoinMeetingCommand, None])
|
|
|
48
43
|
await self._meetings_api.join_user(request.user_id, request.meeting_id)
|
|
49
44
|
```
|
|
50
45
|
|
|
51
|
-
####
|
|
46
|
+
#### Query handler
|
|
52
47
|
|
|
53
48
|
```python
|
|
54
49
|
from cqrs.requests.request_handler import RequestHandler
|
|
@@ -65,10 +60,10 @@ class ReadMeetingQueryHandler(RequestHandler[ReadMeetingQuery, ReadMeetingQueryR
|
|
|
65
60
|
|
|
66
61
|
```
|
|
67
62
|
|
|
68
|
-
|
|
63
|
+
### Producing Notification/ECST Events
|
|
64
|
+
|
|
65
|
+
During the processing of a request/command, messages of type cqrs.NotificationEvent or cqrs.ECSTEvent can be generated, which are subsequently produced by the message broker.
|
|
69
66
|
|
|
70
|
-
Во время обработки запроса/команды можно породить сообщения с типом `cqrs.NotificationEvent` или `cqrs.ECSTEvent`,
|
|
71
|
-
которое в дальнейшем продюсируется брокером сообщений
|
|
72
67
|
|
|
73
68
|
```python
|
|
74
69
|
class CloseMeetingRoomCommandHandler(requests.RequestHandler[CloseMeetingRoomCommand, None]):
|
|
@@ -91,10 +86,10 @@ class CloseMeetingRoomCommandHandler(requests.RequestHandler[CloseMeetingRoomCom
|
|
|
91
86
|
self._events.append(event)
|
|
92
87
|
```
|
|
93
88
|
|
|
94
|
-
|
|
95
|
-
|
|
89
|
+
After processing the command/request, if there are any Notification/ECST events,
|
|
90
|
+
the EventEmitter is invoked to produce the events via the message broker.
|
|
96
91
|
|
|
97
|
-
###
|
|
92
|
+
### Mediator
|
|
98
93
|
|
|
99
94
|
```python
|
|
100
95
|
from cqrs.events import EventMap, EventEmitter
|
|
@@ -123,7 +118,7 @@ mediator = RequestMediator(
|
|
|
123
118
|
await mediator.send(join_user_command)
|
|
124
119
|
```
|
|
125
120
|
|
|
126
|
-
### Kafka
|
|
121
|
+
### Kafka broker
|
|
127
122
|
|
|
128
123
|
```python
|
|
129
124
|
from cqrs.adapters import kafka as kafka_adapter
|
|
@@ -140,8 +135,8 @@ await broker.send_message(...)
|
|
|
140
135
|
|
|
141
136
|
### Transactional Outbox
|
|
142
137
|
|
|
143
|
-
|
|
144
|
-
|
|
138
|
+
The package implements the [Transactional Outbox](https://microservices.io/patterns/data/transactional-outbox.html) pattern, which ensures that messages are produced to the broker according to the at-least-once semantics.
|
|
139
|
+
|
|
145
140
|
|
|
146
141
|
```python
|
|
147
142
|
from sqlalchemy.ext.asyncio import session as sql_session
|
|
@@ -149,7 +144,7 @@ from cqrs import events
|
|
|
149
144
|
|
|
150
145
|
def do_some_logic(meeting_room_id: int, session: sql_session.AsyncSession):
|
|
151
146
|
"""
|
|
152
|
-
|
|
147
|
+
Make changes to the database
|
|
153
148
|
"""
|
|
154
149
|
session.add(...)
|
|
155
150
|
|
|
@@ -166,7 +161,7 @@ class CloseMeetingRoomCommandHandler(requests.RequestHandler[CloseMeetingRoomCom
|
|
|
166
161
|
self.repository.add(
|
|
167
162
|
session,
|
|
168
163
|
events.ECSTEvent(
|
|
169
|
-
event_name="
|
|
164
|
+
event_name="MeetingRoomClosed",
|
|
170
165
|
payload=dict(message="foo"),
|
|
171
166
|
),
|
|
172
167
|
)
|
|
@@ -174,10 +169,10 @@ class CloseMeetingRoomCommandHandler(requests.RequestHandler[CloseMeetingRoomCom
|
|
|
174
169
|
```
|
|
175
170
|
|
|
176
171
|
|
|
177
|
-
###
|
|
172
|
+
### Producing Events from Outbox to Kafka
|
|
173
|
+
|
|
174
|
+
As an implementation of the Transactional Outbox pattern, the SqlAlchemyOutboxedEventRepository is available for use as an access repository to the Outbox storage. It can be utilized in conjunction with the KafkaMessageBroker.
|
|
178
175
|
|
|
179
|
-
В качестве имплементации Transaction Outbox доступен для использования репозиторий доступа к `Outbox` хранилищу SqlAlchemyOutboxedEventRepository.
|
|
180
|
-
Его можно использовать в связке с `KafkaMessageBroker`.
|
|
181
176
|
```python
|
|
182
177
|
import asyncio
|
|
183
178
|
import cqrs
|
|
@@ -8,6 +8,11 @@ authors = [
|
|
|
8
8
|
{name = "Nikita Kunov"},
|
|
9
9
|
{name = "Vadim Kozyrevskiy", email = "vadikko2@mail.ru"}
|
|
10
10
|
]
|
|
11
|
+
classifiers = [
|
|
12
|
+
"Programming Language :: Python :: 3",
|
|
13
|
+
"License :: OSI Approved :: MIT License",
|
|
14
|
+
"Operating System :: OS Independent"
|
|
15
|
+
]
|
|
11
16
|
dependencies = [
|
|
12
17
|
"pydantic==2.*",
|
|
13
18
|
"orjson==3.9.15",
|
|
@@ -17,9 +22,14 @@ dependencies = [
|
|
|
17
22
|
"retry-async==0.1.4"
|
|
18
23
|
]
|
|
19
24
|
description = "Python CQRS pattern implementation"
|
|
25
|
+
maintainers = [
|
|
26
|
+
{name = "Dmitriy Kutlubaev", email = "kutlubaev00@mail.ru"},
|
|
27
|
+
{name = "Nikita Kunov"},
|
|
28
|
+
{name = "Vadim Kozyrevskiy", email = "vadikko2@mail.ru"}
|
|
29
|
+
]
|
|
20
30
|
name = "python-cqrs"
|
|
21
31
|
readme = "README.md"
|
|
22
|
-
version = "0.0.
|
|
32
|
+
version = "0.0.12"
|
|
23
33
|
|
|
24
34
|
[project.optional-dependencies]
|
|
25
35
|
dev = [
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: python-cqrs
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.12
|
|
4
4
|
Summary: Python CQRS pattern implementation
|
|
5
5
|
Author: Nikita Kunov
|
|
6
6
|
Author-email: Dmitriy Kutlubaev <kutlubaev00@mail.ru>, Vadim Kozyrevskiy <vadikko2@mail.ru>
|
|
7
|
+
Maintainer: Nikita Kunov
|
|
8
|
+
Maintainer-email: Dmitriy Kutlubaev <kutlubaev00@mail.ru>, Vadim Kozyrevskiy <vadikko2@mail.ru>
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
7
12
|
Description-Content-Type: text/markdown
|
|
8
13
|
License-File: LICENSE
|
|
9
14
|
Requires-Dist: pydantic==2.*
|
|
@@ -28,25 +33,20 @@ Requires-Dist: aiokafka==0.10.0; extra == "kafka"
|
|
|
28
33
|
|
|
29
34
|
# CQRS
|
|
30
35
|
|
|
31
|
-
Python
|
|
32
|
-
помогут разделить задачи чтения и записи, обеспечивая лучшую масштабируемость, производительность и удобство
|
|
33
|
-
обслуживания приложения.
|
|
36
|
+
A Python library for implementing the CQRS (Command Query Responsibility Segregation) pattern in Python applications. It provides a set of abstractions and utilities to help separate read and write tasks, ensuring better scalability, performance, and maintainability of the application.
|
|
34
37
|
|
|
35
|
-
|
|
36
|
-
библиотеки [diator](https://github.com/akhundMurad/diator) ([документация](https://akhundmurad.github.io/diator/)) с
|
|
37
|
-
рядом улучшений:
|
|
38
|
+
This library is a fork of the [diator](https://github.com/akhundMurad/diator) library ([documentation](https://akhundmurad.github.io/diator/)) with several enhancements:
|
|
38
39
|
|
|
39
|
-
1.
|
|
40
|
-
2.
|
|
41
|
-
3.
|
|
42
|
-
4.
|
|
43
|
-
5.
|
|
44
|
-
6.
|
|
45
|
-
дающего гарантию отправки `Notification` и `ECST` событий в брокера.
|
|
40
|
+
1. Support for Pydantic [v2.*](https://docs.pydantic.dev/2.8/);
|
|
41
|
+
2. Kafka support using [aiokafka](https://github.com/aio-libs/aiokafka);
|
|
42
|
+
3. Added EventMediator for handling Notification and ECST events coming from the bus;
|
|
43
|
+
4. Redesigned the event and request mapping mechanism to handlers;
|
|
44
|
+
5. Added bootstrap for easy setup;
|
|
45
|
+
6. Added support for [Transaction Outbox](https://microservices.io/patterns/data/transactional-outbox.html), ensuring that Notification and ECST events are sent to the broker.
|
|
46
46
|
|
|
47
|
-
##
|
|
47
|
+
## Usage Examples
|
|
48
48
|
|
|
49
|
-
###
|
|
49
|
+
### Event Handlers
|
|
50
50
|
|
|
51
51
|
```python
|
|
52
52
|
from cqrs.events import EventHandler
|
|
@@ -59,9 +59,9 @@ class UserJoinedEventHandler(EventHandler[UserJoinedEventHandler])
|
|
|
59
59
|
await self._meetings_api.notify_room(event.meeting_id, "New user joined!")
|
|
60
60
|
```
|
|
61
61
|
|
|
62
|
-
###
|
|
62
|
+
### Request Handler
|
|
63
63
|
|
|
64
|
-
####
|
|
64
|
+
#### Command Handler
|
|
65
65
|
|
|
66
66
|
```python
|
|
67
67
|
from cqrs.requests.request_handler import RequestHandler
|
|
@@ -76,7 +76,7 @@ class JoinMeetingCommandHandler(RequestHandler[JoinMeetingCommand, None])
|
|
|
76
76
|
await self._meetings_api.join_user(request.user_id, request.meeting_id)
|
|
77
77
|
```
|
|
78
78
|
|
|
79
|
-
####
|
|
79
|
+
#### Query handler
|
|
80
80
|
|
|
81
81
|
```python
|
|
82
82
|
from cqrs.requests.request_handler import RequestHandler
|
|
@@ -93,10 +93,10 @@ class ReadMeetingQueryHandler(RequestHandler[ReadMeetingQuery, ReadMeetingQueryR
|
|
|
93
93
|
|
|
94
94
|
```
|
|
95
95
|
|
|
96
|
-
|
|
96
|
+
### Producing Notification/ECST Events
|
|
97
|
+
|
|
98
|
+
During the processing of a request/command, messages of type cqrs.NotificationEvent or cqrs.ECSTEvent can be generated, which are subsequently produced by the message broker.
|
|
97
99
|
|
|
98
|
-
Во время обработки запроса/команды можно породить сообщения с типом `cqrs.NotificationEvent` или `cqrs.ECSTEvent`,
|
|
99
|
-
которое в дальнейшем продюсируется брокером сообщений
|
|
100
100
|
|
|
101
101
|
```python
|
|
102
102
|
class CloseMeetingRoomCommandHandler(requests.RequestHandler[CloseMeetingRoomCommand, None]):
|
|
@@ -119,10 +119,10 @@ class CloseMeetingRoomCommandHandler(requests.RequestHandler[CloseMeetingRoomCom
|
|
|
119
119
|
self._events.append(event)
|
|
120
120
|
```
|
|
121
121
|
|
|
122
|
-
|
|
123
|
-
|
|
122
|
+
After processing the command/request, if there are any Notification/ECST events,
|
|
123
|
+
the EventEmitter is invoked to produce the events via the message broker.
|
|
124
124
|
|
|
125
|
-
###
|
|
125
|
+
### Mediator
|
|
126
126
|
|
|
127
127
|
```python
|
|
128
128
|
from cqrs.events import EventMap, EventEmitter
|
|
@@ -151,7 +151,7 @@ mediator = RequestMediator(
|
|
|
151
151
|
await mediator.send(join_user_command)
|
|
152
152
|
```
|
|
153
153
|
|
|
154
|
-
### Kafka
|
|
154
|
+
### Kafka broker
|
|
155
155
|
|
|
156
156
|
```python
|
|
157
157
|
from cqrs.adapters import kafka as kafka_adapter
|
|
@@ -168,8 +168,8 @@ await broker.send_message(...)
|
|
|
168
168
|
|
|
169
169
|
### Transactional Outbox
|
|
170
170
|
|
|
171
|
-
|
|
172
|
-
|
|
171
|
+
The package implements the [Transactional Outbox](https://microservices.io/patterns/data/transactional-outbox.html) pattern, which ensures that messages are produced to the broker according to the at-least-once semantics.
|
|
172
|
+
|
|
173
173
|
|
|
174
174
|
```python
|
|
175
175
|
from sqlalchemy.ext.asyncio import session as sql_session
|
|
@@ -177,7 +177,7 @@ from cqrs import events
|
|
|
177
177
|
|
|
178
178
|
def do_some_logic(meeting_room_id: int, session: sql_session.AsyncSession):
|
|
179
179
|
"""
|
|
180
|
-
|
|
180
|
+
Make changes to the database
|
|
181
181
|
"""
|
|
182
182
|
session.add(...)
|
|
183
183
|
|
|
@@ -194,7 +194,7 @@ class CloseMeetingRoomCommandHandler(requests.RequestHandler[CloseMeetingRoomCom
|
|
|
194
194
|
self.repository.add(
|
|
195
195
|
session,
|
|
196
196
|
events.ECSTEvent(
|
|
197
|
-
event_name="
|
|
197
|
+
event_name="MeetingRoomClosed",
|
|
198
198
|
payload=dict(message="foo"),
|
|
199
199
|
),
|
|
200
200
|
)
|
|
@@ -202,10 +202,10 @@ class CloseMeetingRoomCommandHandler(requests.RequestHandler[CloseMeetingRoomCom
|
|
|
202
202
|
```
|
|
203
203
|
|
|
204
204
|
|
|
205
|
-
###
|
|
205
|
+
### Producing Events from Outbox to Kafka
|
|
206
|
+
|
|
207
|
+
As an implementation of the Transactional Outbox pattern, the SqlAlchemyOutboxedEventRepository is available for use as an access repository to the Outbox storage. It can be utilized in conjunction with the KafkaMessageBroker.
|
|
206
208
|
|
|
207
|
-
В качестве имплементации Transaction Outbox доступен для использования репозиторий доступа к `Outbox` хранилищу SqlAlchemyOutboxedEventRepository.
|
|
208
|
-
Его можно использовать в связке с `KafkaMessageBroker`.
|
|
209
209
|
```python
|
|
210
210
|
import asyncio
|
|
211
211
|
import cqrs
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|