Skip to content

Advanced Usage Examples ​

This section provides advanced examples and patterns for using ziegel in complex enterprise scenarios. These examples demonstrate best practices and advanced features across multiple packages.

Enterprise Application Structure ​

Large-Scale Application Setup ​

typescript
// app/core/app-builder.ts
import { ObjectFactory, CultureProvider } from '@breadstone/ziegel-platform';
import { LoggerManager, LogLevel } from '@breadstone/ziegel-platform-logging';
import { SettingsManager, LocalStorage } from '@breadstone/ziegel-platform-configuration';
import { HttpClient } from '@breadstone/ziegel-platform-http';
import { EventAggregator } from '@breadstone/ziegel-platform-messaging';

export class ApplicationBuilder {
    private readonly services = new Map<string, any>();

    configureServices(): this {
        // Configure core services
        const settings = new SettingsManager(new LocalStorage());
        const logger = LoggerManager.getLogger('Application');
        const cultureProvider = new CultureProvider();
        const eventAggregator = new EventAggregator();

        // Configure HTTP client with interceptors
        const httpClient = new HttpClient();
        httpClient.addInterceptor(new AuthenticationInterceptor());
        httpClient.addInterceptor(new LoggingInterceptor(logger));
        httpClient.addInterceptor(new RetryInterceptor({ maxRetries: 3 }));

        // Register services
        this.services.set('settings', settings);
        this.services.set('logger', logger);
        this.services.set('culture', cultureProvider);
        this.services.set('http', httpClient);
        this.services.set('events', eventAggregator);

        return this;
    }

    configureRepositories(): this {
        const httpClient = this.services.get('http');
        const logger = this.services.get('logger');

        // Register repositories
        ObjectFactory.register('userRepository', () =>
            new UserRepository(httpClient, logger));
        ObjectFactory.register('orderRepository', () =>
            new OrderRepository(httpClient, logger));
        ObjectFactory.register('productRepository', () =>
            new ProductRepository(httpClient, logger));

        return this;
    }

    configureBusinessServices(): this {
        // Register business services with dependencies
        ObjectFactory.register('userService', () => {
            const userRepo = ObjectFactory.resolve<IUserRepository>('userRepository');
            const events = this.services.get('events');
            const logger = this.services.get('logger');
            return new UserService(userRepo, events, logger);
        });

        ObjectFactory.register('orderService', () => {
            const orderRepo = ObjectFactory.resolve<IOrderRepository>('orderRepository');
            const userService = ObjectFactory.resolve<UserService>('userService');
            const events = this.services.get('events');
            return new OrderService(orderRepo, userService, events);
        });

        return this;
    }

    build(): Application {
        return new Application(this.services);
    }
}

// Usage
const app = new ApplicationBuilder()
    .configureServices()
    .configureRepositories()
    .configureBusinessServices()
    .build();

await app.start();

Advanced Data Patterns ​

Complex Repository with Caching and Validation ​

typescript
import { IRepository, QueryBuilder, QueryOperator } from '@breadstone/ziegel-data';
import { MemoryCache, CachePolicy } from '@breadstone/ziegel-platform-caching';
import { Guard, ValidationException } from '@breadstone/ziegel-core';
import { ILogger } from '@breadstone/ziegel-platform-logging';

export class EnterpriseUserRepository implements IUserRepository {
    private readonly cache = new MemoryCache<User>();
    private readonly cachePolicy = new CachePolicy({
        ttl: 300000, // 5 minutes
        maxSize: 1000
    });

    constructor(
        private readonly httpClient: IHttpClient,
        private readonly logger: ILogger,
        private readonly validator: IUserValidator
    ) {}

    async getById(id: string): Promise<User | null> {
        Guard.argumentNotNullOrEmpty(id, 'id');

        // Check cache first
        const cacheKey = `user:${id}`;
        const cachedUser = this.cache.get(cacheKey);
        if (cachedUser) {
            this.logger.debug(`Cache hit for user ${id}`);
            return cachedUser;
        }

        try {
            const response = await this.httpClient.get(`/api/users/${id}`);
            const user = this.mapToUser(response.data);

            // Cache the result
            this.cache.set(cacheKey, user, this.cachePolicy);

            this.logger.info(`Retrieved user ${id} from API`);
            return user;
        } catch (error) {
            this.logger.error(`Failed to retrieve user ${id}`, error);
            throw error;
        }
    }

