python-cq 0.9.1__tar.gz → 0.11.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 (29) hide show
  1. {python_cq-0.9.1 → python_cq-0.11.0}/PKG-INFO +1 -1
  2. {python_cq-0.9.1 → python_cq-0.11.0}/cq/__init__.py +9 -3
  3. {python_cq-0.9.1 → python_cq-0.11.0}/cq/_core/dispatcher/base.py +6 -1
  4. {python_cq-0.9.1 → python_cq-0.11.0}/cq/_core/dispatcher/bus.py +7 -2
  5. python_cq-0.11.0/cq/_core/dispatcher/lazy.py +28 -0
  6. python_cq-0.11.0/cq/_core/dispatcher/pipe.py +196 -0
  7. python_cq-0.11.0/cq/_core/pipetools.py +31 -0
  8. python_cq-0.11.0/cq/ext/fastapi.py +73 -0
  9. {python_cq-0.9.1 → python_cq-0.11.0}/pyproject.toml +1 -1
  10. python_cq-0.9.1/cq/_core/defer.py +0 -10
  11. python_cq-0.9.1/cq/_core/dispatcher/pipe.py +0 -82
  12. python_cq-0.9.1/cq/ext/fastapi.py +0 -44
  13. python_cq-0.9.1/cq/ext/fastapi.pyi +0 -5
  14. {python_cq-0.9.1 → python_cq-0.11.0}/.gitignore +0 -0
  15. {python_cq-0.9.1 → python_cq-0.11.0}/LICENSE +0 -0
  16. {python_cq-0.9.1 → python_cq-0.11.0}/README.md +0 -0
  17. {python_cq-0.9.1 → python_cq-0.11.0}/cq/_core/__init__.py +0 -0
  18. {python_cq-0.9.1 → python_cq-0.11.0}/cq/_core/dispatcher/__init__.py +0 -0
  19. {python_cq-0.9.1 → python_cq-0.11.0}/cq/_core/handler.py +0 -0
  20. {python_cq-0.9.1 → python_cq-0.11.0}/cq/_core/message.py +0 -0
  21. {python_cq-0.9.1 → python_cq-0.11.0}/cq/_core/middleware.py +0 -0
  22. {python_cq-0.9.1 → python_cq-0.11.0}/cq/_core/related_events.py +0 -0
  23. {python_cq-0.9.1 → python_cq-0.11.0}/cq/_core/scope.py +0 -0
  24. {python_cq-0.9.1 → python_cq-0.11.0}/cq/exceptions.py +0 -0
  25. {python_cq-0.9.1 → python_cq-0.11.0}/cq/ext/__init__.py +0 -0
  26. {python_cq-0.9.1 → python_cq-0.11.0}/cq/middlewares/__init__.py +0 -0
  27. {python_cq-0.9.1 → python_cq-0.11.0}/cq/middlewares/retry.py +0 -0
  28. {python_cq-0.9.1 → python_cq-0.11.0}/cq/middlewares/scope.py +0 -0
  29. {python_cq-0.9.1 → python_cq-0.11.0}/cq/py.typed +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-cq
3
- Version: 0.9.1
3
+ Version: 0.11.0
4
4
  Summary: Lightweight CQRS library for async Python projects.
5
5
  Project-URL: Repository, https://github.com/100nm/python-cq
6
6
  Author: remimd
@@ -1,6 +1,7 @@
1
- from ._core.defer import DeferredBus
1
+ from ._core.dispatcher.base import DeferredDispatcher, Dispatcher
2
2
  from ._core.dispatcher.bus import Bus
