Dependency Injection & Service Location
Overview
The athomic-docs project manages dependencies and object lifecycles without relying on an external dependency injection framework. Instead, it employs a robust and clear approach using a combination of two classic design patterns:
- The Facade Pattern: The
Athomicclass acts as a central service locator, providing a single, unified entry point to all core framework services. - The Factory Pattern: Dedicated
Factoryclasses are responsible for creating and managing singleton instances of specific components, handling their unique dependencies internally.
This approach promotes loose coupling, simplifies testing through easy mocking, and makes the flow of dependency resolution explicit and easy to follow.
The Athomic Façade
The main entry point to the entire framework is the nala.athomic.facade.Athomic class. During application startup, this class is instantiated once. Its constructor (__init__) is responsible for wiring together the primary, high-level services.
# From: nala.athomic.facade.py
class Athomic:
def __init__(self, ...):
# ...
self.settings = settings or get_settings()
self.secrets_manager = SecretsManager(self.settings)
self.lifecycle_manager = LifecycleManager(...)
self.tracer = get_tracer(__name__)
self.plugin_manager: PluginManager = PluginManager()
# ...
The nala.athomic.provider module then makes this single Athomic instance globally accessible via the get_athomic_instance() function, allowing any part of the application to access core services like the LifecycleManager or PluginManager when needed.
The Factory Pattern
For most components within the Athomic Layer, a dedicated factory is responsible for its creation. This pattern provides several key advantages:
- Centralized Creation Logic: The logic for creating a complex object (e.g., a
ConnectionManagerthat needsDatabaseSettings) is encapsulated in one place. - Singleton Management: Factories typically manage a singleton instance of the object they create, ensuring resources like connection pools are shared efficiently.
- Simplified Dependency Injection: Components that need a dependency can simply call the factory's
create()method without needing to know how to construct it. - Testability: Factories usually include a
.clear()method, which is crucial for resetting state between tests and ensuring test isolation.
Example Flow
A great example is the MongoOutboxRepository. It needs a database connection to work. Here is how its dependencies are resolved:
- The application needs an
OutboxStorageProtocolinstance. - It calls
OutboxStorageFactory.create(). - The
OutboxStorageFactoryknows it needs a database connection. It callsconnection_manager_factory.create(). - The
ConnectionManagerFactorycreates the singletonConnectionManager. - The
OutboxStorageFactorythen asks theConnectionManagerfor the specific database connection (get_document_db(...)). - Finally, it injects the database connection into a new
MongoOutboxRepositoryinstance and returns it.
This chain of factory calls ensures that dependencies are resolved correctly and only when needed.
API Reference
Core Façade and Provider
nala.athomic.facade.Athomic
A Facade that provides a single, simple entry point to all of Athomic's services.
This class acts as the main interaction point for an application utilizing the Athomic framework. It manages the initialization, dependency injection, and lifecycle of all infrastructure components, such as secrets management, plugins, and background services.
Attributes:
| Name | Type | Description |
|---|---|---|
settings |
AppSettings
|
The validated application settings. |
secrets_manager |
SecretsManager
|
The manager responsible for resolving secrets. |
lifecycle_manager |
LifecycleManager
|
The orchestrator for the service lifecycle. |
plugin_manager |
PluginManager
|
The manager for the plugin system. |
tracer |
Tracer
|
The OpenTelemetry Tracer instance for this component. |
observability |
A namespace for observability components like loggers. |
__init__(domain_initializers_registrar=None, settings=None)
Initializes the Athomic layer.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
domain_initializers_registrar
|
Optional[Callable[[], None]]
|
A function from the application layer (e.g., API) that registers all business domain-specific initializers. |
None
|
settings
|
Optional[AppSettings]
|
An instance of application settings. If not provided, it will be loaded globally. Ideal for dependency injection in tests. |
None
|
shutdown()
async
Runs the graceful shutdown sequence for all services.
startup()
async
Runs the complete, ordered startup sequence for the application's infrastructure.
This method orchestrates the startup of services in the correct dependency order: 1. Resolves all secret references within the configuration. 2. Discovers and loads all available plugins. 3. Calls the 'on_athomic_startup' hook, allowing plugins to initialize. 4. Starts all registered services (e.g., database, messaging).
Raises:
| Type | Description |
|---|---|
RuntimeError
|
If any critical step in the startup sequence fails. |
nala.athomic.provider
clear_athomic_instance()
For use in tests, to ensure isolation.
get_athomic_instance()
Returns the singleton instance of the Athomic facade that has already been initialized.
Raises:
| Type | Description |
|---|---|
RuntimeError
|
If the instance has not yet been created and set. |
set_athomic_instance(instance)
Sets the global singleton instance of the Athomic facade. This function should be called ONLY ONCE during application startup.
Example Factories
nala.athomic.database.factory.ConnectionManagerFactory
Manages the singleton instance of the ConnectionManager. Ensures that only one instance of the ConnectionManager exists throughout the application's lifecycle.
clear()
async
Clears the singleton instance of the ConnectionManager. This is primarily used for test isolation.
create(settings=None)
Creates and returns the singleton instance of the ConnectionManager.
On the first call, it instantiates the ConnectionManager. Subsequent calls return the cached instance.
nala.athomic.serializer.factory.SerializerFactory
Factory for instantiating message serializers based on messaging backend. Falls back to BaseSerializer if no specific implementation is registered. Caches instances by backend name for singleton-like behavior per backend.
clear()
classmethod
Clears the singleton cache of serializer instances.
create(settings=None)
classmethod
Creates and returns a singleton instance of the configured SerializerProtocol by delegating to a registered creator.