    async findByQuery(query: UserQuery): Promise<User[]> {
        const queryBuilder = new QueryBuilder<User>();

        if (query.departmentId) {
            queryBuilder.where('departmentId', QueryOperator.Equals, query.departmentId);
        }

        if (query.minAge) {
            queryBuilder.and('age', QueryOperator.GreaterThanOrEqual, query.minAge);
        }

        if (query.skills?.length > 0) {
            queryBuilder.and('skills', QueryOperator.ContainsAny, query.skills);
        }

        if (query.searchTerm) {
            queryBuilder.and(u =>
                u.firstName.includes(query.searchTerm) ||
                u.lastName.includes(query.searchTerm) ||
                u.email.includes(query.searchTerm)
            );
        }

        const queryParams = queryBuilder.toQueryString();
        const response = await this.httpClient.get(`/api/users/search?${queryParams}`);

        return response.data.map(userData => this.mapToUser(userData));
    }

    async add(user: User): Promise<User> {
        // Validate before saving
        const validationResult = await this.validator.validate(user);
        if (!validationResult.isValid) {
            throw new ValidationException(validationResult.errors);
        }

        const response = await this.httpClient.post('/api/users', user);
        const savedUser = this.mapToUser(response.data);

        // Update cache
        this.cache.set(`user:${savedUser.id}`, savedUser, this.cachePolicy);

        this.logger.info(`Created user ${savedUser.id}`);
        return savedUser;
    }

    async update(user: User): Promise<User> {
        Guard.argumentNotNull(user, 'user');

        const validationResult = await this.validator.validate(user);
        if (!validationResult.isValid) {
            throw new ValidationException(validationResult.errors);
        }

        const response = await this.httpClient.put(`/api/users/${user.id}`, user);
        const updatedUser = this.mapToUser(response.data);

        // Invalidate cache
        this.cache.remove(`user:${user.id}`);

        this.logger.info(`Updated user ${user.id}`);
        return updatedUser;
    }

    private mapToUser(data: any): User {
        return {
            id: data.id,
            firstName: data.firstName,
            lastName: data.lastName,
            email: data.email,
            departmentId: data.departmentId,
            age: data.age,
            skills: data.skills || [],
            createdAt: DateTime.parse(data.createdAt),
            updatedAt: DateTime.parse(data.updatedAt)
        };
    }
}

Unit of Work Pattern for Complex Transactions ​

typescript
import { IUnitOfWork, UnitOfWork } from '@breadstone/ziegel-data';
import { TransactionScope } from '@breadstone/ziegel-platform-transaction';

export class OrderProcessingService {
    constructor(
        private readonly unitOfWork: IUnitOfWork,
        private readonly userRepository: IUserRepository,
        private readonly orderRepository: IOrderRepository,
        private readonly productRepository: IProductRepository,
        private readonly inventoryService: IInventoryService,
        private readonly notificationService: INotificationService,
        private readonly logger: ILogger
    ) {}

