neuroglia-python 0.6.12__tar.gz → 0.6.13__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 (119) hide show
  1. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/PKG-INFO +1 -1
  2. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/pyproject.toml +1 -1
  3. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/__init__.py +1 -1
  4. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/data/infrastructure/event_sourcing/event_sourcing_repository.py +13 -9
  5. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/data/infrastructure/event_sourcing/read_model_reconciliator.py +29 -10
  6. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/data/infrastructure/mongo/mongo_repository.py +12 -4
  7. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/data/queryable.py +1 -1
  8. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/LICENSE +0 -0
  9. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/README.md +0 -0
  10. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/application/__init__.py +0 -0
  11. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/application/background_scheduler.py +0 -0
  12. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/coordination/__init__.py +0 -0
  13. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/coordination/abstractions.py +0 -0
  14. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/coordination/leader_election.py +0 -0
  15. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/coordination/redis_backend.py +0 -0
  16. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/core/__init__.py +0 -0
  17. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/core/module_loader.py +0 -0
  18. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/core/operation_result.py +0 -0
  19. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/core/problem_details.py +0 -0
  20. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/core/type_extensions.py +0 -0
  21. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/core/type_finder.py +0 -0
  22. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/core/type_registry.py +0 -0
  23. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/data/__init__.py +0 -0
  24. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/data/abstractions.py +0 -0
  25. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/data/exceptions.py +0 -0
  26. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/data/infrastructure/__init__.py +0 -0
  27. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/data/infrastructure/abstractions.py +0 -0
  28. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/data/infrastructure/event_sourcing/__init__.py +0 -0
  29. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/data/infrastructure/event_sourcing/abstractions.py +0 -0
  30. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/data/infrastructure/event_sourcing/event_store/__init__.py +0 -0
  31. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/data/infrastructure/event_sourcing/event_store/event_store.py +0 -0
  32. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/data/infrastructure/filesystem/__init__.py +0 -0
  33. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/data/infrastructure/filesystem/filesystem_repository.py +0 -0
  34. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/data/infrastructure/memory/__init__.py +0 -0
  35. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/data/infrastructure/memory/memory_repository.py +0 -0
  36. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/data/infrastructure/mongo/__init__.py +0 -0
  37. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/data/infrastructure/mongo/enhanced_mongo_repository.py +0 -0
  38. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/data/infrastructure/mongo/motor_repository.py +0 -0
  39. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/data/infrastructure/mongo/serialization_helper.py +0 -0
  40. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/data/infrastructure/mongo/typed_mongo_query.py +0 -0
  41. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/data/infrastructure/resources/__init__.py +0 -0
  42. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/data/infrastructure/resources/in_memory_storage_backend.py +0 -0
  43. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/data/infrastructure/resources/postgres_resource_store.py +0 -0
  44. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/data/infrastructure/resources/redis_resource_store.py +0 -0
  45. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/data/infrastructure/resources/resource_repository.py +0 -0
  46. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/data/infrastructure/tracing_mixin.py +0 -0
  47. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/data/queries/generic/__init__.py +0 -0
  48. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/data/queries/generic/get_by_id_query.py +0 -0
  49. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/data/queries/generic/list_query.py +0 -0
  50. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/data/resources/__init__.py +0 -0
  51. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/data/resources/abstractions.py +0 -0
  52. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/data/resources/controller.py +0 -0
  53. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/data/resources/serializers/__init__.py +0 -0
  54. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/data/resources/serializers/xml_serializer.py +0 -0
  55. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/data/resources/serializers/yaml_serializer.py +0 -0
  56. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/data/resources/state_machine.py +0 -0
  57. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/data/resources/watcher.py +0 -0
  58. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/data/unit_of_work.py +0 -0
  59. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/dependency_injection/__init__.py +0 -0
  60. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/dependency_injection/service_provider.py +0 -0
  61. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/dependency_injection/service_provider_backup.py +0 -0
  62. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/eventing/__init__.py +0 -0
  63. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/eventing/cloud_events/__init__.py +0 -0
  64. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/eventing/cloud_events/cloud_event.py +0 -0
  65. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/eventing/cloud_events/decorators.py +0 -0
  66. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/eventing/cloud_events/infrastructure/__init__.py +0 -0
  67. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/eventing/cloud_events/infrastructure/cloud_event_bus.py +0 -0
  68. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/eventing/cloud_events/infrastructure/cloud_event_ingestor.py +0 -0
  69. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/eventing/cloud_events/infrastructure/cloud_event_middleware.py +0 -0
  70. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/eventing/cloud_events/infrastructure/cloud_event_publisher.py +0 -0
  71. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/eventing/tracing.py +0 -0
  72. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/expressions/__init__.py +0 -0
  73. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/expressions/javascript_expression_translator.py +0 -0
  74. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/extensions/cqrs_metrics_extensions.py +0 -0
  75. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/extensions/mediator_extensions.py +0 -0
  76. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/extensions/state_persistence_extensions.py +0 -0
  77. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/hosting/__init__.py +0 -0
  78. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/hosting/abstractions.py +0 -0
  79. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/hosting/configuration/data_access_layer.py +0 -0
  80. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/hosting/web.py +0 -0
  81. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/integration/__init__.py +0 -0
  82. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/integration/cache_repository.py +0 -0
  83. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/integration/http_service_client.py +0 -0
  84. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/integration/models.py +0 -0
  85. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/logging/__init__.py +0 -0
  86. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/logging/logger.py +0 -0
  87. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/mapping/__init__.py +0 -0
  88. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/mapping/mapper.py +0 -0
  89. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/mediation/__init__.py +0 -0
  90. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/mediation/behaviors/domain_event_cloudevent_behavior.py +0 -0
  91. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/mediation/behaviors/domain_event_dispatching_middleware.py +0 -0
  92. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/mediation/mediator.py +0 -0
  93. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/mediation/metrics_middleware.py +0 -0
  94. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/mediation/pipeline_behavior.py +0 -0
  95. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/mediation/simple.py +0 -0
  96. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/mediation/tracing_middleware.py +0 -0
  97. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/mvc/__init__.py +0 -0
  98. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/mvc/controller_base.py +0 -0
  99. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/observability/__init__.py +0 -0
  100. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/observability/framework.py +0 -0
  101. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/observability/logging.py +0 -0
  102. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/observability/metrics.py +0 -0
  103. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/observability/otel_sdk.py +0 -0
  104. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/observability/settings.py +0 -0
  105. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/observability/tracing.py +0 -0
  106. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/py.typed +0 -0
  107. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/reactive/__init__.py +0 -0
  108. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/reactive/rx_async.py +0 -0
  109. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/serialization/__init__.py +0 -0
  110. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/serialization/abstractions.py +0 -0
  111. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/serialization/aggregate_serializer.py +0 -0
  112. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/serialization/json.py +0 -0
  113. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/utils/__init__.py +0 -0
  114. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/utils/camel_model.py +0 -0
  115. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/utils/case_conversion.py +0 -0
  116. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/validation/__init__.py +0 -0
  117. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/validation/business_rules.py +0 -0
  118. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/src/neuroglia/validation/exceptions.py +0 -0
  119. {neuroglia_python-0.6.12 → neuroglia_python-0.6.13}/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.12
3
+ Version: 0.6.13
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.12"
3
+ version = "0.6.13"
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.11"
65
+ __version__ = "0.6.13"
66
66
  __author__ = "Neuroglia Team"
67
67
  __email__ = "team@neuroglia.io"
68
68
  __license__ = "Apache"
@@ -1,5 +1,5 @@
1
1
  from dataclasses import dataclass
2
- from typing import Generic, Optional
2
+ from typing import TYPE_CHECKING, Generic, Optional
3
3
 
4
4
  from neuroglia.data.abstractions import DomainEvent, TAggregate, TKey
5
5
  from neuroglia.data.infrastructure.abstractions import Repository
@@ -11,6 +11,9 @@ from neuroglia.data.infrastructure.event_sourcing.abstractions import (
11
11
  )
12
12
  from neuroglia.hosting.abstractions import ApplicationBuilderBase
13
13
 
14
+ if TYPE_CHECKING:
15
+ from neuroglia.mediation.mediator import Mediator
16
+
14
17
 
15
18
  @dataclass
16
19
  class EventSourcingRepositoryOptions(Generic[TAggregate, TKey]):
@@ -20,8 +23,9 @@ class EventSourcingRepositoryOptions(Generic[TAggregate, TKey]):
20
23
  class EventSourcingRepository(Generic[TAggregate, TKey], Repository[TAggregate, TKey]):
21
24
  """Represents an event sourcing repository implementation"""
22
25
 
23
- def __init__(self, eventstore: EventStore, aggregator: Aggregator):
26
+ def __init__(self, eventstore: EventStore, aggregator: Aggregator, mediator: Optional["Mediator"] = None):
24
27
  """Initialize a new event sourcing repository"""
28
+ super().__init__(mediator) # Pass mediator to base class for event publishing
25
29
  self._eventstore = eventstore
26
30
  self._aggregator = aggregator
27
31
 