3
- from ._core.dispatcher.pipe import Pipe
3
+ from ._core.dispatcher.lazy import LazyDispatcher
4
+ from ._core.dispatcher.pipe import ContextPipeline, Pipe
4
5
  from ._core.message import (
5
6
  AnyCommandBus,
6
7
  Command,
@@ -17,6 +18,7 @@ from ._core.message import (
17
18
  query_handler,
18
19
  )
19
20
  from ._core.middleware import Middleware, MiddlewareResult
21
+ from ._core.pipetools import ContextCommandPipeline
20
22
  from ._core.related_events import RelatedEvents
21
23
  from ._core.scope import CQScope
22
24
 
@@ -26,9 +28,13 @@ __all__ = (
26
28
  "CQScope",
27
29
  "Command",
28
30
  "CommandBus",
29
- "DeferredBus",
31
+ "ContextCommandPipeline",
32
+ "ContextPipeline",
33
+ "DeferredDispatcher",
34
+ "Dispatcher",
30
35
  "Event",
31
36
  "EventBus",
37
+ "LazyDispatcher",
32
38
  "Middleware",
33
39
  "MiddlewareResult",
34
40
  "Pipe",
@@ -13,8 +13,13 @@ class Dispatcher[I, O](Protocol):
13
13
  async def dispatch(self, input_value: I, /) -> O:
14
14
  raise NotImplementedError
15
15
 
16
+
17
+ @runtime_checkable
18
+ class DeferredDispatcher[I](Protocol):
19
+ __slots__ = ()
20
+
16
21
  @abstractmethod
17
- def add_middlewares(self, *middlewares: Middleware[[I], O]) -> Self:
22
+ async def defer(self, input_value: I, /) -> None:
18
23
  raise NotImplementedError
19
24
 
20
25
 
@@ -12,6 +12,7 @@ from cq._core.handler import (
12
12
  MultipleHandlerManager,
13
13
  SingleHandlerManager,
14
14
  )
15
+ from cq._core.middleware import Middleware
15
16
 
16
17
  type Listener[T] = Callable[[T], Awaitable[Any]]
17
18
 
@@ -21,11 +22,15 @@ class Bus[I, O](Dispatcher[I, O], Protocol):
21
22
  __slots__ = ()
22
23
 
23
24
  @abstractmethod
24
- def subscribe(self, input_type: type[I], factory: HandlerFactory[[I], O]) -> Self:
25
+ def add_listeners(self, *listeners: Listener[I]) -> Self:
25
26
  raise NotImplementedError
26
27
 
27
28
  @abstractmethod
28
- def add_listeners(self, *listeners: Listener[I]) -> Self:
29
+ def add_middlewares(self, *middlewares: Middleware[[I], O]) -> Self:
30
+ raise NotImplementedError
31
+
32
+ @abstractmethod
33
+ def subscribe(self, input_type: type[I], factory: HandlerFactory[[I], O]) -> Self:
29
34
  raise NotImplementedError
30
35
 
31
36
 
@@ -0,0 +1,28 @@
1
+ from collections.abc import Awaitable
2
+ from types import GenericAlias
3
+ from typing import TypeAliasType
4
+
5
+ import injection
6
+
7
+ from cq._core.dispatcher.base import Dispatcher
8
+
9
+
10
+ class LazyDispatcher[I, O](Dispatcher[I, O]):
11
+ __slots__ = ("__value",)
12
+
13
+ __value: Awaitable[Dispatcher[I, O]]
14
+
15
+ def __init__(
16
+ self,
17
+ dispatcher_type: type[Dispatcher[I, O]] | TypeAliasType | GenericAlias,
18
+ /,
19
+ *,
20
+ injection_module: injection.Module | None = None,
21
+ threadsafe: bool | None = None,
22
+ ) -> None:
23
+ module = injection_module or injection.mod()
24
+ self.__value = module.aget_lazy_instance(dispatcher_type, threadsafe=threadsafe)
25
+
26
+ async def dispatch(self, input_value: I, /) -> O:
27
+ dispatcher = await self.__value
28
+ return await dispatcher.dispatch(input_value)
@@ -0,0 +1,196 @@
1
+ from collections import deque
2
+ from collections.abc import Awaitable, Callable
3
+ from dataclasses import dataclass, field
4
+ from typing import Any, Protocol, Self, overload
5
+
6
+ from cq._core.dispatcher.base import BaseDispatcher, Dispatcher
7
+ from cq._core.middleware import Middleware
8
+
9
+ type PipeConverter[I, O] = Callable[[O], Awaitable[I]]
10
+
11
+
12
+ class PipeConverterMethod[I, O](Protocol):
13
+ def __get__(
14
+ self,
15
+ instance: object,
16
+ owner: type | None = ...,
17
+ ) -> PipeConverter[I, O]: ...
18
+
19
+
20
+ @dataclass(repr=False, eq=False, frozen=True, slots=True)
21
+ class PipeStep[I, O]:
22
+ converter: PipeConverter[I, O]
23
+ dispatcher: Dispatcher[I, Any] | None = field(default=None)
24
+
25
+
26
+ class Pipe[I, O](BaseDispatcher[I, O]):
27
+ __slots__ = ("__dispatcher", "__steps")
28
+
29
+ __dispatcher: Dispatcher[Any, Any]
30
+ __steps: list[PipeStep[Any, Any]]
31
+
32
+ def __init__(self, dispatcher: Dispatcher[Any, Any]) -> None:
33
+ super().__init__()
34
+ self.__dispatcher = dispatcher
35
+ self.__steps = []
36
+
37
+ @overload
38
+ def step[T](
39
+ self,
40
+ wrapped: PipeConverter[T, Any],
41
+ /,
42
+ *,
43
+ dispatcher: Dispatcher[T, Any] | None = ...,
44
+ ) -> PipeConverter[T, Any]: ...
45
+
46
+ @overload
47
+ def step[T](
48
+ self,
49
+ wrapped: None = ...,
50
+ /,
51
+ *,
52
+ dispatcher: Dispatcher[T, Any] | None = ...,
53
+ ) -> Callable[[PipeConverter[T, Any]], PipeConverter[T, Any]]: ...
54
+
55
+ def step[T](
56
+ self,
57
+ wrapped: PipeConverter[T, Any] | None = None,
58
+ /,
59
+ *,
60
+ dispatcher: Dispatcher[T, Any] | None = None,
61
+ ) -> Any:
62
+ def decorator(wp: PipeConverter[T, Any]) -> PipeConverter[T, Any]:
63
+ step = PipeStep(wp, dispatcher)
64
+ self.__steps.append(step)
65
+ return wp
66
+
67
+ return decorator(wrapped) if wrapped else decorator
68
+
69
+ def add_static_step[T](
70
+ self,
71
+ input_value: T,
72
+ *,
73
+ dispatcher: Dispatcher[T, Any] | None = None,
74
+ ) -> Self:
75
+ @self.step(dispatcher=dispatcher)
76
+ async def converter(_: Any) -> T:
77
+ return input_value
78
+
79
+ return self
80
+
81
+ async def dispatch(self, input_value: I, /) -> O:
82
+ return await self._invoke_with_middlewares(self.__execute, input_value)
83
+
84
+ async def __execute(self, input_value: I) -> O:
85
+ dispatcher = self.__dispatcher
86
+
87
+ for step in self.__steps:
88
+ output_value = await dispatcher.dispatch(input_value)
89
+ input_value = await step.converter(output_value)
90
+
91
+ if input_value is None:
92
+ return NotImplemented
93
+
94
+ dispatcher = step.dispatcher or self.__dispatcher
95
+
96
+ return await dispatcher.dispatch(input_value)
97
+
98
+
99
+ @dataclass(repr=False, eq=False, frozen=True, slots=True)
100
+ class ContextPipelineStep[I, O]:
101
+ converter: PipeConverterMethod[I, O]
102
+ dispatcher: Dispatcher[I, Any] | None = field(default=None)
103
+
104
+
105
+ class ContextPipeline[I]:
106
+ __slots__ = ("__dispatcher", "__middlewares", "__steps")
107
+
108
+ __dispatcher: Dispatcher[Any, Any]
109
+ __middlewares: deque[Middleware[Any, Any]]
110
+ __steps: list[ContextPipelineStep[Any, Any]]
111
+
112
+ def __init__(self, dispatcher: Dispatcher[Any, Any]) -> None:
113
+ self.__dispatcher = dispatcher
114
+ self.__middlewares = deque()
115
+ self.__steps = []
116
+
117
+ @overload
118
+ def __get__[O](
119
+ self,
120
+ instance: O,
121
+ owner: type[O] | None = ...,
122
+ ) -> Dispatcher[I, O]: ...
123
+
124
+ @overload
125
+ def __get__(self, instance: None = ..., owner: type | None = ...) -> Self: ...
126
+
127
+ def __get__[O](
128
+ self,
129
+ instance: O | None = None,
130
+ owner: type[O] | None = None,
131
+ ) -> Self | Dispatcher[I, O]:
132
+ if instance is None:
133
+ return self
134
+
135
+ pipeline = self.__new_pipeline(instance, owner)
136
+ return BoundContextPipeline(instance, pipeline)
137
+
138
+ def add_middlewares(self, *middlewares: Middleware[[I], Any]) -> Self:
139
+ self.__middlewares.extendleft(reversed(middlewares))
140
+ return self
141
+
142
+ @overload
143
+ def step[T](
144
+ self,
145
+ wrapped: PipeConverterMethod[T, Any],
146
+ /,
147
+ *,
148
+ dispatcher: Dispatcher[T, Any] | None = ...,
149
+ ) -> PipeConverterMethod[T, Any]: ...
150
+
151
+ @overload
152
+ def step[T](
153
+ self,
154
+ wrapped: None = ...,
155
+ /,
156
+ *,
157
+ dispatcher: Dispatcher[T, Any] | None = ...,
158
+ ) -> Callable[[PipeConverterMethod[T, Any]], PipeConverterMethod[T, Any]]: ...
159
+
160
+ def step[T](
161
+ self,
162
+ wrapped: PipeConverterMethod[T, Any] | None = None,
163
+ /,
164
+ *,
165
+ dispatcher: Dispatcher[T, Any] | None = None,
166
+ ) -> Any:
167
+ def decorator(wp: PipeConverterMethod[T, Any]) -> PipeConverterMethod[T, Any]:
168
+ step = ContextPipelineStep(wp, dispatcher)
169
+ self.__steps.append(step)
170
+ return wp
171
+
172
+ return decorator(wrapped) if wrapped else decorator
173
+
174
+ def __new_pipeline[T](
175
+ self,
176
+ context: T,
177
+ context_type: type[T] | None,
178
+ ) -> Pipe[I, Any]:
179
+ pipeline: Pipe[I, Any] = Pipe(self.__dispatcher)
180
+ pipeline.add_middlewares(*self.__middlewares)
181
+
182
+ for step in self.__steps:
183
+ converter = step.converter.__get__(context, context_type)
184
+ pipeline.step(converter, dispatcher=step.dispatcher)
185
+
186
+ return pipeline
187
+
188
+
189
+ @dataclass(repr=False, eq=False, frozen=True, slots=True)
190
+ class BoundContextPipeline[I, O](Dispatcher[I, O]):
191
+ context: O
192
+ pipeline: Pipe[I, Any]
193
+
194
+ async def dispatch(self, input_value: I, /) -> O:
195
+ await self.pipeline.dispatch(input_value)
196
+ return self.context
@@ -0,0 +1,31 @@
1
+ import injection
2
+
3
+ from cq._core.dispatcher.lazy import LazyDispatcher
4
+ from cq._core.dispatcher.pipe import ContextPipeline
5
+ from cq._core.message import AnyCommandBus, Command
6
+ from cq._core.scope import CQScope
7
+ from cq.middlewares.scope import InjectionScopeMiddleware
8
+
9
+
10
+ class ContextCommandPipeline[I: Command](ContextPipeline[I]):
11
+ __slots__ = ()
12
+
13
+ def __init__(
14
+ self,
15
+ /,
16
+ *,
17
+ injection_module: injection.Module | None = None,
18
+ threadsafe: bool | None = None,
19
+ ) -> None:
20
+ dispatcher = LazyDispatcher(
21
+ AnyCommandBus,
22
+ injection_module=injection_module,
23
+ threadsafe=threadsafe,
24
+ )
25
+ super().__init__(dispatcher)
26
+ transaction_scope_middleware = InjectionScopeMiddleware(
27
+ CQScope.TRANSACTION,
28
+ exist_ok=True,
29
+ threadsafe=threadsafe,
30
+ )
31
+ self.add_middlewares(transaction_scope_middleware)
@@ -0,0 +1,73 @@
1
+ from dataclasses import dataclass
2
+ from typing import TYPE_CHECKING, Annotated, Any
3
+
4
+ from fastapi import BackgroundTasks, Depends
5
+ from injection.ext.fastapi import Inject
6
+
7
+ from cq import (
8
+ Command,
9
+ CommandBus,
10
+ DeferredDispatcher,
11
+ Dispatcher,
12
+ Event,
13
+ EventBus,
14
+ Query,
15
+ QueryBus,
16
+ )
17
+
18
+ __all__ = (
19
+ "DeferredCommandBus",
20
+ "DeferredEventBus",
21
+ "DeferredQueryBus",
22
+ "FastAPIDeferredDispatcher",
23
+ )
24
+
25
+
26
+ @dataclass(repr=False, eq=False, frozen=True, slots=True)
27
+ class FastAPIDeferredDispatcher[I](DeferredDispatcher[I]):
28
+ background_tasks: BackgroundTasks
29
+ dispatcher: Dispatcher[I, Any]
30
+
31
+ async def defer(self, input_value: I, /) -> None:
32
+ self.background_tasks.add_task(self.dispatcher.dispatch, input_value)
33
+
34
+
35
+ async def new_deferred_command_bus[T](
36
+ background_tasks: BackgroundTasks,
37
+ command_bus: Inject[CommandBus[T]],
38
+ ) -> DeferredDispatcher[Command]:
39
+ return FastAPIDeferredDispatcher(background_tasks, command_bus)
40
+
41
+
42
+ async def new_deferred_event_bus(
43
+ background_tasks: BackgroundTasks,
44
+ event_bus: Inject[EventBus],
45
+ ) -> DeferredDispatcher[Event]:
46
+ return FastAPIDeferredDispatcher(background_tasks, event_bus)
47
+
48
+
49
+ async def new_deferred_query_bus[T](
50
+ background_tasks: BackgroundTasks,
51
+ query_bus: Inject[QueryBus[T]],
52
+ ) -> DeferredDispatcher[Query]:
53
+ return FastAPIDeferredDispatcher(background_tasks, query_bus)
54
+
55
+
56
+ if TYPE_CHECKING:
57
+ type DeferredCommandBus = DeferredDispatcher[Command]
58
+ type DeferredEventBus = DeferredDispatcher[Event]
59
+ type DeferredQueryBus = DeferredDispatcher[Query]
60
+
61
+ else:
62
+ DeferredCommandBus = Annotated[
63
+ DeferredDispatcher[Command],
64
+ Depends(new_deferred_command_bus, use_cache=False),
65
+ ]
66
+ DeferredEventBus = Annotated[
67
+ DeferredDispatcher[Event],
68
+ Depends(new_deferred_event_bus, use_cache=False),
69
+ ]
70
+ DeferredQueryBus = Annotated[
71
+ DeferredDispatcher[Query],
72
+ Depends(new_deferred_query_bus, use_cache=False),
73
+ ]
@@ -23,7 +23,7 @@ test = [
23
23
 
24
24
  [project]
25
25
  name = "python-cq"
26
- version = "0.9.1"
26
+ version = "0.11.0"
27
27
  description = "Lightweight CQRS library for async Python projects."
28
28
  license = "MIT"
29
29
  license-files = ["LICENSE"]
@@ -1,10 +0,0 @@
1
- from abc import abstractmethod
2
- from typing import Protocol
3
-
4
-
5
- class DeferredBus[I](Protocol):
6
- __slots__ = ()
7
-
8
- @abstractmethod
9
- async def defer(self, input_value: I, /) -> None:
10
- raise NotImplementedError
@@ -1,82 +0,0 @@
1
- from collections.abc import Awaitable, Callable
2
- from dataclasses import dataclass, field
3
- from typing import Any, Self, overload
4
-
5
- from cq._core.dispatcher.base import BaseDispatcher, Dispatcher
6
-
7
- type PipeConverter[I, O] = Callable[[O], Awaitable[I]]
8
-
9
-
10
- @dataclass(repr=False, eq=False, frozen=True, slots=True)
11
- class PipeStep[I, O]:
12
- converter: PipeConverter[I, O]
13
- dispatcher: Dispatcher[I, Any] | None = field(default=None)
14
-
15
-
16
- class Pipe[I, O](BaseDispatcher[I, O]):
17
- __slots__ = ("__dispatcher", "__steps")
18
-
19
- __dispatcher: Dispatcher[Any, Any]
20
- __steps: list[PipeStep[Any, Any]]
21
-
22
- def __init__(self, dispatcher: Dispatcher[Any, Any]) -> None:
23
- super().__init__()
24
- self.__dispatcher = dispatcher
25
- self.__steps = []
26
-
27
- @overload
28
- def step[T](
29
- self,
30
- wrapped: PipeConverter[T, Any],
31
- /,
32
- *,
33
- dispatcher: Dispatcher[T, Any] | None = ...,
34
- ) -> PipeConverter[T, Any]: ...
35
-
36
- @overload
37
- def step[T](
38
- self,
39
- wrapped: None = ...,
40
- /,
41
- *,
42
- dispatcher: Dispatcher[T, Any] | None = ...,
43
- ) -> Callable[[PipeConverter[T, Any]], PipeConverter[T, Any]]: ...
44
-
45
- def step[T](
46
- self,
47
- wrapped: PipeConverter[T, Any] | None = None,
48
- /,
49
- *,
50
- dispatcher: Dispatcher[T, Any] | None = None,
51
- ) -> Any:
52
- def decorator(wp: PipeConverter[T, Any]) -> PipeConverter[T, Any]:
53
- step = PipeStep(wp, dispatcher)
54
- self.__steps.append(step)
55
- return wp
56
-
57
- return decorator(wrapped) if wrapped else decorator
58
-
59
- def add_static_step[T](
60
- self,
61
- input_value: T,
62
- *,
63
- dispatcher: Dispatcher[T, Any] | None = None,
64
- ) -> Self:
65
- @self.step(dispatcher=dispatcher)
66
- async def converter(_: Any) -> T:
67
- return input_value
68
-
69
- return self
70
-
71
- async def dispatch(self, input_value: I, /) -> O:
72
- return await self._invoke_with_middlewares(self.__execute, input_value)
73
-
74
- async def __execute(self, input_value: I) -> O:
75
- dispatcher = self.__dispatcher
76
-
77
- for step in self.__steps:
78
- output_value = await dispatcher.dispatch(input_value)
79
- input_value = await step.converter(output_value)
80
- dispatcher = step.dispatcher or self.__dispatcher
81
-
82
- return await dispatcher.dispatch(input_value)
@@ -1,44 +0,0 @@
1
- from dataclasses import dataclass
2
- from typing import Annotated, Any
3
-
4
- from fastapi import BackgroundTasks, Depends
5
- from injection.ext.fastapi import Inject
6
-
7
- from cq import Bus, Command, CommandBus, DeferredBus, Event, EventBus, Query, QueryBus
8
-
9
- __all__ = ("DeferredCommandBus", "DeferredEventBus", "DeferredQueryBus")
10
-
11
-
12
- @dataclass(repr=False, eq=False, frozen=True, slots=True)
13
- class FastAPIDeferredBus[I](DeferredBus[I]):
14
- background_tasks: BackgroundTasks
15
- bus: Bus[I, Any]
16
-
17
- async def defer(self, input_value: I, /) -> None:
18
- self.background_tasks.add_task(self.bus.dispatch, input_value)
19
-
20
-
21
- async def new_deferred_command_bus[T](
22
- background_tasks: BackgroundTasks,
23
- command_bus: Inject[CommandBus[T]],
24
- ) -> DeferredBus[Command]:
25
- return FastAPIDeferredBus(background_tasks, command_bus)
26
-
27
-
28
- async def new_deferred_event_bus(
29
- background_tasks: BackgroundTasks,
30
- event_bus: Inject[EventBus],
31
- ) -> DeferredBus[Event]:
32
- return FastAPIDeferredBus(background_tasks, event_bus)
33
-
34
-
35
- async def new_deferred_query_bus[T](
36
- background_tasks: BackgroundTasks,
37
- query_bus: Inject[QueryBus[T]],
38
- ) -> DeferredBus[Query]:
39
- return FastAPIDeferredBus(background_tasks, query_bus)
40
-
41
-
42
- DeferredCommandBus = Annotated[DeferredBus[Command], Depends(new_deferred_command_bus)]
43
- DeferredEventBus = Annotated[DeferredBus[Event], Depends(new_deferred_event_bus)]
44
- DeferredQueryBus = Annotated[DeferredBus[Query], Depends(new_deferred_query_bus)]
@@ -1,5 +0,0 @@
1
- from cq import Command, DeferredBus, Event, Query
2
-
3
- type DeferredCommandBus = DeferredBus[Command]
4
- type DeferredEventBus = DeferredBus[Event]
5
- type DeferredQueryBus = DeferredBus[Query]
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes