neuroglia-python 0.6.20__tar.gz → 0.6.22__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 (121) hide show
  1. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/PKG-INFO +1 -1
  2. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/pyproject.toml +1 -1
  3. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/__init__.py +1 -1
  4. neuroglia_python-0.6.22/src/neuroglia/hosting/configuration/data_access_layer.py +292 -0
  5. neuroglia_python-0.6.20/src/neuroglia/hosting/configuration/data_access_layer.py +0 -57
  6. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/LICENSE +0 -0
  7. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/README.md +0 -0
  8. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/application/__init__.py +0 -0
  9. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/application/background_scheduler.py +0 -0
  10. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/coordination/__init__.py +0 -0
  11. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/coordination/abstractions.py +0 -0
  12. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/coordination/leader_election.py +0 -0
  13. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/coordination/redis_backend.py +0 -0
  14. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/core/__init__.py +0 -0
  15. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/core/module_loader.py +0 -0
  16. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/core/operation_result.py +0 -0
  17. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/core/problem_details.py +0 -0
  18. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/core/type_extensions.py +0 -0
  19. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/core/type_finder.py +0 -0
  20. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/core/type_registry.py +0 -0
  21. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/data/__init__.py +0 -0
  22. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/data/abstractions.py +0 -0
  23. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/data/exceptions.py +0 -0
  24. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/data/infrastructure/__init__.py +0 -0
  25. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/data/infrastructure/abstractions.py +0 -0
  26. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/data/infrastructure/event_sourcing/__init__.py +0 -0
  27. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/data/infrastructure/event_sourcing/abstractions.py +0 -0
  28. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/data/infrastructure/event_sourcing/event_sourcing_repository.py +0 -0
  29. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/data/infrastructure/event_sourcing/event_store/__init__.py +0 -0
  30. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/data/infrastructure/event_sourcing/event_store/event_store.py +0 -0
  31. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/data/infrastructure/event_sourcing/patches.py +0 -0
  32. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/data/infrastructure/event_sourcing/read_model_reconciliator.py +0 -0
  33. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/data/infrastructure/filesystem/__init__.py +0 -0
  34. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/data/infrastructure/filesystem/filesystem_repository.py +0 -0
  35. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/data/infrastructure/memory/__init__.py +0 -0
  36. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/data/infrastructure/memory/memory_repository.py +0 -0
  37. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/data/infrastructure/mongo/__init__.py +0 -0
  38. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/data/infrastructure/mongo/enhanced_mongo_repository.py +0 -0
  39. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/data/infrastructure/mongo/mongo_repository.py +0 -0
  40. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/data/infrastructure/mongo/motor_repository.py +0 -0
  41. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/data/infrastructure/mongo/serialization_helper.py +0 -0
  42. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/data/infrastructure/mongo/typed_mongo_query.py +0 -0
  43. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/data/infrastructure/resources/__init__.py +0 -0
  44. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/data/infrastructure/resources/in_memory_storage_backend.py +0 -0
  45. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/data/infrastructure/resources/postgres_resource_store.py +0 -0
  46. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/data/infrastructure/resources/redis_resource_store.py +0 -0
  47. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/data/infrastructure/resources/resource_repository.py +0 -0
  48. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/data/infrastructure/tracing_mixin.py +0 -0
  49. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/data/queries/generic/__init__.py +0 -0
  50. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/data/queries/generic/get_by_id_query.py +0 -0
  51. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/data/queries/generic/list_query.py +0 -0
  52. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/data/queryable.py +0 -0
  53. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/data/resources/__init__.py +0 -0
  54. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/data/resources/abstractions.py +0 -0
  55. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/data/resources/controller.py +0 -0
  56. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/data/resources/serializers/__init__.py +0 -0
  57. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/data/resources/serializers/xml_serializer.py +0 -0
  58. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/data/resources/serializers/yaml_serializer.py +0 -0
  59. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/data/resources/state_machine.py +0 -0
  60. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/data/resources/watcher.py +0 -0
  61. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/data/unit_of_work.py +0 -0
  62. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/dependency_injection/__init__.py +0 -0
  63. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/dependency_injection/service_provider.py +0 -0
  64. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/dependency_injection/service_provider_backup.py +0 -0
  65. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/eventing/__init__.py +0 -0
  66. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/eventing/cloud_events/__init__.py +0 -0
  67. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/eventing/cloud_events/cloud_event.py +0 -0
  68. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/eventing/cloud_events/decorators.py +0 -0
  69. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/eventing/cloud_events/infrastructure/__init__.py +0 -0
  70. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/eventing/cloud_events/infrastructure/cloud_event_bus.py +0 -0
  71. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/eventing/cloud_events/infrastructure/cloud_event_ingestor.py +0 -0
  72. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/eventing/cloud_events/infrastructure/cloud_event_middleware.py +0 -0
  73. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/eventing/cloud_events/infrastructure/cloud_event_publisher.py +0 -0
  74. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/eventing/tracing.py +0 -0
  75. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/expressions/__init__.py +0 -0
  76. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/expressions/javascript_expression_translator.py +0 -0
  77. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/extensions/cqrs_metrics_extensions.py +0 -0
  78. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/extensions/mediator_extensions.py +0 -0
  79. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/extensions/state_persistence_extensions.py +0 -0
  80. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/hosting/__init__.py +0 -0
  81. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/hosting/abstractions.py +0 -0
  82. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/hosting/web.py +0 -0
  83. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/integration/__init__.py +0 -0
  84. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/integration/cache_repository.py +0 -0
  85. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/integration/http_service_client.py +0 -0
  86. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/integration/models.py +0 -0
  87. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/logging/__init__.py +0 -0
  88. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/logging/logger.py +0 -0
  89. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/mapping/__init__.py +0 -0
  90. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/mapping/mapper.py +0 -0
  91. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/mediation/__init__.py +0 -0
  92. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/mediation/behaviors/domain_event_cloudevent_behavior.py +0 -0
  93. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/mediation/behaviors/domain_event_dispatching_middleware.py +0 -0
  94. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/mediation/mediator.py +0 -0
  95. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/mediation/metrics_middleware.py +0 -0
  96. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/mediation/pipeline_behavior.py +0 -0
  97. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/mediation/simple.py +0 -0
  98. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/mediation/tracing_middleware.py +0 -0
  99. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/mvc/__init__.py +0 -0
  100. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/mvc/controller_base.py +0 -0
  101. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/observability/__init__.py +0 -0
  102. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/observability/framework.py +0 -0
  103. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/observability/logging.py +0 -0
  104. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/observability/metrics.py +0 -0
  105. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/observability/otel_sdk.py +0 -0
  106. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/observability/settings.py +0 -0
  107. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/observability/tracing.py +0 -0
  108. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/py.typed +0 -0
  109. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/reactive/__init__.py +0 -0
  110. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/reactive/rx_async.py +0 -0
  111. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/serialization/__init__.py +0 -0
  112. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/serialization/abstractions.py +0 -0
  113. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/serialization/aggregate_serializer.py +0 -0
  114. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/serialization/json.py +0 -0
  115. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/utils/__init__.py +0 -0
  116. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/utils/camel_model.py +0 -0
  117. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/utils/case_conversion.py +0 -0
  118. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/validation/__init__.py +0 -0
  119. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/validation/business_rules.py +0 -0
  120. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/validation/exceptions.py +0 -0
  121. {neuroglia_python-0.6.20 → neuroglia_python-0.6.22}/src/neuroglia/validation/validators.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: neuroglia-python
