hexastack-cqrs 0.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.

Potentially problematic release.


This version of hexastack-cqrs might be problematic. Click here for more details.

Files changed (40) hide show
  1. hexastack_cqrs-0.0.0/PKG-INFO +177 -0
  2. hexastack_cqrs-0.0.0/README.md +164 -0
  3. hexastack_cqrs-0.0.0/pyproject.toml +59 -0
  4. hexastack_cqrs-0.0.0/pyproject.toml.orig +60 -0
  5. hexastack_cqrs-0.0.0/src/hexastack_cqrs/__init__.py +8 -0
  6. hexastack_cqrs-0.0.0/src/hexastack_cqrs/adapters/__init__.py +19 -0
  7. hexastack_cqrs-0.0.0/src/hexastack_cqrs/adapters/buses/__init__.py +23 -0
  8. hexastack_cqrs-0.0.0/src/hexastack_cqrs/adapters/buses/command/__init__.py +13 -0
  9. hexastack_cqrs-0.0.0/src/hexastack_cqrs/adapters/buses/command/asynchronous.py +131 -0
  10. hexastack_cqrs-0.0.0/src/hexastack_cqrs/adapters/buses/command/synchronous.py +53 -0
  11. hexastack_cqrs-0.0.0/src/hexastack_cqrs/adapters/buses/event/__init__.py +17 -0
  12. hexastack_cqrs-0.0.0/src/hexastack_cqrs/adapters/buses/event/asynchronous.py +231 -0
  13. hexastack_cqrs-0.0.0/src/hexastack_cqrs/adapters/buses/event/recording.py +68 -0
  14. hexastack_cqrs-0.0.0/src/hexastack_cqrs/adapters/buses/event/synchronous.py +93 -0
  15. hexastack_cqrs-0.0.0/src/hexastack_cqrs/adapters/buses/query/__init__.py +5 -0
  16. hexastack_cqrs-0.0.0/src/hexastack_cqrs/adapters/buses/query/synchronous.py +53 -0
  17. hexastack_cqrs-0.0.0/src/hexastack_cqrs/domain/__init__.py +7 -0
  18. hexastack_cqrs-0.0.0/src/hexastack_cqrs/domain/handlers.py +51 -0
  19. hexastack_cqrs-0.0.0/src/hexastack_cqrs/infra/__init__.py +99 -0
  20. hexastack_cqrs-0.0.0/src/hexastack_cqrs/infra/autodiscovery.py +173 -0
  21. hexastack_cqrs-0.0.0/src/hexastack_cqrs/infra/bootstrap.py +267 -0
  22. hexastack_cqrs-0.0.0/src/hexastack_cqrs/infra/config.py +107 -0
  23. hexastack_cqrs-0.0.0/src/hexastack_cqrs/infra/decorators.py +226 -0
  24. hexastack_cqrs-0.0.0/src/hexastack_cqrs/infra/middleware/__init__.py +17 -0
  25. hexastack_cqrs-0.0.0/src/hexastack_cqrs/infra/middleware/correlation.py +59 -0
  26. hexastack_cqrs-0.0.0/src/hexastack_cqrs/infra/middleware/feature_flag.py +81 -0
  27. hexastack_cqrs-0.0.0/src/hexastack_cqrs/infra/middleware/generic.py +32 -0
  28. hexastack_cqrs-0.0.0/src/hexastack_cqrs/infra/middleware/logging.py +104 -0
  29. hexastack_cqrs-0.0.0/src/hexastack_cqrs/infra/middleware/retry.py +95 -0
  30. hexastack_cqrs-0.0.0/src/hexastack_cqrs/infra/middleware/timing.py +91 -0
  31. hexastack_cqrs-0.0.0/src/hexastack_cqrs/infra/middleware/unit_of_work.py +75 -0
  32. hexastack_cqrs-0.0.0/src/hexastack_cqrs/infra/pipeline.py +220 -0
  33. hexastack_cqrs-0.0.0/src/hexastack_cqrs/infra/registries/__init__.py +24 -0
  34. hexastack_cqrs-0.0.0/src/hexastack_cqrs/infra/registries/command.py +20 -0
  35. hexastack_cqrs-0.0.0/src/hexastack_cqrs/infra/registries/handler.py +25 -0
  36. hexastack_cqrs-0.0.0/src/hexastack_cqrs/infra/registries/presenter.py +80 -0
  37. hexastack_cqrs-0.0.0/src/hexastack_cqrs/infra/registries/query.py +22 -0
  38. hexastack_cqrs-0.0.0/src/hexastack_cqrs/ports/__init__.py +0 -0
  39. hexastack_cqrs-0.0.0/src/hexastack_cqrs/ports/buses.py +75 -0
  40. hexastack_cqrs-0.0.0/src/hexastack_cqrs/py.typed +0 -0
