/** * Base Domain Service * * Abstract base class for all domain services in @plyaz/core. * Follows the same pattern as BaseAdapter in @plyaz/notifications. * * Provides: * - API client initialization via constructor config * - Automatic mapper/validator instantiation * - Service enabled/disabled state management * - Default values handling * - Runtime context detection * - Lazy logger initialization * * Subclasses must implement: * - isAvailable() - Check if service is configured and ready * - dispose() - Cleanup resources * * @example * ```typescript * interface MyServiceConfig extends BaseDomainServiceConfig { * apiBasePath?: string; * } * * class MyDomainService extends BaseDomainService< * MyServiceConfig, * MyMapper, * MyValidator * > { * constructor(config: MyServiceConfig) { * super({ * serviceName: 'MyDomainService', * supportedRuntimes: ['universal'], * serviceConfig: config, * apiClientConfig: { * baseURL: config.apiBasePath || '/api', // Use || to treat empty strings as falsy * }, * MapperClass: MyMapperClass, * ValidatorClass: MyValidatorClass, * }); * } * * isAvailable(): boolean { * return this.config.enabled !== false; * } * * dispose(): void { * // Cleanup * } * * async create(data: unknown): Promise { * this.assertReady(); * const validated = this.validator.validateCreateOrThrow(data); * const dto = this.mapper.toCreateDTO(validated); * const response = await this.apiClient.post('/entities', dto); * return this.mapper.toDomain(response.data); * } * } * * // Usage * const service = new MyDomainService({ enabled: true, apiBasePath: '/api/v1' }); * await service.ensureApiClientInitialized(); * const entity = await service.create({ name: 'Test' }); * ``` */ import { PackageLogger } from '@plyaz/logger'; import type { CoreBaseMapperInstance, CoreBaseValidatorInstance, CoreServiceRuntime } from '@plyaz/types/core'; import type { FeatureFlagValue } from '@plyaz/types/features'; import { ApiClientService } from '../../services/ApiClientService'; import type { CoreBaseDomainServiceConfig, CoreBaseServiceConfig, CoreCacheManagerInstance } from '@plyaz/types/core'; import type { DatabaseServiceInterface } from '@plyaz/types/db'; import type { ObservabilityAdapter, Span } from '@plyaz/types/observability'; /** * API client type (from @plyaz/core ApiClientService) */ export type ApiClient = Awaited>; /** * Abstract base class for domain services * * Uses generics for strong typing: * - TConfig: Service configuration type * - TMapper: Mapper instance type * - TValidator: Validator instance type * * Subclasses use the super({...}) pattern: * ```typescript * class MyService extends BaseDomainService { * constructor(config: MyConfig) { * super({ * serviceName: 'MyService', * supportedRuntimes: ['universal'], * serviceConfig: config, * apiClientConfig: { baseURL: '/api' }, * MapperClass: MyMapperClass, * ValidatorClass: MyValidatorClass, * }); * } * } * ``` */ export declare abstract class BaseDomainService = CoreBaseMapperInstance, TValidator extends CoreBaseValidatorInstance = CoreBaseValidatorInstance> { /** Service name for logging and error messages */ readonly serviceName: string; /** Supported runtimes for this service */ readonly supportedRuntimes: readonly CoreServiceRuntime[]; /** Service configuration */ protected readonly config: TConfig; /** Logger instance */ protected readonly logger: PackageLogger; /** Initialization state */ protected _initialized: boolean; private _apiClient; private _clientInitPromise; private readonly _apiClientConfig?; private readonly _setAsDefaultClient; protected readonly cacheManager?: CoreCacheManagerInstance; protected readonly dbService?: DatabaseServiceInterface; protected readonly apiService?: ApiClient; protected readonly observabilityService?: ObservabilityAdapter; /** * Cache prefix for namespacing cache keys (e.g., 'example', 'user', 'product'). * Subclasses can override by defining as a property. * If not overridden, defaults to serviceName.toLowerCase(). * * @example * ```typescript * protected cachePrefix = 'my-service'; // Override default * ``` */ protected cachePrefix: string; private _mapperInstance; private _validatorInstance; private readonly _MapperClass?; private readonly _ValidatorClass?; /** * Create a new domain service instance * * @param config - Base service configuration (passed via super({...})) */ constructor(config: CoreBaseServiceConfig); /** * Check if the service is enabled * Service is enabled by default unless explicitly disabled */ get isServiceEnabled(): boolean; /** * Check if the service is initialized (API client ready) */ get isInitialized(): boolean; /** * Check if mapper is available */ get hasMapper(): boolean; /** * Check if validator is available */ get hasValidator(): boolean; /** * Get the mapper instance (lazy initialization) * @throws CorePackageError if MapperClass was not provided */ get mapper(): TMapper; /** * Set the mapper instance * Allows subclasses to override mapper initialization */ set mapper(value: TMapper); /** * Get the validator instance (lazy initialization) * @throws CorePackageError if ValidatorClass was not provided */ get validator(): TValidator; /** * Set the validator instance * Allows subclasses to override validator initialization */ set validator(value: TValidator); /** * Get the API client (after initialization) * @throws CorePackageError if API client was not configured or not initialized */ get apiClient(): ApiClient; /** * Get the observability adapter (if injected) * Returns undefined if observability is not enabled */ get observability(): ObservabilityAdapter | undefined; /** * Check if observability is available */ get hasObservability(): boolean; /** * Initialize API client asynchronously * Called from constructor if apiClientConfig is provided * * Uses ApiClientService.createStandaloneClient() which includes: * - Automatic error handling (single errors and arrays) * - Event emission to CORE_EVENTS.SYSTEM.ERROR and CORE_EVENTS.API.REQUEST_ERROR * - Serialization to unified SerializedError format */ private initializeApiClient; /** * Ensure API client is initialized before use. * Call this from methods that need the API client. * Also called by ServiceRegistry.create() for services with async initialization. */ ensureApiClientInitialized(): Promise; /** * Get current configuration (immutable copy) */ getConfig(): Readonly; /** * Get a default value * @param key - The key to get the default for */ getDefault(key: string): T | undefined; /** * Set a default value (mutates config.defaults) * @param key - The key to set the default for * @param value - The default value */ setDefault(key: string, value: FeatureFlagValue): void; /** * Set multiple default values * @param defaults - Record of default values */ setDefaults(defaults: Record): void; /** * Assert that the service is available (configured and ready) * @throws CorePackageError if not available */ protected assertAvailable(): void; /** * Assert that the service is enabled * @throws CorePackageError if disabled */ protected assertEnabled(): void; /** * Assert that the service is ready (enabled AND available) * @throws CorePackageError if not ready */ protected assertReady(): void; /** * Check if service is available (configured and ready) * Subclasses must implement this to check credentials, config, etc. * * @returns true if available */ abstract isAvailable(): boolean; /** * Dispose/cleanup the service * Subclasses must implement to release resources */ abstract dispose(): void; /** * Log debug message with service context */ protected logDebug(message: string, data?: Record): void; /** * Log info message with service context */ protected logInfo(message: string, data?: Record): void; /** * Log warning message with service context */ protected logWarn(message: string, data?: Record): void; /** * Log error message with service context */ protected logError(message: string, data?: Record): void; /** * Record a metric (no-op if observability not available) */ protected recordMetric(name: string, value: number, tags?: Record): Promise; /** * Increment a counter (no-op if observability not available) */ protected incrementCounter(name: string, value?: number, tags?: Record): Promise; /** * Start a span for tracing (returns noop span if observability not available) */ protected startSpan(name: string, attributes?: Record): Span; /** * Execute a function within a traced span */ protected withSpan(name: string, fn: (span: Span) => Promise, attributes?: Record): Promise; } //# sourceMappingURL=BaseDomainService.d.ts.map