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.