    async processOrder(orderRequest: OrderRequest): Promise<Order> {
        using transactionScope = new TransactionScope();

        try {
            this.unitOfWork.begin();

            // 1. Validate user
            const user = await this.userRepository.getById(orderRequest.userId);
            if (!user) {
                throw new NotFoundException(`User ${orderRequest.userId} not found`);
            }

            // 2. Validate and reserve products
            const orderItems: OrderItem[] = [];
            for (const item of orderRequest.items) {
                const product = await this.productRepository.getById(item.productId);
                if (!product) {
                    throw new NotFoundException(`Product ${item.productId} not found`);
                }

                // Check inventory and reserve
                const reservation = await this.inventoryService.reserve(
                    item.productId,
                    item.quantity
                );

                orderItems.push({
                    productId: item.productId,
                    quantity: item.quantity,
                    price: product.price,
                    reservationId: reservation.id
                });
            }

            // 3. Calculate totals
            const subtotal = orderItems.reduce((sum, item) =>
                sum + (item.price * item.quantity), 0);
            const tax = subtotal * 0.1; // 10% tax
            const total = subtotal + tax;

            // 4. Create order
            const order = new Order({
                id: Guid.newGuid().toString(),
                userId: user.id,
                items: orderItems,
                subtotal,
                tax,
                total,
                status: OrderStatus.Processing,
                createdAt: DateTime.now()
            });

            // 5. Save order
            await this.orderRepository.add(order);

            // 6. Update inventory
            for (const item of orderItems) {
                await this.inventoryService.commitReservation(item.reservationId);
            }

            // 7. Send notifications
            await this.notificationService.sendOrderConfirmation(user, order);

            // Commit transaction
            await this.unitOfWork.commit();
            transactionScope.complete();

            this.logger.info(`Order ${order.id} processed successfully`, {
                orderId: order.id,
                userId: user.id,
                total: order.total,
                itemCount: order.items.length
            });

            return order;

        } catch (error) {
            this.logger.error('Order processing failed', error);
            await this.unitOfWork.rollback();
            throw error;
        }
    }
}

Advanced HTTP Patterns ​

Custom HTTP Interceptors ​

typescript
import { IHttpInterceptor, HttpRequest, HttpResponse } from '@breadstone/ziegel-platform-http';
import { ILogger } from '@breadstone/ziegel-platform-logging';
import { PerformanceTimer } from '@breadstone/ziegel-core';

export class PerformanceInterceptor implements IHttpInterceptor {
    constructor(private readonly logger: ILogger) {}

    async intercept(request: HttpRequest, next: Function): Promise<HttpResponse> {
        const timer = new PerformanceTimer();
        const requestId = Guid.newGuid().toString();

        this.logger.debug(`Starting HTTP request ${requestId}`, {
            method: request.method,
            url: request.url,
            headers: request.headers
        });

        try {
            const response = await next(request);

            const duration = timer.elapsed;
            this.logger.info(`HTTP request completed ${requestId}`, {
                method: request.method,
                url: request.url,
                status: response.status,
                duration: duration.totalMilliseconds
            });

            // Log slow requests
            if (duration.totalSeconds > 5) {
                this.logger.warn(`Slow HTTP request detected ${requestId}`, {
                    duration: duration.totalSeconds
                });
            }

            return response;

        } catch (error) {
            const duration = timer.elapsed;
            this.logger.error(`HTTP request failed ${requestId}`, {
                method: request.method,
                url: request.url,
                duration: duration.totalMilliseconds,
                error: error.message
            });

            throw error;
        }
    }
}

export class CircuitBreakerInterceptor implements IHttpInterceptor {
    private failures = 0;
    private lastFailureTime?: DateTime;
    private readonly maxFailures = 5;
    private readonly timeoutMinutes = 5;

    constructor(private readonly logger: ILogger) {}

    async intercept(request: HttpRequest, next: Function): Promise<HttpResponse> {
        if (this.isCircuitOpen()) {
            const error = new Error('Circuit breaker is open');
            this.logger.warn('Circuit breaker prevented HTTP request', {
                url: request.url,
                failures: this.failures
            });
            throw error;
        }

        try {
            const response = await next(request);
            this.onSuccess();
            return response;
        } catch (error) {
            this.onFailure();
            throw error;
        }
    }

    private isCircuitOpen(): boolean {
        if (this.failures < this.maxFailures) {
            return false;
        }

        if (!this.lastFailureTime) {
            return true;
        }

        const timeSinceLastFailure = DateTime.now().subtract(this.lastFailureTime);
        return timeSinceLastFailure.totalMinutes < this.timeoutMinutes;
    }

    private onSuccess(): void {
        this.failures = 0;
        this.lastFailureTime = undefined;
    }

    private onFailure(): void {
        this.failures++;
        this.lastFailureTime = DateTime.now();

        if (this.failures >= this.maxFailures) {
            this.logger.error('Circuit breaker opened due to consecutive failures', {
                failures: this.failures
            });
        }
    }
}

