claude-flow-multilang
Version:
Revolutionary multilingual AI orchestration framework with cultural awareness and DDD architecture
234 lines (207 loc) • 6.39 kB
text/typescript
/**
* Claude Flow Multilang - Example Command Handler
* Creates products using DDD patterns
*/
import { CommandHandler, ICreateCommand, ICommandResult } from './command-handler.js';
import { SupportedLanguage } from '../../polyglot/types.js';
import { ILogger } from '../../core/logger.js';
import { EntityId, MultilingualString } from '../base-domain.js';
import { IMultilingualRepository } from '../base-domain.js';
/**
* Product aggregate for the example
*/
export class Product {
constructor(
public readonly id: EntityId,
public name: MultilingualString,
public description: MultilingualString,
public price: number,
public inventory: number,
public category: string,
public status: 'active' | 'discontinued' = 'active',
) {}
public domainEvents: any[] = [];
updatePrice(newPrice: number): void {
if (newPrice <= 0) {
throw new Error('Price must be positive');
}
if (this.status === 'discontinued') {
throw new Error('Cannot update price of discontinued product');
}
this.price = newPrice;
this.domainEvents.push({
eventType: 'PriceUpdated',
aggregateId: this.id.toString(),
data: { oldPrice: this.price, newPrice },
});
}
adjustInventory(quantity: number, reason: string): void {
const newInventory = this.inventory + quantity;
if (newInventory < 0) {
throw new Error('Inventory cannot be negative');
}
this.inventory = newInventory;
this.domainEvents.push({
eventType: 'InventoryAdjusted',
aggregateId: this.id.toString(),
data: { quantity, reason, newInventory },
});
}
markChangesAsCommitted(): void {
this.domainEvents = [];
}
}
/**
* Create product command
*/
export interface ICreateProductCommand extends ICreateCommand {
price: number;
inventory: number;
category: string;
}
/**
* Product repository interface
*/
export interface IProductRepository extends IMultilingualRepository<Product> {
findByCategory(category: string): Promise<Product[]>;
findByPriceRange(min: number, max: number): Promise<Product[]>;
}
/**
* Create product command handler
*/
export class CreateProductHandler extends CommandHandler<ICreateProductCommand, Product> {
constructor(
repository: IProductRepository,
logger: ILogger,
) {
super(repository, logger);
}
async handle(command: ICreateProductCommand): Promise<ICommandResult<Product>> {
try {
// Validate command
if (!command.name || Object.keys(command.name).length === 0) {
return this.errorResult('Product name is required');
}
if (command.price <= 0) {
return this.errorResult('Product price must be positive');
}
if (command.inventory < 0) {
return this.errorResult('Product inventory cannot be negative');
}
// Create product aggregate
const product = new Product(
EntityId.generate(),
new MultilingualString(command.name),
new MultilingualString(command.description),
command.price,
command.inventory,
command.category,
);
// Add creation event
product.domainEvents.push({
eventType: 'ProductCreated',
aggregateId: product.id.toString(),
data: {
name: command.name,
price: command.price,
inventory: command.inventory,
category: command.category,
},
});
// Save to repository
await this.executeWithTransaction(async () => {
await this.repository.save(product);
await this.publishEvents(product);
});
this.logger.info('Product created successfully', {
id: product.id.toString(),
name: product.name.get(command.language),
});
return this.successResult(
product,
product.id.toString(),
['ProductCreated'],
);
} catch (error) {
this.logger.error('Failed to create product', error);
return this.errorResult(
error instanceof Error ? error.message : 'Unknown error',
);
}
}
canHandle(command: any): boolean {
return command.type === 'CreateProduct' ||
command.constructor.name === 'CreateProductCommand';
}
}
/**
* Update product command
*/
export interface IUpdateProductCommand {
id: string;
timestamp: Date;
language: SupportedLanguage;
productId: string;
updates: {
price?: number;
inventory?: { quantity: number; reason: string };
status?: 'active' | 'discontinued';
};
}
/**
* Update product command handler
*/
export class UpdateProductHandler extends CommandHandler<IUpdateProductCommand, Product> {
constructor(
repository: IProductRepository,
logger: ILogger,
) {
super(repository, logger);
}
async handle(command: IUpdateProductCommand): Promise<ICommandResult<Product>> {
try {
// Find product
const product = await this.repository.findById(new EntityId(command.productId));
if (!product) {
return this.errorResult('Product not found');
}
// Apply updates
await this.executeWithTransaction(async () => {
if (command.updates.price !== undefined) {
product.updatePrice(command.updates.price);
}
if (command.updates.inventory) {
product.adjustInventory(
command.updates.inventory.quantity,
command.updates.inventory.reason,
);
}
if (command.updates.status) {
product.status = command.updates.status;
product.domainEvents.push({
eventType: 'ProductStatusChanged',
aggregateId: product.id.toString(),
data: { newStatus: command.updates.status },
});
}
// Save changes
await this.repository.save(product);
await this.publishEvents(product);
});
return this.successResult(
product,
product.id.toString(),
product.domainEvents.map(e => e.eventType),
);
} catch (error) {
this.logger.error('Failed to update product', error);
return this.errorResult(
error instanceof Error ? error.message : 'Unknown error',
);
}
}
canHandle(command: any): boolean {
return command.type === 'UpdateProduct' ||
command.constructor.name === 'UpdateProductCommand';
}
}