3
- Version: 0.6.20
3
+ Version: 0.6.22
4
4
  Summary: Port from .NET to Python of the Neuroglia Framework
5
5
  License: Apache
6
6
  License-File: LICENSE
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "neuroglia-python"
3
- version = "0.6.20"
3
+ version = "0.6.22"
4
4
  description = "Port from .NET to Python of the Neuroglia Framework"
5
5
  authors = ["Charles d'Avernas <charles.davernas@neuroglia.io>", "Bruno van de Werve <bvandewe@gmail.com>"]
6
6
  license = "Apache"
@@ -62,7 +62,7 @@ __all__ = [
62
62
  ]
63
63
 
64
64
  # Framework metadata
65
- __version__ = "0.6.18"
65
+ __version__ = "0.6.21"
66
66
  __author__ = "Neuroglia Team"
67
67
  __email__ = "team@neuroglia.io"
68
68
  __license__ = "Apache"
@@ -0,0 +1,292 @@
1
+ import inspect
2
+ from collections.abc import Callable
3
+ from typing import TYPE_CHECKING, Optional
4
+
5
+ from neuroglia.core import ModuleLoader, TypeFinder
6
+ from neuroglia.data.abstractions import AggregateRoot
7
+ from neuroglia.data.infrastructure.event_sourcing.read_model_reconciliator import (
8
+ ReadModelConciliationOptions,
9
+ ReadModelReconciliator,
10
+ )
11
+ from neuroglia.data.queries.generic import GetByIdQueryHandler, ListQueryHandler
12
+ from neuroglia.hosting.abstractions import ApplicationBuilderBase, HostedService
13
+ from neuroglia.mediation.mediator import RequestHandler
14
+
15
+ if TYPE_CHECKING:
16
+ from neuroglia.data.infrastructure.event_sourcing.event_sourcing_repository import (
17
+ EventSourcingRepositoryOptions,
18
+ )
19
+
20
+
21
+ class DataAccessLayer:
22
+ class WriteModel:
23
+ """Represents a helper class used to configure an application's Write Model DAL
24
+
25
+ Supports two configuration patterns:
26
+ 1. Simplified: Pass options directly to constructor
27
+ 2. Custom: Pass custom repository_setup function to configure()
28
+
29
+ Examples:
30
+ # Simple configuration with default options
31
+ DataAccessLayer.WriteModel().configure(builder, ["domain.entities"])
32
+
33
+ # With custom delete mode
34
+ from neuroglia.data.infrastructure.event_sourcing.abstractions import DeleteMode
35
+ from neuroglia.data.infrastructure.event_sourcing.event_sourcing_repository import (
36
+ EventSourcingRepositoryOptions
37
+ )
38
+
39
+ DataAccessLayer.WriteModel(
40
+ options=EventSourcingRepositoryOptions(delete_mode=DeleteMode.HARD)
41
+ ).configure(builder, ["domain.entities"])
42
+
43
+ # Custom factory (advanced, backwards compatible)
44
+ def custom_setup(builder_, entity_type, key_type):
45
+ # Custom configuration logic
46
+ pass
47
+ DataAccessLayer.WriteModel().configure(
48
+ builder, ["domain.entities"], custom_setup
49
+ )
50
+ """
51
+
52
+ def __init__(self, options: Optional["EventSourcingRepositoryOptions"] = None):
53
+ """Initialize WriteModel configuration
54
+
55
+ Args:
56
+ options: Optional repository options (e.g., delete_mode).
57
+ If not provided, default options will be used.
58
+ """
59
+ self._options = options
60
+
61
+ def configure(self, builder: ApplicationBuilderBase, modules: list[str], repository_setup: Optional[Callable[[ApplicationBuilderBase, type, type], None]] = None) -> ApplicationBuilderBase:
62
+ """Configures the application's Write Model DAL, scanning for aggregate root types within the specified modules
63
+
64
+ Args:
65
+ builder (ApplicationBuilderBase): the application builder to configure
66
+ modules (List[str]): a list containing the names of the modules to scan for aggregate root types
67
+ repository_setup (Optional[Callable[[ApplicationBuilderBase, Type, Type], None]]):
68
+ Optional custom function to setup repositories. If provided, takes precedence over options.
69
+ If not provided, uses simplified configuration with options (if any).
70
+
71
+ Returns:
72
+ ApplicationBuilderBase: The configured builder
73
+
74
+ Raises:
75
+ ImportError: If EventSourcingRepository cannot be imported
76
+ """
77
+ # If custom setup provided, use it (backwards compatible)
78
+ if repository_setup is not None:
79
+ for module in [ModuleLoader.load(module_name) for module_name in modules]:
80
+ for aggregate_type in TypeFinder.get_types(module, lambda cls: inspect.isclass(cls) and issubclass(cls, AggregateRoot) and not cls == AggregateRoot):
81
+ key_type = str # todo: reflect from DTO base type
82
+ repository_setup(builder, aggregate_type, key_type)
83
+ return builder
84
+
85
+ # Otherwise use simplified configuration with options
86
+ return self._configure_with_options(builder, modules)
87
+
88
+ def _configure_with_options(self, builder: ApplicationBuilderBase, modules: list[str]) -> ApplicationBuilderBase:
89
+ """Configure repositories using simplified options pattern
90
+
91
+ Args:
92
+ builder: The application builder
93
+ modules: List of module names to scan for aggregates
94
+
95
+ Returns:
96
+ The configured builder
97
+ """
98
+ from neuroglia.data.infrastructure.abstractions import Repository
99
+ from neuroglia.data.infrastructure.event_sourcing.abstractions import (
100
+ Aggregator,
101
+ EventStore,
102
+ )
103
+ from neuroglia.data.infrastructure.event_sourcing.event_sourcing_repository import (
104
+ EventSourcingRepository,
105
+ EventSourcingRepositoryOptions,
106
+ )
107
+ from neuroglia.dependency_injection import ServiceProvider
108
+ from neuroglia.mediation import Mediator
109
+
110
+ # Discover and configure each aggregate type
111
+ for module in [ModuleLoader.load(module_name) for module_name in modules]:
112
+ for aggregate_type in TypeFinder.get_types(
113
+ module,
114
+ lambda cls: inspect.isclass(cls) and issubclass(cls, AggregateRoot) and not cls == AggregateRoot,
115
+ ):
116
+ key_type = str # todo: reflect from DTO base type
117
+
118
+ # Create type-specific options if global options provided
119
+ typed_options = None
120
+ if self._options:
121
+ typed_options = EventSourcingRepositoryOptions[aggregate_type, key_type]( # type: ignore
122
+ delete_mode=self._options.delete_mode,
123
+ soft_delete_method_name=self._options.soft_delete_method_name,
124
+ )
125
+
126
+ # Create factory function with proper closure
127
+ def make_factory(et, kt, opts):
128
+ def repository_factory(sp: ServiceProvider):
129
+ return EventSourcingRepository[et, kt]( # type: ignore
130
+ eventstore=sp.get_required_service(EventStore),
131
+ aggregator=sp.get_required_service(Aggregator),
132
+ mediator=sp.get_service(Mediator),
133
+ options=opts,
134
+ )
135
+
136
+ return repository_factory
137
+
138
+ # Register repository with factory
139
+ builder.services.add_singleton(
140
+ Repository[aggregate_type, key_type], # type: ignore
141
+ implementation_factory=make_factory(aggregate_type, key_type, typed_options),
142
+ )
143
+
144
+ return builder
145
+
146
+ class ReadModel:
147
+ """Represents a helper class used to configure an application's Read Model DAL
148
+
149
+ Supports two configuration patterns:
150
+ 1. Simplified: Pass database_name directly to constructor
151
+ 2. Custom: Pass custom repository_setup function to configure()
152
+
153
+ Examples:
154
+ # Simple configuration with database name
155
+ DataAccessLayer.ReadModel(database_name="myapp").configure(
156
+ builder, ["integration.models"]
157
+ )
158
+
159
+ # Custom factory (advanced, backwards compatible)
160
+ def custom_setup(builder_, entity_type, key_type):
161
+ # Custom configuration logic
162
+ pass
163
+ DataAccessLayer.ReadModel().configure(
164
+ builder, ["integration.models"], custom_setup
165
+ )
166
+ """
167
+
168
+ def __init__(self, database_name: Optional[str] = None):
169
+ """Initialize ReadModel configuration
170
+
171
+ Args:
172
+ database_name: Optional database name for MongoDB repositories.
173
+ If not provided, custom repository_setup must be used.
174
+ """
175
+ self._database_name = database_name
176
+
177
+ def configure(
178
+ self,
179
+ builder: ApplicationBuilderBase,
180
+ modules: list[str],
181
+ repository_setup: Optional[Callable[[ApplicationBuilderBase, type, type], None]] = None,
182
+ ) -> ApplicationBuilderBase:
183
+ """Configures the application's Read Model DAL, scanning for types marked with the 'queryable' decorator within the specified modules
184
+
185
+ Args:
186
+ builder (ApplicationBuilderBase): the application builder to configure
187
+ modules (List[str]): a list containing the names of the modules to scan for types decorated with 'queryable'
188
+ repository_setup (Optional[Callable[[ApplicationBuilderBase, Type, Type], None]]):
189
+ Optional custom function to setup repositories. If provided, takes precedence over database_name.
190
+ If not provided, uses simplified configuration with database_name.
191
+
192
+ Returns:
193
+ ApplicationBuilderBase: The configured builder
194
+
195
+ Raises:
196
+ ValueError: If consumer_group not specified in settings
197
+ ValueError: If neither repository_setup nor database_name is provided
198
+ """
199
+ consumer_group = builder.settings.consumer_group
200
+ if not consumer_group:
201
+ raise ValueError("Cannot configure Read Model DAL: consumer group not specified in application settings")
202
+ builder.services.add_singleton(ReadModelConciliationOptions, singleton=ReadModelConciliationOptions(consumer_group))
203
+ builder.services.add_singleton(HostedService, ReadModelReconciliator)
204
+
205
+ # If custom setup provided, use it (backwards compatible)
206
+ if repository_setup is not None:
207
+ for module in [ModuleLoader.load(module_name) for module_name in modules]:
208
+ for queryable_type in TypeFinder.get_types(module, lambda cls: inspect.isclass(cls) and hasattr(cls, "__queryable__")):
209
+ key_type = str # todo: reflect from DTO base type
210
+ repository_setup(builder, queryable_type, key_type)
211
+ builder.services.add_transient(RequestHandler, GetByIdQueryHandler[queryable_type, key_type]) # type: ignore
212
+ builder.services.add_transient(RequestHandler, ListQueryHandler[queryable_type, key_type]) # type: ignore
213
+ return builder
214
+
215
+ # Otherwise use simplified configuration with database_name
216
+ return self._configure_with_database_name(builder, modules)
217
+
218
+ def _configure_with_database_name(
219
+ self,
220
+ builder: ApplicationBuilderBase,
221
+ modules: list[str],
222
+ ) -> ApplicationBuilderBase:
223
+ """Configure repositories using simplified database_name pattern
224
+
225
+ Args:
226
+ builder: The application builder
227
+ modules: List of module names to scan for queryable types
228
+
229
+ Returns:
230
+ The configured builder
231
+
232
+ Raises:
233
+ ValueError: If database_name was not provided
234
+ """
235
+ if not self._database_name:
236
+ raise ValueError("Cannot configure Read Model with simplified API: " "database_name not provided. Either pass database_name to ReadModel() " "or use custom repository_setup function.")
237
+
238
+ from pymongo import MongoClient
239
+
240
+ from neuroglia.data.infrastructure.abstractions import (
241
+ QueryableRepository,
242
+ Repository,
243
+ )
244
+ from neuroglia.data.infrastructure.mongo.mongo_repository import (
245
+ MongoRepository,
246
+ MongoRepositoryOptions,
247
+ )
248
+ from neuroglia.dependency_injection import ServiceProvider
249
+
250
+ # Get MongoDB connection string
251
+ connection_string_name = "mongo"
252
+ connection_string = builder.settings.connection_strings.get(connection_string_name, None)
253
+ if connection_string is None:
254
+ raise ValueError(f"Missing '{connection_string_name}' connection string in application settings")
255
+
256
+ # Register MongoClient singleton (shared across all repositories)
257
+ builder.services.try_add_singleton(MongoClient, singleton=MongoClient(connection_string))
258
+
259
+ # Discover and configure each queryable type
260
+ for module in [ModuleLoader.load(module_name) for module_name in modules]:
261
+ for queryable_type in TypeFinder.get_types(module, lambda cls: inspect.isclass(cls) and hasattr(cls, "__queryable__")):
262
+ key_type = str # todo: reflect from DTO base type
263
+
264
+ # Register options for this entity type
265
+ builder.services.try_add_singleton(
266
+ MongoRepositoryOptions[queryable_type, key_type], # type: ignore
267
+ singleton=MongoRepositoryOptions[queryable_type, key_type](self._database_name), # type: ignore
268
+ )
269
+
270
+ # Register repository
271
+ builder.services.try_add_singleton(
272
+ Repository[queryable_type, key_type], # type: ignore
273
+ MongoRepository[queryable_type, key_type], # type: ignore
274
+ )
275
+
276
+ # Register queryable repository alias
277
+ def make_queryable_factory(qt, kt):
278
+ def queryable_factory(provider: ServiceProvider):
279
+ return provider.get_required_service(Repository[qt, kt]) # type: ignore
280
+
281
+ return queryable_factory
282
+
283
+ builder.services.try_add_singleton(
284
+ QueryableRepository[queryable_type, key_type], # type: ignore
285
+ implementation_factory=make_queryable_factory(queryable_type, key_type),
286
+ )
287
+
288
+ # Register query handlers
289
+ builder.services.add_transient(RequestHandler, GetByIdQueryHandler[queryable_type, key_type]) # type: ignore
290
+ builder.services.add_transient(RequestHandler, ListQueryHandler[queryable_type, key_type]) # type: ignore
291
+
292
+ return builder
@@ -1,57 +0,0 @@
1
- import inspect
2
- from collections.abc import Callable
3
-
4
- from neuroglia.core import ModuleLoader, TypeFinder
5
- from neuroglia.data.abstractions import AggregateRoot
6
- from neuroglia.data.infrastructure.event_sourcing.read_model_reconciliator import (
7
- ReadModelConciliationOptions,
8
- ReadModelReconciliator,
9
- )
10
- from neuroglia.data.queries.generic import GetByIdQueryHandler, ListQueryHandler
11
- from neuroglia.hosting.abstractions import ApplicationBuilderBase, HostedService
12
- from neuroglia.mediation.mediator import RequestHandler
13
-
14
-
15
- class DataAccessLayer:
16
- class WriteModel:
17
- """Represents an helper class used to configure an application's Write Model DAL"""
18
-
19
- @staticmethod
20
- def configure(builder: ApplicationBuilderBase, modules: list[str], repository_setup: Callable[[ApplicationBuilderBase, type, type], None]) -> ApplicationBuilderBase:
21
- """Configures the application's Write Model DAL, scanning for aggregate root types within the specified modules
22
-
23
- Args:
24
- builder (ApplicationBuilderBase): the application builder to configure
25
- modules (List[str]): a list containing the names of the modules to scan for aggregate root types
26
- repository_setup (Callable[[ApplicationBuilderBase, Type, Type], None]): a function used to setup the repository for the specified entity and key types
27
- """
28
- for module in [ModuleLoader.load(module_name) for module_name in modules]:
29
- for aggregate_type in TypeFinder.get_types(module, lambda cls: inspect.isclass(cls) and issubclass(cls, AggregateRoot) and not cls == AggregateRoot):
30
- key_type = str # todo: reflect from DTO base type
31
- repository_setup(builder, aggregate_type, key_type)
32
- return builder
33
-
34
- class ReadModel:
35
- """Represents an helper class used to configure an application's Read Model DAL"""
36
-
37
- @staticmethod
38
- def configure(builder: ApplicationBuilderBase, modules: list[str], repository_setup: Callable[[ApplicationBuilderBase, type, type], None]) -> ApplicationBuilderBase:
39
- """Configures the application's Read Model DAL, scanning for types marked with the 'queryable' decorator within the specified modules
40
-
41
- Args:
42
- builder (ApplicationBuilderBase): the application builder to configure
43
- modules (List[str]): a list containing the names of the modules to scan for types decorated with 'queryable'
44
- repository_setup (Callable[[ApplicationBuilderBase, Type, Type], None]): a function used to setup the repository for the specified entity and key types
45
- """
46
- consumer_group = builder.settings.consumer_group
47
- if not consumer_group:
48
- raise ValueError("Cannot configure Read Model DAL: consumer group not specified in application settings")
49
- builder.services.add_singleton(ReadModelConciliationOptions, singleton=ReadModelConciliationOptions(consumer_group))
50
- builder.services.add_singleton(HostedService, ReadModelReconciliator)
51
- for module in [ModuleLoader.load(module_name) for module_name in modules]:
52
- for queryable_type in TypeFinder.get_types(module, lambda cls: inspect.isclass(cls) and hasattr(cls, "__queryable__")):
53
- key_type = str # todo: reflect from DTO base type
54
- repository_setup(builder, queryable_type, key_type)
55
- builder.services.add_transient(RequestHandler, GetByIdQueryHandler[queryable_type, key_type])
56
- builder.services.add_transient(RequestHandler, ListQueryHandler[queryable_type, key_type])
57
- return builder