Advanced Reactive Patterns ​

Complex Event-Driven Architecture ​

typescript
import { EventAggregator, IEvent } from '@breadstone/ziegel-platform-messaging';
import { fromPubSubEvent } from '@breadstone/ziegel-rx';
import { Observable, combineLatest, debounceTime, distinctUntilChanged } from 'rxjs';

// Domain events
export class UserCreatedEvent implements IEvent {
    constructor(public readonly user: User) {}
}

export class OrderCreatedEvent implements IEvent {
    constructor(public readonly order: Order) {}
}

export class ProductUpdatedEvent implements IEvent {
    constructor(public readonly product: Product) {}
}

// Event-driven service
export class AnalyticsService {
    private readonly eventAggregator: EventAggregator;
    private readonly logger: ILogger;

    constructor(eventAggregator: EventAggregator, logger: ILogger) {
        this.eventAggregator = eventAggregator;
        this.logger = logger;
        this.setupEventStreams();
    }

    private setupEventStreams(): void {
        // User events stream
        const userEvents$ = fromPubSubEvent(this.eventAggregator, UserCreatedEvent);

        userEvents$.subscribe(event => {
            this.trackUserCreation(event.user);
        });

        // Order events stream with debouncing
        const orderEvents$ = fromPubSubEvent(this.eventAggregator, OrderCreatedEvent)
            .pipe(
                debounceTime(1000), // Debounce for 1 second
                distinctUntilChanged((a, b) => a.order.userId === b.order.userId)
            );

        orderEvents$.subscribe(event => {
            this.trackOrderCreation(event.order);
            this.updateCustomerLifetimeValue(event.order.userId);
        });

        // Combined streams for complex analytics
        const userAndOrderStream$ = combineLatest([
            userEvents$,
            orderEvents$
        ]).pipe(
            debounceTime(5000) // Wait for 5 seconds of inactivity
        );

        userAndOrderStream$.subscribe(([userEvent, orderEvent]) => {
            this.analyzeUserOrderCorrelation(userEvent.user, orderEvent.order);
        });
    }

    private trackUserCreation(user: User): void {
        this.logger.info('Tracking user creation', { userId: user.id });

        // Send to analytics service
        this.sendAnalyticsEvent('user.created', {
            userId: user.id,
            email: user.email,
            registrationSource: user.registrationSource,
            timestamp: DateTime.now().toISOString()
        });
    }

    private trackOrderCreation(order: Order): void {
        this.logger.info('Tracking order creation', {
            orderId: order.id,
            userId: order.userId
        });

        this.sendAnalyticsEvent('order.created', {
            orderId: order.id,
            userId: order.userId,
            total: order.total,
            itemCount: order.items.length,
            timestamp: DateTime.now().toISOString()
        });
    }

    private sendAnalyticsEvent(eventName: string, data: any): void {
        // Implementation depends on analytics provider
        // (Google Analytics, Mixpanel, custom analytics, etc.)
    }
}

Advanced Localization Patterns ​

Dynamic Localization with Caching ​

typescript
import { LocalizationManager, HttpLocalizationLoader } from '@breadstone/ziegel-platform-localization';
import { CultureProvider } from '@breadstone/ziegel-platform';
import { MemoryCache } from '@breadstone/ziegel-platform-caching';
import { Observable, BehaviorSubject } from 'rxjs';

export class AdvancedLocalizationService {
    private readonly localizationManager: LocalizationManager;
    private readonly cultureProvider: CultureProvider;
    private readonly cache = new MemoryCache<string>();
    private readonly localizationSubject = new BehaviorSubject<Map<string, string>>(new Map());

    constructor(
        httpClient: IHttpClient,
        logger: ILogger
    ) {
        const loader = new HttpLocalizationLoader('/api/localization/{culture}.json');
        this.localizationManager = new LocalizationManager(loader);
        this.cultureProvider = new CultureProvider();

        // Listen for culture changes and preload localizations
        this.cultureProvider.cultureChanged.add(this.onCultureChanged, this);
    }

