castlecraft_engineer.abstractions
¶
castlecraft_engineer.abstractions
¶
Aggregate
¶
Bases: Generic[TAggregateId]
, ABC
Base class for Aggregate Roots.
Source code in src/castlecraft_engineer/abstractions/aggregate.py
AggregateRepository
¶
Bases: _RepositoryBase[TAggregateId, TAggregate, TSQLModel]
Generic repository mapping Aggregates to SQLModels.
Handles persistence for Aggregates, potentially spanning multiple related SQLModels if relationships and cascades are configured correctly on the SQLModel classes themselves.
Relies on the SQLAlchemy Unit of Work pattern managed outside the repository (e.g., in a Command Handler or Application Service).
Source code in src/castlecraft_engineer/abstractions/repository.py
198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 |
|
delete_by_id(session, id)
¶
Deletes an Aggregate by its ID. Relies on the ORM's cascade delete configuration for related models.
Source code in src/castlecraft_engineer/abstractions/repository.py
get_by_id(session, id)
¶
Retrieves an Aggregate by its ID. Loads the root model and relies on ORM relationship loading (eager or lazy) for related data.
Source code in src/castlecraft_engineer/abstractions/repository.py
save(session, aggregate)
¶
Persists Aggregate state (handles create or update).
Relies on the provided session being managed externally (Unit of Work). Adds new aggregates to the session or updates existing ones. Handles optimistic concurrency checking based on the 'version' field of the root model.
For updates involving related models (multiple tables), ensure
SQLModel relationships and cascade options are correctly configured.
The mapping logic (_map_aggregate_to_model
) should update the
state of the model instances, and the SQLAlchemy session flush
will handle persisting those changes.
Source code in src/castlecraft_engineer/abstractions/repository.py
246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 |
|
AsyncAggregateRepository
¶
Bases: _RepositoryBase[TAggregateId, TAggregate, TSQLModel]
Generic asynchronous repository mapping Aggregates to SQLModels using AsyncSession.
Source code in src/castlecraft_engineer/abstractions/repository.py
362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 |
|
delete_by_id(session, id)
async
¶
Asynchronously deletes an Aggregate by its ID using AsyncSession.
Source code in src/castlecraft_engineer/abstractions/repository.py
get_by_id(session, id)
async
¶
Asynchronously retrieves an Aggregate by its ID using AsyncSession.
Source code in src/castlecraft_engineer/abstractions/repository.py
save(session, aggregate)
async
¶
Asynchronously persists Aggregate state using AsyncSession.
Source code in src/castlecraft_engineer/abstractions/repository.py
404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 |
|
Command
dataclass
¶
Bases: ABC
Abstract base class for all commands in the CQRS pattern. Commands represent an intention to change the system state. They should be immutable data structures.
Source code in src/castlecraft_engineer/abstractions/command.py
CommandBus
¶
Coordinates the execution of commands by routing them to registered handlers, using a globally accessible dependency injection container.
Source code in src/castlecraft_engineer/abstractions/command_bus.py
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 |
|
__init__(container)
¶
Initializes the CommandBus with an empty handler registry.
Source code in src/castlecraft_engineer/abstractions/command_bus.py
execute(command, subject_id=None, permissions=[], *args, **kwargs)
async
¶
Executes a command by finding its handler class, resolving it via the global DI container, authorizing, and handling.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
command
|
Command
|
The command instance to execute. |
required |
subject_id
|
Optional[str]
|
The ID of the subject attempting to execute the command. |
None
|
permissions
|
List[Permission]
|
The permissions associated with the subject. |
[]
|
Raises:
Type | Description |
---|---|
CommandHandlerNotFoundError
|
If no handler class is registered for the command type. |
AuthorizationError
|
If the handler denies authorization. |
MissingDependencyError
|
If the container cannot resolve the handler or its dependencies. |
Exception
|
Any other exception raised during handler resolution or execution. |
Source code in src/castlecraft_engineer/abstractions/command_bus.py
register(handler_cls)
¶
Decorator to register a CommandHandler class with the bus and the global DI container.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
handler_cls
|
Type[CommandHandler[TCommand]]
|
The CommandHandler class to register. |
required |
Returns:
Type | Description |
---|---|
Type[CommandHandler[TCommand]]
|
The original handler class, unchanged. |
Raises:
Type | Description |
---|---|
TypeError
|
If the handler_cls is not a valid CommandHandler subclass or its command type cannot be determined. |
Source code in src/castlecraft_engineer/abstractions/command_bus.py
CommandHandler
¶
Bases: Generic[TCommand]
, ABC
Abstract base class for command handlers. Each handler is responsible for processing a specific type of command.
Source code in src/castlecraft_engineer/abstractions/command_handler.py
authorize(subject_id=None, permissions=[], *args, **kwargs)
async
¶
Optional pre-execution authorization check for the command.
NOTE: This method is NOT automatically called by the default CommandBus.
It serves as a convention or hook for developers to implement custom
pre-authorization logic if they choose to call it explicitly from within
their execute
method, or if they are using a custom bus implementation
that invokes it.
The recommended pattern for authorization with the default bus is to use
an injected AuthorizationService
within the execute
method.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
subject_id
|
Optional[str]
|
The ID of the subject. |
None
|
permissions
|
List[Permission]
|
A list of Permission objects representing the permissions granted to the subject. |
[]
|
*args
|
Any
|
Additional positional arguments. |
()
|
**kwargs
|
Any
|
Additional keyword arguments for context. |
{}
|
Returns: True if the subject has permission, False otherwise.
Source code in src/castlecraft_engineer/abstractions/command_handler.py
execute(command, *args, subject_id=None, permissions=[], **kwargs)
abstractmethod
async
¶
Handles the execution logic for the given command.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
command
|
TCommand
|
The command instance to be processed. |
required |
subject_id
|
Optional[str]
|
The ID of the subject attempting to execute the command. Optional. |
None
|
permissions
|
List[Permission]
|
The permissions associated with the subject. |
[]
|
Raises:
Type | Description |
---|---|
NotImplementedError
|
This method must be implemented by concrete subclasses. |
Source code in src/castlecraft_engineer/abstractions/command_handler.py
EventHandler
¶
Bases: Generic[TEvent]
, ABC
Abstract base class for event handlers. Each handler is responsible for reacting to a specific type of domain event.
Source code in src/castlecraft_engineer/abstractions/event_handler.py
handle(event)
abstractmethod
async
¶
Handles the logic to execute when the specific event occurs. Marked as async to easily accommodate I/O operations (like sending emails, updating databases, calling APIs).
Parameters:
Name | Type | Description | Default |
---|---|---|---|
event
|
TEvent
|
The event instance to be processed. |
required |
Raises:
Type | Description |
---|---|
NotImplementedError
|
This method must be implemented by concrete subclasses. |
Source code in src/castlecraft_engineer/abstractions/event_handler.py
EventStore
¶
Bases: Generic[TAggregateId]
, ABC
Abstract base class for an event store, responsible for persisting and retrieving streams of domain events.
Source code in src/castlecraft_engineer/abstractions/event_store.py
append_events(aggregate_id, expected_version, events)
abstractmethod
async
¶
Appends a list of events to the stream for a given aggregate.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
aggregate_id
|
TAggregateId
|
The ID of the aggregate to which the events belong. |
required |
expected_version
|
int
|
The version of the aggregate that these events are based on. Used for optimistic concurrency control. If the current version in the store does not match this, an EventStoreConflictError should be raised. |
required |
events
|
List[Event]
|
A list of domain event instances to append. |
required |
Raises:
Type | Description |
---|---|
EventStoreConflictError
|
If the expected_version does not match the current version of the event stream for the aggregate. |
Exception
|
Implementation-specific exceptions related to storage failures. |
Source code in src/castlecraft_engineer/abstractions/event_store.py
get_current_version(aggregate_id)
abstractmethod
async
¶
Retrieves the current version of the event stream for a given aggregate.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
aggregate_id
|
TAggregateId
|
The ID of the aggregate. |
required |
Returns:
Type | Description |
---|---|
Optional[int]
|
The current version (number of events - 1, or sequence of last event) |
Optional[int]
|
or None if the aggregate stream doesn't exist. |
Source code in src/castlecraft_engineer/abstractions/event_store.py
load_events(aggregate_id, from_version=None)
abstractmethod
async
¶
Loads the stream of events for a given aggregate.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
aggregate_id
|
TAggregateId
|
The ID of the aggregate whose events are to be loaded. |
required |
from_version
|
Optional[int]
|
Optionally, the version from which to start loading events. If None, loads all events for the aggregate. |
None
|
Returns:
Type | Description |
---|---|
List[Event]
|
A list of domain event instances, ordered by their sequence. |
List[Event]
|
Returns an empty list if the aggregate has no events or doesn't exist. |
Source code in src/castlecraft_engineer/abstractions/event_store.py
EventStreamConsumer
¶
Bases: ABC
Abstract base class for consuming events from an external stream and publishing them to an internal EventBus.
Source code in src/castlecraft_engineer/abstractions/event_consumer.py
run()
abstractmethod
async
¶
Starts the consumer loop to listen for and process events. This method should typically run indefinitely until shutdown is requested.
shutdown()
abstractmethod
async
¶
Initiates a graceful shutdown of the consumer. Implementations should ensure pending work is handled and resources are released.
ExternalEventPublisher
¶
Bases: ABC
Abstract base class for publishing domain events to external messaging systems (e.g., Kafka, RabbitMQ, Redis Streams).
Source code in src/castlecraft_engineer/abstractions/event_publisher.py
close()
abstractmethod
async
¶
publish(events)
abstractmethod
async
¶
Publishes a list of domain events to the external broker.
Implementations should handle serialization,connection management, and the specifics of the chosen message broker protocol.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
events
|
List[Event]
|
A list of domain event instances to publish. |
required |
Raises:
Type | Description |
---|---|
NotImplementedError
|
This method must be implemented by concrete subclasses. |
Exception
|
Implementation-specific exceptions related to publishing failures (e.g., connection errors, serialization issues). |
Source code in src/castlecraft_engineer/abstractions/event_publisher.py
Query
dataclass
¶
Bases: ABC
Abstract base class for all Queries in the CQRS pattern. Query represent an intention to request the system state. They should be immutable data structures.
Source code in src/castlecraft_engineer/abstractions/query.py
QueryBus
¶
Coordinates the execution of query by routing them to registered handlers, using a globally accessible dependency injection container.
Source code in src/castlecraft_engineer/abstractions/query_bus.py
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 |
|
__init__(container)
¶
Initializes the QueryBus with an empty handler registry. It relies on a globally accessible DI container ('container').
Source code in src/castlecraft_engineer/abstractions/query_bus.py
execute(query, subject_id=None, permissions=[], *args, **kwargs)
async
¶
Executes a query by finding its handler class, resolving it via the global DI container, authorizing, and handling.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
query
|
Query
|
The query instance to execute. |
required |
Raises:
Type | Description |
---|---|
QueryHandlerNotFoundError
|
If no handler class is registered for the query type. |
MissingDependencyError
|
If the container cannot resolve the handler or its dependencies. |
Exception
|
Any other exception raised during handler resolution or execution. |
Source code in src/castlecraft_engineer/abstractions/query_bus.py
register(handler_cls)
¶
Decorator to register a QueryHandler class with the bus and the global DI container.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
handler_cls
|
Type[QueryHandler[TQuery]]
|
The QueryHandler class to register. |
required |
Returns:
Type | Description |
---|---|
Type[QueryHandler[TQuery]]
|
The original handler class, unchanged. |
Raises:
Type | Description |
---|---|
TypeError
|
If the handler_cls is not a valid QueryHandler subclass or its query type cannot be determined. |
ValueError
|
If a handler is already registered for the query type. |
Source code in src/castlecraft_engineer/abstractions/query_bus.py
QueryHandler
¶
Bases: Generic[TQuery]
, ABC
Abstract base class for query handlers. Each handler is responsible for processing a specific type of query.
Source code in src/castlecraft_engineer/abstractions/query_handler.py
authorize(subject_id=None, permissions=[], *args, **kwargs)
async
¶
Optional pre-execution authorization check for the query.
NOTE: This method is NOT automatically called by the default QueryBus.
It serves as a convention or hook for developers to implement custom
pre-authorization logic if they choose to call it explicitly from within
their execute
method, or if they are using a custom bus implementation
that invokes it.
The recommended pattern for authorization with the default bus is to use
an injected AuthorizationService
within the execute
method.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
subject_id
|
Optional[str]
|
The ID of the subject. |
None
|
permissions
|
List[Permission]
|
A list of Permission objects representing the permissions granted to the subject. |
[]
|
*args
|
Any
|
Additional positional arguments. |
()
|
**kwargs
|
Any
|
Additional keyword arguments for context. |
{}
|
Returns: True if the subject has permission, False otherwise.
Source code in src/castlecraft_engineer/abstractions/query_handler.py
execute(query, *args, subject_id=None, permissions=[], **kwargs)
abstractmethod
async
¶
Handles the execution logic for the given query.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
query
|
TQuery
|
The query instance to be processed. |
required |
subject_id
|
Optional[str]
|
The ID of the subject attempting to execute the query. Optional. |
None
|
permissions
|
List[Permission]
|
The permissions associated with the subject. |
[]
|
Raises:
Type | Description |
---|---|
NotImplementedError
|
This method must be implemented by concrete subclasses. |