@@ -18,7 +18,9 @@ project ([documentation](https://akhundmurad.github.io/diator/)) with several en
1818 that ` Notification ` and ` ECST ` events are sent to the broker;
19197 . FastAPI supporting;
20208 . FastStream supporting;
21- 9 . [ Protobuf] ( https://protobuf.dev/ ) events supporting.
21+ 9 . [ Protobuf] ( https://protobuf.dev/ ) events supporting;
22+ 10 . ` StreamingRequestMediator ` and ` StreamingRequestHandler ` for handling streaming requests with real-time progress updates;
23+ 11 . Parallel event processing with configurable concurrency limits.
2224
2325## Request Handlers
2426
@@ -98,6 +100,40 @@ class ReadMeetingQueryHandler(RequestHandler[ReadMeetingQuery, ReadMeetingQueryR
98100A complete example can be found in
99101the [ documentation] ( https://github.com/vadikko2/cqrs/blob/master/examples/request_handler.py )
100102
103+ ### Streaming Request Handler
104+
105+ Streaming Request Handler processes requests incrementally and yields results as they become available.
106+ This is particularly useful for processing large batches of items, file uploads, or any operation that benefits from
107+ real-time progress updates.
108+
109+ ` StreamingRequestHandler ` works with ` StreamingRequestMediator ` that streams results to clients in real-time.
110+
111+ ``` python
112+ import typing
113+ from cqrs.requests.request_handler import StreamingRequestHandler
114+ from cqrs.events.event import Event
115+
116+ class ProcessFilesCommandHandler (StreamingRequestHandler[ProcessFilesCommand, FileProcessedResult]):
117+ def __init__ (self ):
118+ self ._events: list[Event] = []
119+
120+ @ property
121+ def events (self ) -> list[Event]:
122+ return self ._events.copy()
123+
124+ async def handle (self , request : ProcessFilesCommand) -> typing.AsyncIterator[FileProcessedResult]:
125+ for file_id in request.file_ids:
126+ # Process file
127+ result = FileProcessedResult(file_id = file_id, status = " completed" , ... )
128+ # Emit events
129+ self ._events.append(FileProcessedEvent(file_id = file_id, ... ))
130+ yield result
131+ self ._events.clear()
132+ ```
133+
134+ A complete example can be found in
135+ the [ documentation] ( https://github.com/vadikko2/cqrs/blob/master/examples/streaming_handler_parallel_events.py )
136+
101137## Event Handlers
102138
103139Event handlers are designed to process ` Notification ` and ` ECST ` events that are consumed from the broker.
@@ -129,6 +165,36 @@ class UserJoinedEventHandler(cqrs.EventHandler[UserJoined]):
129165A complete example can be found in
130166the [ documentation] ( https://github.com/vadikko2/cqrs/blob/master/examples/domain_event_handler.py )
131167
168+ ### Parallel Event Processing
169+
170+ Both ` RequestMediator ` and ` StreamingRequestMediator ` support parallel processing of domain events. You can control
171+ the number of event handlers that run simultaneously using the ` max_concurrent_event_handlers ` parameter.
172+
173+ This feature is especially useful when:
174+ - Multiple event handlers need to process events independently
175+ - You want to improve performance by processing events concurrently
176+ - You need to limit resource consumption by controlling concurrency
177+
178+ ** Configuration:**
179+
180+ ``` python
181+ from cqrs.requests import bootstrap
182+
183+ mediator = bootstrap.bootstrap_streaming(
184+ di_container = container,
185+ commands_mapper = commands_mapper,
186+ domain_events_mapper = domain_events_mapper,
187+ message_broker = broker,
188+ max_concurrent_event_handlers = 3 , # Process up to 3 events in parallel
189+ concurrent_event_handle_enable = True , # Enable parallel processing
190+ )
191+ ```
192+
193+ > [ !TIP]
194+ > - Set ` max_concurrent_event_handlers ` to limit the number of simultaneously running event handlers
195+ > - Set ` concurrent_event_handle_enable=False ` to disable parallel processing and process events sequentially
196+ > - The default value for ` max_concurrent_event_handlers ` is ` 10 ` for ` StreamingRequestMediator ` and ` 1 ` for ` RequestMediator `
197+
132198## Producing Notification Events
133199
134200During the handling of a command, ` cqrs.NotificationEvent ` events may be generated and then sent to the broker.
@@ -317,6 +383,10 @@ The current version of the python-cqrs package does not support the implementati
317383Use the following example to set up dependency injection in your command, query and event handlers. This will make
318384dependency management simpler.
319385
386+ The package supports two DI container libraries:
387+
388+ ### di library
389+
320390``` python
321391import di
322392...
@@ -342,7 +412,35 @@ def setup_di() -> di.Container:
342412```
343413
344414A complete example can be found in
345- the [ documentation] ( https://github.com/vadikko2/python-cqrs/blob/master/examples/dependency_injection.py )
415+ the [ documentation] ( https://github.com/vadikko2/cqrs/blob/master/examples/dependency_injection.py )
416+
417+ ### dependency-injector library
418+
419+ The package also supports [ dependency-injector] ( https://github.com/ets-labs/python-dependency-injector ) library.
420+ You can use ` DependencyInjectorCQRSContainer ` adapter to integrate dependency-injector containers with python-cqrs.
421+
422+ ``` python
423+ from dependency_injector import containers, providers
424+ from cqrs.container.dependency_injector import DependencyInjectorCQRSContainer
425+
426+ class ApplicationContainer (containers .DeclarativeContainer ):
427+ # Define your providers
428+ service = providers.Factory(ServiceImplementation)
429+
430+ # Create CQRS container adapter
431+ cqrs_container = DependencyInjectorCQRSContainer(ApplicationContainer())
432+
433+ # Use with bootstrap
434+ mediator = bootstrap.bootstrap(
435+ di_container = cqrs_container,
436+ commands_mapper = commands_mapper,
437+ ...
438+ )
439+ ```
440+
441+ Complete examples can be found in:
442+ - [ Simple example] ( https://github.com/vadikko2/cqrs/blob/master/examples/dependency_injector_integration_simple_example.py )
443+ - [ Practical example with FastAPI] ( https://github.com/vadikko2/cqrs/blob/master/examples/dependency_injector_integration_practical_example.py )
346444
347445## Mapping
348446
@@ -485,6 +583,54 @@ async def hello_world_event_handler(
485583A complete example can be found in
486584the [ documentation] ( https://github.com/vadikko2/python-cqrs/blob/master/examples/kafka_event_consuming.py )
487585
586+ ### FastAPI SSE Streaming
587+
588+ ` StreamingRequestMediator ` is ready and designed for use with Server-Sent Events (SSE) in FastAPI applications.
589+ This allows you to stream results to clients in real-time as they are processed.
590+
591+ ** Example FastAPI endpoint with SSE:**
592+
593+ ``` python
594+ import fastapi
595+ import json
596+ from cqrs.requests import bootstrap
597+
598+ def streaming_mediator_factory () -> cqrs.StreamingRequestMediator:
599+ return bootstrap.bootstrap_streaming(
600+ di_container = container,
601+ commands_mapper = commands_mapper,
602+ domain_events_mapper = domain_events_mapper,
603+ message_broker = broker,
604+ max_concurrent_event_handlers = 3 ,
605+ concurrent_event_handle_enable = True ,
606+ )
607+
608+ @app.post (" /process-files" )
609+ async def process_files_stream (
610+ command : ProcessFilesCommand,
611+ mediator : cqrs.StreamingRequestMediator = fastapi.Depends(streaming_mediator_factory),
612+ ) -> fastapi.responses.StreamingResponse:
613+ async def generate_sse ():
614+ yield f " data: { json.dumps({' type' : ' start' , ' message' : ' Processing...' })} \n\n "
615+
616+ async for result in mediator.stream(command):
617+ sse_data = {
618+ " type" : " progress" ,
619+ " data" : result.model_dump(),
620+ }
621+ yield f " data: { json.dumps(sse_data)} \n\n "
622+
623+ yield f " data: { json.dumps({' type' : ' complete' })} \n\n "
624+
625+ return fastapi.responses.StreamingResponse(
626+ generate_sse(),
627+ media_type = " text/event-stream" ,
628+ )
629+ ```
630+
631+ A complete example can be found in
632+ the [ documentation] ( https://github.com/vadikko2/cqrs/blob/master/examples/fastapi_sse_streaming.py )
633+
488634## Protobuf messaging
489635
490636The ` python-cqrs ` package supports integration with [ protobuf] ( https://developers.google.com/protocol-buffers/ ) .\
0 commit comments