    async initialize(): Promise<void> {
        const currentCulture = this.cultureProvider.getCurrentCulture();
        await this.loadLocalization(currentCulture);
    }

    get localizations$(): Observable<Map<string, string>> {
        return this.localizationSubject.asObservable();
    }

    async getString(key: string, ...args: any[]): Promise<string> {
        const cacheKey = `${this.cultureProvider.getCurrentCulture()}:${key}`;

        // Check cache first
        let localizedString = this.cache.get(cacheKey);

        if (!localizedString) {
            localizedString = await this.localizationManager.getLocalizedString(key);
            this.cache.set(cacheKey, localizedString);
        }

        // Format with arguments if provided
        if (args.length > 0) {
            return StringExtensions.format(localizedString, ...args);
        }

        return localizedString;
    }

    async getFormattedDate(date: DateTime, format?: string): Promise<string> {
        const culture = this.cultureProvider.getCurrentCulture();
        const formatter = new DateTimeFormatter(culture);
        return formatter.format(date, format);
    }

    async getFormattedNumber(value: number, format?: string): Promise<string> {
        const culture = this.cultureProvider.getCurrentCulture();
        const formatter = new NumberFormatter(culture);
        return formatter.format(value, format);
    }

    async getFormattedCurrency(amount: number, currency?: string): Promise<string> {
        const culture = this.cultureProvider.getCurrentCulture();
        const formatter = new NumberFormatter(culture);
        return formatter.formatCurrency(amount, currency);
    }

    private async onCultureChanged(newCulture: string): Promise<void> {
        await this.loadLocalization(newCulture);

        // Clear cache for old culture
        this.cache.clear();

        // Notify subscribers
        const currentLocalizations = await this.getAllLocalizations();
        this.localizationSubject.next(currentLocalizations);
    }

    private async loadLocalization(culture: string): Promise<void> {
        try {
            await this.localizationManager.loadCulture(culture);
        } catch (error) {
            // Fallback to default culture
            await this.localizationManager.loadCulture('en-US');
        }
    }

    private async getAllLocalizations(): Promise<Map<string, string>> {
        // Implementation depends on your localization data structure
        return new Map<string, string>();
    }
}

// Usage in Angular component
export class ProductListComponent {
    private localizations = new Map<string, string>();

    constructor(
        private localizationService: AdvancedLocalizationService,
        private productService: ProductService
    ) {
        // Subscribe to localization changes
        this.localizationService.localizations$.subscribe(localizations => {
            this.localizations = localizations;
            this.updateDisplayTexts();
        });
    }

    async ngOnInit(): Promise<void> {
        await this.loadProducts();
    }

    private async loadProducts(): Promise<void> {
        try {
            this.products = await this.productService.getAll();
            this.displayMessage = await this.localizationService.getString(
                'product.list.loaded',
                this.products.length
            );
        } catch (error) {
            this.displayMessage = await this.localizationService.getString(
                'product.list.error'
            );
        }
    }

    private updateDisplayTexts(): void {
        // Update all displayed texts when localization changes
        this.headerText = this.localizations.get('product.list.header') || 'Products';
        this.loadingText = this.localizations.get('common.loading') || 'Loading...';
    }
}

Performance Optimization Patterns ​

Advanced Caching Strategy ​

typescript
import {
    MemoryCache,
    SessionCache,
    PersistentCache,
    CachePolicy,
    ICacheStrategy
} from '@breadstone/ziegel-platform-caching';

export class MultiTierCacheService<T> {
    private readonly l1Cache: MemoryCache<T>;        // Fastest, smallest
    private readonly l2Cache: SessionCache<T>;       // Medium speed, session scope
    private readonly l3Cache: PersistentCache<T>;    // Slowest, persistent

    constructor(
        private readonly cacheStrategy: ICacheStrategy,
        private readonly logger: ILogger
    ) {
        this.l1Cache = new MemoryCache<T>();
        this.l2Cache = new SessionCache<T>();
        this.l3Cache = new PersistentCache<T>();
    }