@@ -40,33 +44,33 @@ class EventSourcingRepository(Generic[TAggregate, TKey], Repository[TAggregate,
40
44
  events = await self._eventstore.read_async(stream_id, StreamReadDirection.FORWARDS, 0)
41
45
  return self._aggregator.aggregate(events, self.__orig_class__.__args__[0])
42
46
 
43
- async def add_async(self, aggregate: TAggregate) -> TAggregate:
47
+ async def _do_add_async(self, aggregate: TAggregate) -> TAggregate:
44
48
  """Adds and persists the specified aggregate"""
45
49
  stream_id = self._build_stream_id_for(aggregate.id())
46
50
  events = aggregate._pending_events
47
51
  if len(events) < 1:
48
- raise Exception()
52
+ raise Exception("No pending events to persist")
49
53
  encoded_events = [self._encode_event(e) for e in events]
50
54
  await self._eventstore.append_async(stream_id, encoded_events)
51
55
  aggregate.state.state_version = events[-1].aggregate_version
52
56
  aggregate.clear_pending_events()
53
57
  return aggregate
54
58
 
55
- async def update_async(self, aggregate: TAggregate) -> TAggregate:
56
- """Perists the changes made to the specified aggregate"""
59
+ async def _do_update_async(self, aggregate: TAggregate) -> TAggregate:
60
+ """Persists the changes made to the specified aggregate"""
57
61
  stream_id = self._build_stream_id_for(aggregate.id())
58
62
  events = aggregate._pending_events
59
63
  if len(events) < 1:
60
- raise Exception()
64
+ raise Exception("No pending events to persist")
61
65
  encoded_events = [self._encode_event(e) for e in events]
62
66
  await self._eventstore.append_async(stream_id, encoded_events, aggregate.state.state_version)
63
67
  aggregate.state.state_version = events[-1].aggregate_version
64
68
  aggregate.clear_pending_events()
65
69
  return aggregate
66
70
 
67
- async def remove_async(self, id: TKey) -> None:
71
+ async def _do_remove_async(self, id: TKey) -> None:
68
72
  """Removes the aggregate root with the specified key, if any"""
69
- raise NotImplementedError()
73
+ raise NotImplementedError("Event sourcing repositories do not support hard deletes")
70
74
 
71
75
  def _build_stream_id_for(self, aggregate_id: TKey):
72
76
  """Builds a new stream id for the specified aggregate"""
@@ -1,8 +1,14 @@
1
1
  import asyncio
2
- from dataclasses import dataclass
3
2
  import logging
3
+ from dataclasses import dataclass
4
+
4
5
  from rx.core.typing import Disposable
5
- from neuroglia.data.infrastructure.event_sourcing.abstractions import AckableEventRecord, EventRecord, EventStore, EventStoreOptions
6
+
7
+ from neuroglia.data.infrastructure.event_sourcing.abstractions import (
8
+ EventRecord,
9
+ EventStore,
10
+ EventStoreOptions,
11
+ )
6
12
  from neuroglia.dependency_injection.service_provider import ServiceProviderBase
7
13
  from neuroglia.hosting.abstractions import HostedService
8
14
  from neuroglia.mediation.mediator import Mediator
@@ -11,25 +17,25 @@ from neuroglia.reactive import AsyncRx
11
17
 
12
18
  @dataclass
13
19
  class ReadModelConciliationOptions:
14
- ''' Represents the options used to configure the application's read model reconciliation features '''
20
+ """Represents the options used to configure the application's read model reconciliation features"""
15
21
 
16
22
  consumer_group: str
17
- ''' Gets the name of the group of consumers the application's read model is maintained by '''
23
+ """ Gets the name of the group of consumers the application's read model is maintained by """
18
24
 
19
25
 
20
26
  class ReadModelReconciliator(HostedService):
21
- ''' Represents the service used to reconciliate the read model by streaming and handling events recorded on the application's event store '''
27
+ """Represents the service used to reconciliate the read model by streaming and handling events recorded on the application's event store"""
22
28
 
23
29
  _service_provider: ServiceProviderBase
24
- ''' Gets the current service provider '''
30
+ """ Gets the current service provider """
25
31
 
26
32
  _mediator: Mediator
27
33
 
28
34
  _event_store_options: EventStoreOptions
29
- ''' Gets the options used to configure the event store '''
35
+ """ Gets the options used to configure the event store """
30
36
 
31
37
  _event_store: EventStore
32
- ''' Gets the service used to persist and stream domain events '''
38
+ """ Gets the service used to persist and stream domain events """
33
39
 
34
40
  _subscription: Disposable
35
41
 
@@ -46,8 +52,21 @@ class ReadModelReconciliator(HostedService):
46
52
  self._subscription.dispose()
47
53
 
48
54
  async def subscribe_async(self):
49
- observable = await self._event_store.observe_async(f'$ce-{self._event_store_options.database_name}', self._event_store_options.consumer_group)
50
- self._subscription = AsyncRx.subscribe(observable, lambda e: asyncio.run(self.on_event_record_stream_next_async(e)))
55
+ observable = await self._event_store.observe_async(f"$ce-{self._event_store_options.database_name}", self._event_store_options.consumer_group)
56
+
57
+ # Get the current event loop to schedule tasks on
58
+ loop = asyncio.get_event_loop()
59
+
60
+ def on_next(e):
61
+ """Schedule the async handler on the main event loop without closing it."""
62
+ try:
63
+ # Use call_soon_threadsafe to schedule the coroutine on the main loop
64
+ # This prevents creating/closing new event loops which breaks Motor
65
+ loop.call_soon_threadsafe(lambda: asyncio.create_task(self.on_event_record_stream_next_async(e)))
66
+ except RuntimeError as ex:
67
+ logging.warning(f"Event loop closed, skipping event: {type(e.data).__name__ if hasattr(e, 'data') else 'unknown'} - {ex}")
68
+
69
+ self._subscription = AsyncRx.subscribe(observable, on_next)
51
70
 
52
71
  async def on_event_record_stream_next_async(self, e: EventRecord):
53
72
  try:
@@ -2,7 +2,7 @@ import ast
2
2
  from ast import NodeVisitor, expr
3
3
  from dataclasses import dataclass
4
4
  from inspect import isclass
5
- from typing import Any, Generic, Optional
5
+ from typing import TYPE_CHECKING, Any, Generic, List, Optional
6
6
 
7
7
  import pymongo
8
8
  from pymongo import MongoClient
@@ -23,6 +23,9 @@ from neuroglia.expressions.javascript_expression_translator import (
23
23
  from neuroglia.hosting.abstractions import ApplicationBuilderBase
24
24
  from neuroglia.serialization.json import JsonSerializer
25
25
 
26
+ if TYPE_CHECKING:
27
+ from neuroglia.mediation.mediator import Mediator
28
+
26
29
 
27
30
  @dataclass
28
31
  class MongoRepositoryOptions(Generic[TEntity, TKey]):
@@ -130,8 +133,10 @@ class MongoRepository(Generic[TEntity, TKey], QueryableRepository[TEntity, TKey]
130
133
  options: MongoRepositoryOptions[TEntity, TKey],
131
134
  mongo_client: MongoClient,
132
135
  serializer: JsonSerializer,
136
+ mediator: Optional["Mediator"] = None,
133
137
  ):
134
138
  """Initializes a new Mongo repository"""
139
+ super().__init__(mediator) # Pass mediator to base Repository class
135
140
  self._options = options
136
141
  self._mongo_client = mongo_client
137
142
  self._mongo_database = self._mongo_client[self._options.database_name]
@@ -209,7 +214,8 @@ class MongoRepository(Generic[TEntity, TKey], QueryableRepository[TEntity, TKey]
209
214
  entity = self._serializer.deserialize_from_text(json, self._get_entity_type())
210
215
  return entity
211
216
 
212
- async def add_async(self, entity: TEntity) -> TEntity:
217
+ async def _do_add_async(self, entity: TEntity) -> TEntity:
218
+ """Template method implementation for adding an entity to MongoDB"""
213
219
  entity_id = self._get_id(entity)
214
220
  if entity_id is None:
215
221
  raise Exception(f"Cannot add {self._get_entity_type().__name__} without an ID")
@@ -220,7 +226,8 @@ class MongoRepository(Generic[TEntity, TKey], QueryableRepository[TEntity, TKey]
220
226
  self._get_mongo_collection().insert_one(attributes_dictionary)
221
227
  return entity
222
228
 
223
- async def update_async(self, entity: TEntity) -> TEntity:
229
+ async def _do_update_async(self, entity: TEntity) -> TEntity:
230
+ """Template method implementation for updating an entity in MongoDB"""
224
231
  entity_id = self._get_id(entity)
225
232
  if entity_id is None:
226
233
  raise Exception(f"Cannot update {self._get_entity_type().__name__} without an ID")
@@ -235,7 +242,8 @@ class MongoRepository(Generic[TEntity, TKey], QueryableRepository[TEntity, TKey]
235
242
  self._get_mongo_collection().replace_one(query_filter, attributes_dictionary)
236
243
  return entity
237
244
 
238
- async def remove_async(self, id: TKey) -> None:
245
+ async def _do_remove_async(self, id: TKey) -> None:
246
+ """Template method implementation for removing an entity from MongoDB"""
239
247
  if not await self.contains_async(id) is not None:
240
248
  raise Exception(f"Failed to find a {self._get_entity_type().__name__} with the specified id '{id}'")
241
249
  self._get_mongo_collection().delete_one({"id": id})
@@ -4,7 +4,7 @@ import os
4
4
  from abc import ABC, abstractclassmethod
5
5
  from ast import Attribute, Name, NodeTransformer, expr
6
6
  from collections.abc import Callable
7
- from typing import Any, Generic, Optional, TypeVar
7
+ from typing import Any, Generic, List, Optional, TypeVar
8
8
 
9
9
  T = TypeVar("T")
10
10
  """ Represents the type of data associated to a queryable """