Registry Pattern
The Registry
pattern, implemented by IRegistry
and BaseRegistry
, provides a way to store and retrieve objects or configuration templates using a simple string name.
It’s often used as the data source for a Factory.
Imagine needing different sets of configuration for different environments (dev, staging, prod) or different types of resources (small server, large server). A Registry is ideal for managing these named variations.
Key Features & Examples
-
Registration (
register
/registerMany
): Store items. Each item must have aname: string
property that serves as its unique key.src/config-registry.tsimport { createRegistry, type IRegistry } from '@elsikora/cladi'; // Define the structure of our configuration objects interface ServerConfig { name: string; // Mandatory for the registry key cpu: number; memory: string; storage: string; region: 'us-east-1' | 'eu-west-1'; } const registry: IRegistry<ServerConfig> = createRegistry<ServerConfig>({}); // Logger can be passed in options // Register different server configurations registry.register({ name: "standard-dev", cpu: 2, memory: "4Gi", storage: "50Gi", region: "us-east-1", }); registry.register({ name: "high-mem-prod", cpu: 8, memory: "32Gi", storage: "500Gi", region: "eu-west-1", }); console.log(`Registry contains 'standard-dev': ${registry.has("standard-dev")}`); // true
-
Retrieval (
get
/getMany
/getAll
): Fetch items by their registered name(s).getAll
retrieves all registered items.src/retrieve-configs.ts// Assuming registry from previous example const devConfig = registry.get("standard-dev"); console.log(`Dev CPU: ${devConfig?.cpu}`); // 2 const prodConfig = registry.get("high-mem-prod"); console.log(`Prod Memory: ${prodConfig?.memory}`); // "32Gi" const allConfigs = registry.getAll(); console.log(`Total configs registered: ${allConfigs.length}`); // 2 const specificConfigs = registry.getMany(["standard-dev", "non-existent"]); console.log(`Found specific configs: ${specificConfigs.length}`); // 1 (only standard-dev)
-
Checking Existence (
has
): Quickly check if a name is registered without retrieving the object.src/check-config.tsif (registry.has("high-mem-prod")) { console.log("Production configuration is available."); }
-
Unregistration (
unregister
/unregisterMany
): Remove items by name.src/unregister-config.tsregistry.unregister("standard-dev"); console.log(`Registry contains 'standard-dev' after unregister: ${registry.has("standard-dev")}`); // false
-
Clearing (
clear
): Empty the entire registry.src/clear-registry.tsregistry.clear(); console.log(`Total configs after clear: ${registry.getAll().length}`); // 0
-
Caching (Internal): The
BaseRegistry
implementation automatically caches the results ofgetAll
andgetMany
calls. Subsequent identical calls return the cached result for performance. The cache is automatically cleared wheneverregister
,registerMany
,unregister
,unregisterMany
, orclear
is called.
Purpose & Use Cases
- Managing Variations: Store different versions or configurations of an object (e.g., environment settings, feature flag states, UI themes, resource definitions).
- Decoupling Definitions: Separates the definition of these variations from the code that uses them.
- Data Source for Factories: Provide the named templates that a Factory will use to create actual instances.
Core Implementation
IRegistry<T>
(Interface): Located insrc/domain/interface/registry.interface.ts
. Defines the contract. The generic typeT
is constrained to be an object with at least aname: string
property (T extends { name: string }
).BaseRegistry<T>
(Class): Located insrc/infrastructure/class/base/registry.class.ts
. The default implementation, featuring internal caching and optional logging.createRegistry<T>
(Utility): Exported from the library root. A convenient function to instantiateBaseRegistry
, acceptingIBaseRegistryOptions
(primarily for passing a logger).
Base Implementation Options (IBaseRegistryOptions
)
Name | Type | Default |
---|---|---|
logger | any The logger to use for logging. | new ConsoleLoggerService() |