    async get(key: string): Promise<T | null> {
        const timer = new PerformanceTimer();

        try {
            // Try L1 cache first
            let value = this.l1Cache.get(key);
            if (value) {
                this.logger.debug(`Cache L1 hit for key: ${key}`, {
                    duration: timer.elapsed.totalMilliseconds
                });
                return value;
            }

            // Try L2 cache
            value = await this.l2Cache.get(key);
            if (value) {
                // Promote to L1
                this.l1Cache.set(key, value, this.cacheStrategy.getL1Policy());
                this.logger.debug(`Cache L2 hit for key: ${key}`, {
                    duration: timer.elapsed.totalMilliseconds
                });
                return value;
            }

            // Try L3 cache
            value = await this.l3Cache.get(key);
            if (value) {
                // Promote to L2 and L1
                await this.l2Cache.set(key, value, this.cacheStrategy.getL2Policy());
                this.l1Cache.set(key, value, this.cacheStrategy.getL1Policy());
                this.logger.debug(`Cache L3 hit for key: ${key}`, {
                    duration: timer.elapsed.totalMilliseconds
                });
                return value;
            }

            this.logger.debug(`Cache miss for key: ${key}`, {
                duration: timer.elapsed.totalMilliseconds
            });
            return null;

        } catch (error) {
            this.logger.error(`Cache get error for key: ${key}`, error);
            return null;
        }
    }

    async set(key: string, value: T): Promise<void> {
        try {
            // Set in all cache levels based on strategy
            const l1Policy = this.cacheStrategy.getL1Policy();
            const l2Policy = this.cacheStrategy.getL2Policy();
            const l3Policy = this.cacheStrategy.getL3Policy();

            // Set in parallel for better performance
            await Promise.all([
                this.l1Cache.set(key, value, l1Policy),
                this.l2Cache.set(key, value, l2Policy),
                this.l3Cache.set(key, value, l3Policy)
            ]);

            this.logger.debug(`Cache set for key: ${key}`);

        } catch (error) {
            this.logger.error(`Cache set error for key: ${key}`, error);
        }
    }

    async invalidate(key: string): Promise<void> {
        try {
            // Remove from all cache levels
            await Promise.all([
                this.l1Cache.remove(key),
                this.l2Cache.remove(key),
                this.l3Cache.remove(key)
            ]);

            this.logger.debug(`Cache invalidated for key: ${key}`);

        } catch (error) {
            this.logger.error(`Cache invalidate error for key: ${key}`, error);
        }
    }
}

// Cache-aside pattern with automatic loading
export class CacheAsideRepository<T> implements IRepository<T> {
    constructor(
        private readonly dataSource: IDataSource<T>,
        private readonly cache: MultiTierCacheService<T>,
        private readonly keyGenerator: (entity: T) => string,
        private readonly logger: ILogger
    ) {}

    async getById(id: string): Promise<T | null> {
        const cacheKey = `entity:${id}`;

        // Try cache first
        let entity = await this.cache.get(cacheKey);
        if (entity) {
            return entity;
        }

        // Load from data source
        entity = await this.dataSource.getById(id);
        if (entity) {
            // Cache the result
            await this.cache.set(cacheKey, entity);
        }

        return entity;
    }

    async add(entity: T): Promise<T> {
        // Save to data source
        const savedEntity = await this.dataSource.add(entity);

        // Update cache
        const cacheKey = this.keyGenerator(savedEntity);
        await this.cache.set(cacheKey, savedEntity);

        return savedEntity;
    }

    async update(entity: T): Promise<T> {
        // Update data source
        const updatedEntity = await this.dataSource.update(entity);

        // Invalidate cache (let it reload on next access)
        const cacheKey = this.keyGenerator(updatedEntity);
        await this.cache.invalidate(cacheKey);

        return updatedEntity;
    }

    async delete(id: string): Promise<void> {
        // Delete from data source
        await this.dataSource.delete(id);

        // Invalidate cache
        const cacheKey = `entity:${id}`;
        await this.cache.invalidate(cacheKey);
    }
}

These advanced examples demonstrate how to leverage ziegel's powerful features for building robust, scalable enterprise applications. Each pattern shows real-world scenarios and best practices for common enterprise development challenges.