Parameter System Architecture
Unified parameter metadata system for actions and embeddables
🔧 Parameter System Architecture
A unified, type-safe parameter definition system shared by both Public Actions and Embeddable Components.
🎯 Design Goals
- Single Source of Truth: One parameter type (
ModuleParameterMetadata) for the entire system. - Rich Metadata: Validation rules, UI hints, defaults, and descriptions.
- Type Safety: Full TypeScript support with compile-time guarantees.
- Library Independence:
module-baseandaction-registryremain independent (no circular dependencies).
📐 Core Types
ModuleParameterMetadata
The foundation type used everywhere.
export interface ModuleParameterMetadata {
readonly name: string;
readonly type: ParameterType;
readonly description?: string;
readonly required: boolean;
readonly defaultValue?: unknown;
readonly validators?: readonly ParameterValidator[];
readonly uiHints?: ParameterUIHints;
readonly example?: unknown;
readonly sensitive?: boolean;
}
ParameterType
Supported parameter types.
type ParameterType =
| 'string'
| 'number'
| 'boolean'
| 'date'
| 'object'
| 'array'
| 'enum'
| 'any';
ParameterValidator
Validation rules.
export interface ParameterValidator {
readonly type: ValidatorType;
readonly value?: unknown;
readonly message?: string;
}
type ValidatorType =
| 'required'
| 'min'
| 'max'
| 'minLength'
| 'maxLength'
| 'pattern'
| 'email'
| 'url'
| 'enum'
| 'custom';
ParameterUIHints
Hints for automatic UI generation.
export interface ParameterUIHints {
readonly component?: string; // 'input' | 'textarea' | 'select' | 'checkbox'
readonly placeholder?: string;
readonly label?: string;
readonly helpText?: string;
readonly group?: string;
readonly order?: number;
}
🏗️ Builder API
ActionParameterBuilder
Fluent builder for constructing parameters.
import { ActionParameterBuilder, ParameterBuilders } from '@libs-core/action-registry';
// Using the fluent builder
const emailParam = ActionParameterBuilder.string('email')
.description('User email address')
.required('Email is required')
.pattern(/^[^\s@]+@[^\s@]+\.[^\s@]+$/, 'Invalid email format')
.label('Email Address')
.placeholder('[email protected]')
.build();
// Pre-configured builders
const emailParam = ParameterBuilders.email('email').build();
const pageParam = ParameterBuilders.page().build();
const searchParam = ParameterBuilders.search().build();
🔍 Validation Service
The ParameterValidationService validates parameters against their metadata.
import { ParameterValidationService } from '@libs-core/module-base';
@Injectable()
export class MyService {
private validation = inject(ParameterValidationService);
validateUserInput(params: Record<string, unknown>): Observable<ValidationResult> {
return this.validation.validate(params, parameterDefinitions);
}
}
🧩 Integration
In Public Actions
@PublicAction({
id: 'users:create',
// ...
parameters: [
ParameterBuilders.email('email').build(),
ActionParameterBuilder.string('firstName').required().build()
]
})
export class CreateUserAction implements ExecutableAction<User> { ... }
In Embeddable Components
embeddableComponents: [
{
id: 'users:list-widget',
componentType: UserListWidgetComponent,
inputs: [
ParameterBuilders.page().build(),
ActionParameterBuilder.boolean('showAvatars').build()
]
}
]
🔬 Technical Solutions
Challenge: Circular Dependencies
Problem: Both module-base and action-registry needed to share parameter types.
Solution: Maintain independent but identical type definitions.
// module-base exports ModuleParameterMetadata
// action-registry exports ActionParameterMetadata (identical)
// Documentation notes they must stay in sync
Challenge: Readonly Builder State
Problem: Readonly properties prevent mutation in builders.
Solution: DeepMutable<T> utility type.
type DeepMutable<T> = T extends readonly (infer U)[]
? U[]
: T extends object
? { -readonly [K in keyof T]: DeepMutable<T[K]> }
: T;
// Builder uses mutable state internally, returns immutable interface
Last Updated: December 3, 2025 Session ID: 3 Status: ✅ Production Architecture