@@ -0,0 +1,177 @@
1
+ Metadata-Version: 2.3
2
+ Name: hexastack-cqrs
3
+ Version: 0.0.0
4
+ Summary: CQRS commands, queries, events, buses, and execution pipeline for Hexastack
5
+ Author: Richard West
6
+ Author-email: Richard West <dopplereffect.us@gmail.com>
7
+ Requires-Dist: hexastack-core
8
+ Requires-Dist: tenacity>=9.0.0
9
+ Requires-Dist: huey>=3.3.4 ; extra == 'huey'
10
+ Requires-Python: >=3.13
11
+ Provides-Extra: huey
12
+ Description-Content-Type: text/markdown
13
+
14
+ # hexastack-cqrs
15
+
16
+ > Command Query Responsibility Segregation (CQRS) buses, execution pipelines, and middleware for Hexastack.
17
+
18
+ [![Python 3.13+](https://img.shields.io/badge/python-3.13+-blue.svg)](https://www.python.org/downloads/)
19
+
20
+ ---
21
+
22
+ ## 1. Overview & Capabilities
23
+
24
+ `hexastack-cqrs` powers the application logic layer of Hexastack applications, enforcing a strict separation between write operations (Commands) and read projections (Queries):
25
+
26
+ - **Synchronous & Asynchronous Buses**: `SynchronousCommandBus`, `SynchronousQueryBus`, `SynchronousEventBus`, and async/distributed buses.
27
+ - **Composable Middleware Pipeline**: Pluggable middlewares executed sequentially before and after command/query handling:
28
+ - `CorrelationMiddleware`: Propagates correlation IDs across requests and async tasks.
29
+ - `TimingMiddleware`: Measures and records execution latency.
30
+ - `LoggingMiddleware`: Logs execution lifecycles and errors via `LoggerPort` (dynamically gated via `features.cqrs.logging`).
31
+ - `TenacityRetryMiddleware`: Exponential backoff and retry policies powered by `tenacity` (dynamically gated via `features.cqrs.retry`).
32
+ - `UnitOfWorkMiddleware`: Automatic transaction scoping (`commit()` on success, `rollback()` on failure).
33
+ - `ConditionalFeatureFlagMiddleware`: Evaluates dynamic feature flags before dispatching commands/queries.
34
+ - **Declarative Decorators & Scanning**: `@command_handler`, `@query_handler`, `@event_handler`, and `@feature_flag` registered via reflective module scanning.
35
+ - **Presenter & Response Mapping**: Integration with `PresenterPort` for formatting outputs across CLI and REST adapters.
36
+
37
+ ---
38
+
39
+ ## 2. Package Anatomy & Key Components
40
+
41
+ ```
42
+ hexastack_cqrs/
43
+ ├── domain/ # Command, Query, Event, Handler protocols, CQRS exceptions
44
+ ├── ports/ # CommandBusPort, QueryBusPort, EventBusPort, MiddlewarePort
45
+ ├── adapters/ # Buses (Synchronous & Asynchronous with Huey)
46
+ └── infra/ # CqrsBootstrapper (order=20), Middleware pipeline, Registries, @feature_flag
47
+ ```
48
+
49
+ ### Key Exports
50
+
51
+ | Category | Exports |
52
+ |---|---|
53
+ | **Adapters** | `SynchronousCommandBus`, `SynchronousQueryBus`, `SynchronousEventBus`, `HueyCommandBus`, `HueyEventBus` |
54
+ | **Bootstrap** | `CqrsBootstrapper` (order=20), `CqrsConfig` |
55
+ | **Decorators** | `@command_handler`, `@query_handler`, `@event_handler`, `@feature_flag`, `@presenter` |
56
+ | **Domain** | `Command`, `Query`, `Event`, `CommandHandler`, `QueryHandler`, `EventHandler` |
57
+ | **Middlewares** | `CorrelationMiddleware`, `TimingMiddleware`, `LoggingMiddleware`, `TenacityRetryMiddleware`, `UnitOfWorkMiddleware`, `ConditionalFeatureFlagMiddleware`, `ExecutionPipeline` |
58
+ | **Ports** | `CommandBusPort`, `QueryBusPort`, `EventBusPort`, `MiddlewarePort` |
59
+
60
+ ---
61
+
62
+ ## 3. Monorepo & Sibling Relationships
63
+
64
+ ```mermaid
65
+ graph TD
66
+ subgraph DrivingAdapters ["Inbound Driving Adapters"]
67
+ FASTAPI["hexastack-fastapi"]
68
+ GRAPHQL["hexastack-graphql"]
69
+ MCP["hexastack-mcp"]
70
+ GRPC["hexastack-grpc"]
71
+ CLI["hexastack-cli"]
72
+ end
73
+
74
+ subgraph CQRS ["hexastack-cqrs"]
75
+ CBUS["CommandBusPort"]
76
+ QBUS["QueryBusPort"]
77
+ EBUS["EventBusPort"]
78
+ PIPE["Middleware Pipeline"]
79
+ end
80
+
81
+ subgraph DrivenDependencies ["Driven Subsystems & Kernel"]
82
+ CORE["hexastack-core (DI & Kernel)"]
83
+ DB["hexastack-db (UnitOfWorkPort)"]
84
+ LOG["hexastack-logging (LoggerPort)"]
85
+ end
86
+
87
+ FASTAPI -->|dispatches to| CBUS
88
+ FASTAPI -->|dispatches to| QBUS
89
+ GRAPHQL -->|dispatches to| CBUS
90
+ GRAPHQL -->|dispatches to| QBUS
91
+ MCP -->|dispatches to| CBUS
92
+ MCP -->|dispatches to| QBUS
93
+ GRPC -->|dispatches to| CBUS
94
+ GRPC -->|dispatches to| QBUS
95
+ CLI -->|dispatches to| CBUS
96
+ CLI -->|dispatches to| QBUS
97
+
98
+ CBUS --> PIPE
99
+ QBUS --> PIPE
100
+ EBUS --> PIPE
101
+
102
+ PIPE -->|managed by| CORE
103
+ PIPE -. consumes from DI .-> DB
104
+ PIPE -. consumes from DI .-> LOG
105
+ ```
106
+
107
+ ### Explicit Dependencies (Direct)
108
+ - `hexastack-core`: Core kernel, DI container, base exceptions, and ports.
109
+ - `tenacity>=9.0.0`: Resilient retry policies with exponential backoff.
110
+
111
+ ### Implied / Behavioral Relationships (DI-Mediated)
112
+ - **UnitOfWork Scoping**: `UnitOfWorkMiddleware` dynamically resolves `UnitOfWorkPort` from the DI container (injected by `hexastack-db`) to manage transactions.
113
+ - **Telemetry Integration**: `LoggingMiddleware` dynamically resolves `LoggerPort` from the DI container (injected by `hexastack-logging`).
114
+ - **Driving Adapters**: Consumed by `hexastack-fastapi` and `hexastack-cli` to route incoming user actions to business handlers.
115
+
116
+ ### Optional Integrations (Extras)
117
+ - `[huey]`: Enables asynchronous, distributed task queue execution using `HueyCommandBus` and `HueyEventBus`.
118
+
119
+ ---
120
+
121
+ ## 4. Installation
122
+
123
+ ```bash
124
+ # Standalone installation
125
+ pip install hexastack-cqrs
126
+
127
+ # With asynchronous distributed background worker (Huey)
128
+ pip install "hexastack-cqrs[huey]"
129
+
130
+ # Via umbrella package
131
+ pip install hexastack
132
+ ```
133
+
134
+ ---
135
+
136
+ ## 5. Configuration Reference
137
+
138
+ ```toml
139
+ [hexastack.cqrs]
140
+ enable_correlation = true
141
+ enable_timing = true
142
+ enable_logging = true
143
+ enable_uow = true
144
+ retry_attempts = 3
145
+ retry_backoff_base = 0.5
146
+ ```
147
+
148
+ ---
149
+
150
+ ## 6. Quickstart Example
151
+
152
+ ```python
153
+ from dataclasses import dataclass
154
+ from hexastack_core.infra.bootstrap import bootstrap
155
+ from hexastack_cqrs.domain.command import Command
156
+ from hexastack_cqrs.infra.decorators import command_handler
157
+ from hexastack_cqrs.ports.buses import CommandBusPort
158
+
159
+
160
+ @dataclass(frozen=True)
161
+ class CreateOrderCommand(Command):
162
+ order_id: str
163
+ amount: float
164
+
165
+
166
+ @command_handler(CreateOrderCommand)
167
+ class CreateOrderHandler:
168
+ def __call__(self, cmd: CreateOrderCommand) -> str:
169
+ return f"Order {cmd.order_id} created for ${cmd.amount:.2f}"
170
+
171
+
172
+ runtime = bootstrap(packages_to_scan=[__name__])
173
+ bus = runtime.container.get(CommandBusPort)
174
+
175
+ result = bus.dispatch(CreateOrderCommand(order_id="ord-99", amount=49.99))
176
+ print(result) # "Order ord-99 created for $49.99"
177
+ ```
@@ -0,0 +1,164 @@
1
+ # hexastack-cqrs
2
+
3
+ > Command Query Responsibility Segregation (CQRS) buses, execution pipelines, and middleware for Hexastack.
4
+
5
+ [![Python 3.13+](https://img.shields.io/badge/python-3.13+-blue.svg)](https://www.python.org/downloads/)
6
+
7
+ ---
8
+
9
+ ## 1. Overview & Capabilities
10
+
11
+ `hexastack-cqrs` powers the application logic layer of Hexastack applications, enforcing a strict separation between write operations (Commands) and read projections (Queries):
12
+
13
+ - **Synchronous & Asynchronous Buses**: `SynchronousCommandBus`, `SynchronousQueryBus`, `SynchronousEventBus`, and async/distributed buses.
14
+ - **Composable Middleware Pipeline**: Pluggable middlewares executed sequentially before and after command/query handling:
15
+ - `CorrelationMiddleware`: Propagates correlation IDs across requests and async tasks.
16
+ - `TimingMiddleware`: Measures and records execution latency.
17
+ - `LoggingMiddleware`: Logs execution lifecycles and errors via `LoggerPort` (dynamically gated via `features.cqrs.logging`).
18
+ - `TenacityRetryMiddleware`: Exponential backoff and retry policies powered by `tenacity` (dynamically gated via `features.cqrs.retry`).
19
+ - `UnitOfWorkMiddleware`: Automatic transaction scoping (`commit()` on success, `rollback()` on failure).
20
+ - `ConditionalFeatureFlagMiddleware`: Evaluates dynamic feature flags before dispatching commands/queries.
21
+ - **Declarative Decorators & Scanning**: `@command_handler`, `@query_handler`, `@event_handler`, and `@feature_flag` registered via reflective module scanning.
22
+ - **Presenter & Response Mapping**: Integration with `PresenterPort` for formatting outputs across CLI and REST adapters.
23
+
24
+ ---
25
+
26
+ ## 2. Package Anatomy & Key Components
27
+
28
+ ```
29
+ hexastack_cqrs/
30
+ ├── domain/ # Command, Query, Event, Handler protocols, CQRS exceptions
31
+ ├── ports/ # CommandBusPort, QueryBusPort, EventBusPort, MiddlewarePort
32
+ ├── adapters/ # Buses (Synchronous & Asynchronous with Huey)
33
+ └── infra/ # CqrsBootstrapper (order=20), Middleware pipeline, Registries, @feature_flag
34
+ ```
35
+
36
+ ### Key Exports
37
+
38
+ | Category | Exports |
39
+ |---|---|
40
+ | **Adapters** | `SynchronousCommandBus`, `SynchronousQueryBus`, `SynchronousEventBus`, `HueyCommandBus`, `HueyEventBus` |
41
+ | **Bootstrap** | `CqrsBootstrapper` (order=20), `CqrsConfig` |
42
+ | **Decorators** | `@command_handler`, `@query_handler`, `@event_handler`, `@feature_flag`, `@presenter` |
43
+ | **Domain** | `Command`, `Query`, `Event`, `CommandHandler`, `QueryHandler`, `EventHandler` |
44
+ | **Middlewares** | `CorrelationMiddleware`, `TimingMiddleware`, `LoggingMiddleware`, `TenacityRetryMiddleware`, `UnitOfWorkMiddleware`, `ConditionalFeatureFlagMiddleware`, `ExecutionPipeline` |
45
+ | **Ports** | `CommandBusPort`, `QueryBusPort`, `EventBusPort`, `MiddlewarePort` |
46
+
47
+ ---
48
+
49
+ ## 3. Monorepo & Sibling Relationships
50
+
51
+ ```mermaid
52
+ graph TD
53
+ subgraph DrivingAdapters ["Inbound Driving Adapters"]
54
+ FASTAPI["hexastack-fastapi"]
55
+ GRAPHQL["hexastack-graphql"]
56
+ MCP["hexastack-mcp"]
57
+ GRPC["hexastack-grpc"]
58
+ CLI["hexastack-cli"]
59
+ end
60
+
61
+ subgraph CQRS ["hexastack-cqrs"]
62
+ CBUS["CommandBusPort"]
63
+ QBUS["QueryBusPort"]
64
+ EBUS["EventBusPort"]
65
+ PIPE["Middleware Pipeline"]
66
+ end
67
+
68
+ subgraph DrivenDependencies ["Driven Subsystems & Kernel"]
69
+ CORE["hexastack-core (DI & Kernel)"]
70
+ DB["hexastack-db (UnitOfWorkPort)"]
71
+ LOG["hexastack-logging (LoggerPort)"]
72
+ end
73
+
74
+ FASTAPI -->|dispatches to| CBUS
75
+ FASTAPI -->|dispatches to| QBUS
76
+ GRAPHQL -->|dispatches to| CBUS
77
+ GRAPHQL -->|dispatches to| QBUS
78
+ MCP -->|dispatches to| CBUS
79
+ MCP -->|dispatches to| QBUS
80
+ GRPC -->|dispatches to| CBUS
81
+ GRPC -->|dispatches to| QBUS
82
+ CLI -->|dispatches to| CBUS
83
+ CLI -->|dispatches to| QBUS
84
+
85
+ CBUS --> PIPE
86
+ QBUS --> PIPE
87
+ EBUS --> PIPE
88
+
89
+ PIPE -->|managed by| CORE
90
+ PIPE -. consumes from DI .-> DB
91
+ PIPE -. consumes from DI .-> LOG
92
+ ```
93
+
94
+ ### Explicit Dependencies (Direct)
95
+ - `hexastack-core`: Core kernel, DI container, base exceptions, and ports.
96
+ - `tenacity>=9.0.0`: Resilient retry policies with exponential backoff.
97
+
98
+ ### Implied / Behavioral Relationships (DI-Mediated)
99
+ - **UnitOfWork Scoping**: `UnitOfWorkMiddleware` dynamically resolves `UnitOfWorkPort` from the DI container (injected by `hexastack-db`) to manage transactions.
100
+ - **Telemetry Integration**: `LoggingMiddleware` dynamically resolves `LoggerPort` from the DI container (injected by `hexastack-logging`).
101
+ - **Driving Adapters**: Consumed by `hexastack-fastapi` and `hexastack-cli` to route incoming user actions to business handlers.
102
+
103
+ ### Optional Integrations (Extras)
104
+ - `[huey]`: Enables asynchronous, distributed task queue execution using `HueyCommandBus` and `HueyEventBus`.
105
+
106
+ ---
107
+
108
+ ## 4. Installation
109
+
110
+ ```bash
111
+ # Standalone installation
112
+ pip install hexastack-cqrs
113
+
114
+ # With asynchronous distributed background worker (Huey)
115
+ pip install "hexastack-cqrs[huey]"
116
+
117
+ # Via umbrella package
118
+ pip install hexastack
119
+ ```
120
+
121
+ ---
122
+
123
+ ## 5. Configuration Reference
124
+
125
+ ```toml
126
+ [hexastack.cqrs]
127
+ enable_correlation = true
128
+ enable_timing = true
129
+ enable_logging = true
130
+ enable_uow = true
131
+ retry_attempts = 3
132
+ retry_backoff_base = 0.5
133
+ ```
134
+
135
+ ---
136
+
137
+ ## 6. Quickstart Example
138
+
139
+ ```python
140
+ from dataclasses import dataclass
141
+ from hexastack_core.infra.bootstrap import bootstrap
142
+ from hexastack_cqrs.domain.command import Command
143
+ from hexastack_cqrs.infra.decorators import command_handler
144
+ from hexastack_cqrs.ports.buses import CommandBusPort
145
+
146
+
147
+ @dataclass(frozen=True)
148
+ class CreateOrderCommand(Command):
149
+ order_id: str
150
+ amount: float
151
+
152
+
153
+ @command_handler(CreateOrderCommand)
154
+ class CreateOrderHandler:
155
+ def __call__(self, cmd: CreateOrderCommand) -> str:
156
+ return f"Order {cmd.order_id} created for ${cmd.amount:.2f}"
157
+
158
+
159
+ runtime = bootstrap(packages_to_scan=[__name__])
160
+ bus = runtime.container.get(CommandBusPort)
161
+
162
+ result = bus.dispatch(CreateOrderCommand(order_id="ord-99", amount=49.99))
163
+ print(result) # "Order ord-99 created for $49.99"
164
+ ```
@@ -0,0 +1,59 @@
1
+ [project]
2
+ name = "hexastack-cqrs"
3
+ version = "0.0.0"
4
+ description = "CQRS commands, queries, events, buses, and execution pipeline for Hexastack"
5
+ readme = "README.md"
6
+ requires-python = ">=3.13"
7
+ dependencies = [
8
+ "hexastack-core",
9
+ "tenacity>=9.0.0",
10
+ ]
11
+
12
+ [[project.authors]]
13
+ name = "Richard West"
14
+ email = "dopplereffect.us@gmail.com"
15
+
16
+ [project.optional-dependencies]
17
+ huey = ["huey>=3.3.4"]
18
+
19
+ [project.entry-points."hexastack.bootstrappers"]
20
+ cqrs = "hexastack_cqrs.infra.bootstrap:CqrsBootstrapper"
21
+
22
+ [build-system]
23
+ requires = ["uv_build>=0.12.3,<0.13.0"]
24
+ build-backend = "uv_build"
25
+
26
+ [tool.uv.sources.hexastack-core]
27
+ workspace = true
28
+
29
+ [tool.importlinter]
30
+ root_packages = ["hexastack_cqrs"]
31
+
32
+ [[tool.importlinter.contracts]]
33
+ name = "Hexagonal architecture layer hierarchy"
34
+ type = "layers"
35
+ containers = ["hexastack_cqrs"]
36
+ layers = [
37
+ "adapters",
38
+ "ports",
39
+ "domain",
40
+ ]
41
+
42
+ [[tool.importlinter.contracts]]
43
+ name = "Forbidden imports for domain"
44
+ type = "forbidden"
45
+ source_modules = ["hexastack_cqrs.domain"]
46
+ forbidden_modules = [
47
+ "hexastack_cqrs.ports",
48
+ "hexastack_cqrs.adapters",
49
+ "hexastack_cqrs.infra",
50
+ ]
51
+
52
+ [[tool.importlinter.contracts]]
53
+ name = "Forbidden imports for ports"
54
+ type = "forbidden"
55
+ source_modules = ["hexastack_cqrs.ports"]
56
+ forbidden_modules = [
57
+ "hexastack_cqrs.adapters",
58
+ "hexastack_cqrs.infra",
59
+ ]
@@ -0,0 +1,60 @@
1
+ [project]
2
+ name = "hexastack-cqrs"
3
+ version = "0.0.0"
4
+ description = "CQRS commands, queries, events, buses, and execution pipeline for Hexastack"
5
+ readme = "README.md"
6
+ authors = [
7
+ { name = "Richard West", email = "dopplereffect.us@gmail.com" }
8
+ ]
9
+ requires-python = ">=3.13"
10
+ dependencies = [
11
+ "hexastack-core",
12
+ "tenacity>=9.0.0",
13
+ ]
14
+
15
+ [project.optional-dependencies]
16
+ huey = [
17
+ "huey>=3.3.4",
18
+ ]
19
+
20
+ [project.entry-points."hexastack.bootstrappers"]
21
+ cqrs = "hexastack_cqrs.infra.bootstrap:CqrsBootstrapper"
22
+
23
+ [build-system]
24
+ requires = ["uv_build>=0.12.3,<0.13.0"]
25
+ build-backend = "uv_build"
26
+
27
+ [tool.uv.sources]
28
+ hexastack-core = { workspace = true }
29
+
30
+ [tool.importlinter]
31
+ root_packages = ["hexastack_cqrs"]
32
+
33
+ [[tool.importlinter.contracts]]
34
+ name = "Hexagonal architecture layer hierarchy"
35
+ type = "layers"
36
+ containers = ["hexastack_cqrs"]
37
+ layers = [
38
+ "adapters",
39
+ "ports",
40
+ "domain",
41
+ ]
42
+
43
+ [[tool.importlinter.contracts]]
44
+ name = "Forbidden imports for domain"
45
+ type = "forbidden"
46
+ source_modules = ["hexastack_cqrs.domain"]
47
+ forbidden_modules = [
48
+ "hexastack_cqrs.ports",
49
+ "hexastack_cqrs.adapters",
50
+ "hexastack_cqrs.infra",
51
+ ]
52
+
53
+ [[tool.importlinter.contracts]]
54
+ name = "Forbidden imports for ports"
55
+ type = "forbidden"
56
+ source_modules = ["hexastack_cqrs.ports"]
57
+ forbidden_modules = [
58
+ "hexastack_cqrs.adapters",
59
+ "hexastack_cqrs.infra",
60
+ ]
@@ -0,0 +1,8 @@
1
+ from hexastack_cqrs import adapters, domain, infra, ports
2
+
3
+ __all__ = [
4
+ "adapters",
5
+ "domain",
6
+ "infra",
7
+ "ports",
8
+ ]
@@ -0,0 +1,19 @@
1
+ from hexastack_cqrs.adapters.buses import (
2
+ AsyncNativeCommandBus,
3
+ AsyncNativeEventBus,
4
+ HueyCommandBus,
5
+ HueyEventBus,
6
+ SynchronousCommandBus,
7
+ SynchronousEventBus,
8
+ SynchronousQueryBus,
9
+ )
10
+
11
+ __all__ = [
12
+ "AsyncNativeCommandBus",
13
+ "AsyncNativeEventBus",
14
+ "HueyCommandBus",
15
+ "HueyEventBus",
16
+ "SynchronousCommandBus",
17
+ "SynchronousEventBus",
18
+ "SynchronousQueryBus",
19
+ ]
@@ -0,0 +1,23 @@
1
+ from hexastack_cqrs.adapters.buses.command import (
2
+ AsyncNativeCommandBus,
3
+ HueyCommandBus,
4
+ SynchronousCommandBus,
5
+ )
6
+ from hexastack_cqrs.adapters.buses.event import (
7
+ AsyncNativeEventBus,
8
+ HueyEventBus,
9
+ RecordingEventBus,
10
+ SynchronousEventBus,
11
+ )
12
+ from hexastack_cqrs.adapters.buses.query import SynchronousQueryBus
13
+
14
+ __all__ = [
15
+ "AsyncNativeCommandBus",
16
+ "AsyncNativeEventBus",
17
+ "HueyCommandBus",
18
+ "HueyEventBus",
19
+ "RecordingEventBus",
20
+ "SynchronousCommandBus",
21
+ "SynchronousEventBus",
22
+ "SynchronousQueryBus",
23
+ ]
@@ -0,0 +1,13 @@
1
+ from hexastack_cqrs.adapters.buses.command.asynchronous import (
2
+ AsyncNativeCommandBus,
3
+ HueyCommandBus,
4
+ )
5
+ from hexastack_cqrs.adapters.buses.command.synchronous import (
6
+ SynchronousCommandBus,
7
+ )
8
+
9
+ __all__ = [
10
+ "AsyncNativeCommandBus",
11
+ "HueyCommandBus",
12
+ "SynchronousCommandBus",
13
+ ]
@@ -0,0 +1,131 @@
1
+ import importlib.util
2
+ from collections.abc import Callable
3
+ from concurrent.futures import Future, ThreadPoolExecutor
4
+ from typing import TYPE_CHECKING, Any
5
+
6
+ from hexastack_core.domain import Command, Generic
7
+ from hexastack_core.domain.exceptions import MissingDependencyError
8
+ from hexastack_cqrs.infra.middleware.generic import GenericMiddleware
9
+ from hexastack_cqrs.infra.registries.handler import HandlerRegistry
10
+ from hexastack_cqrs.ports.buses import CommandBusPort
11
+
12
+ if TYPE_CHECKING:
13
+ from huey import Huey
14
+
15
+
16
+ class HueyCommandBus(CommandBusPort):
17
+ """Asynchronous CommandBus adapter dispatching commands via Huey task queue.
18
+
19
+ Notes/Architectural Intent:
20
+ Delegates command execution to background worker processes via Huey,
21
+ enabling distributed, durable asynchronous background processing.
22
+ Requires hexastack-cqrs[huey].
23
+ """
24
+
25
+ def __init__(
26
+ self,
27
+ huey: "Huey",
28
+ handler_registry: HandlerRegistry,
29
+ middleware: list[GenericMiddleware] | None = None,
30
+ ) -> None:
31
+ """Initialize Huey command bus with Huey instance and handler registry.
32
+
33
+ Args:
34
+ huey: Initialized Huey task queue instance.
35
+ handler_registry: HandlerRegistry containing registered command handlers.
36
+ middleware: Optional ordered list of GenericMiddleware interceptors.
37
+
38
+ Raises:
39
+ MissingDependencyError: If huey package is not installed.
40
+ """
41
+ if importlib.util.find_spec("huey") is None:
42
+ raise MissingDependencyError(
43
+ "huey is required to use HueyCommandBus. "
44
+ "Install via 'pip install hexastack-cqrs[huey]'."
45
+ )
46
+
47
+ self._huey = huey
48
+ self._registry = handler_registry
49
+ self._middleware = list(middleware) if middleware is not None else []
50
+
51
+ @self._huey.task()
52
+ def _execute_command_task(cmd: Command) -> Any:
53
+ pipeline: Callable[[Generic], Any] = lambda inst: self._registry.handle(
54
+ inst, reraise=True
55
+ )
56
+ for mw in reversed(self._middleware):
57
+ next_fn = pipeline
58
+ pipeline = lambda inst, m=mw, n=next_fn: m(inst, n)
59
+ return pipeline(cmd)
60
+
61
+ self._task_fn = _execute_command_task
62
+
63
+ def dispatch(self, command: Command) -> Any:
64
+ """Enqueue command to Huey background queue for execution.
65
+
66
+ Args:
67
+ command: The command instance to enqueue.
68
+
69
+ Returns:
70
+ Huey Task object representing the background job.
71
+
72
+ Raises:
73
+ Exception: If enqueuing to Huey queue fails.
74
+ """
75
+ return self._task_fn(command)
76
+
77
+
78
+ class AsyncNativeCommandBus(CommandBusPort):
79
+ """Asynchronous in-process CommandBus using thread pool execution.
80
+
81
+ Notes/Architectural Intent:
82
+ Provides non-blocking in-process command execution without requiring external
83
+ queue backends, returning a standard concurrent.futures.Future.
84
+ """
85
+
86
+ def __init__(
87
+ self,
88
+ handler_registry: HandlerRegistry,
89
+ middleware: list[GenericMiddleware] | None = None,
90
+ executor: ThreadPoolExecutor | None = None,
91
+ ) -> None:
92
+ """Initialize native async command bus.
93
+
94
+ Args:
95
+ handler_registry: HandlerRegistry containing registered command handlers.
96
+ middleware: Optional ordered list of GenericMiddleware interceptors.
97
+ executor: Optional ThreadPoolExecutor for managing worker threads.
98
+ """
99
+ self._registry = handler_registry
100
+ self._middleware = list(middleware) if middleware is not None else []
101
+ self._executor = executor or ThreadPoolExecutor(thread_name_prefix="cqrs-async")
102
+
103
+ def dispatch(self, command: Command) -> Future[Any]:
104
+ """Submit command for asynchronous background execution on thread pool.
105
+
106
+ Args:
107
+ command: The command instance to dispatch.
108
+
109
+ Returns:
110
+ Future resolving to the handler execution result.
111
+
112
+ Raises:
113
+ Exception: If submitting task to thread pool executor fails.
114
+ """
115
+
116
+ def _run() -> Any:
117
+ pipeline: Callable[[Generic], Any] = lambda inst: self._registry.handle(
118
+ inst, reraise=True
119
+ )
120
+ for mw in reversed(self._middleware):
121
+ next_fn = pipeline
122
+ pipeline = lambda inst, m=mw, n=next_fn: m(inst, n)
123
+ return pipeline(command)
124
+
125
+ return self._executor.submit(_run)
126
+
127
+
128
+ __all__ = [
129
+ "AsyncNativeCommandBus",
130
+ "HueyCommandBus",
131
+ ]