### Full Integration Example with QueryLoggerMiddleware Source: https://github.com/evyweb/simple-ddd-toolkit/blob/master/_autodocs/api-reference/QueryLoggerMiddleware.md A comprehensive example demonstrating the setup of queries, DTOs, query handlers, and the integration of QueryLoggerMiddleware within a Bus. It also shows how to register handlers and execute queries. ```typescript import { Bus, Query, QueryHandler, QueryLoggerMiddleware } from '@evyweb/simple-ddd-toolkit'; // Query definitions class GetUserByIdQuery extends Query { public readonly __TAG = 'GetUserByIdQuery'; constructor(public readonly userId: string) { super(); } } class SearchUsersQuery extends Query { public readonly __TAG = 'SearchUsersQuery'; constructor( public readonly searchTerm: string, public readonly limit: number = 50, public readonly offset: number = 0 ) { super(); } } // DTOs interface UserDto { id: string; name: string; email: string; } // Query handlers class GetUserByIdQueryHandler extends QueryHandler { public readonly __TAG = 'GetUserByIdQueryHandler'; constructor(private userRepository: UserRepository) { super(); } async handle(query: GetUserByIdQuery): Promise { const user = await this.userRepository.findById(query.userId); return { id: user.id(), name: user.getName(), email: user.getEmail() }; } } class SearchUsersQueryHandler extends QueryHandler { public readonly __TAG = 'SearchUsersQueryHandler'; constructor(private userRepository: UserRepository) { super(); } async handle(query: SearchUsersQuery): Promise { const users = await this.userRepository.search({ searchTerm: query.searchTerm, limit: query.limit, offset: query.offset }); return users.map(user => ({ id: user.id(), name: user.getName(), email: user.getEmail() })); } } // Setup const logger = { log: console.log }; const queryBus = new Bus(); // Add logging middleware queryBus.use(new QueryLoggerMiddleware(logger, 'UserQueryBus')); // Register handlers queryBus.register(() => new GetUserByIdQueryHandler(userRepository)); queryBus.register(() => new SearchUsersQueryHandler(userRepository)); // Execute queries const user = await queryBus.execute(new GetUserByIdQuery('user-123')); const results = await queryBus.execute( new SearchUsersQuery('alice', 25, 0) ); ``` -------------------------------- ### Full Integration Example with CommandLoggerMiddleware Source: https://github.com/evyweb/simple-ddd-toolkit/blob/master/_autodocs/api-reference/CommandLoggerMiddleware.md A comprehensive example showing command definitions, handlers, setup with CommandLoggerMiddleware, and command execution. This illustrates a complete command processing flow. ```typescript import { Bus, Command, CommandHandler, CommandLoggerMiddleware, Aggregate, UUID } from '@evyweb/simple-ddd-toolkit'; // Command definitions class CreateUserCommand extends Command { public readonly __TAG = 'CreateUserCommand'; constructor( public readonly name: string, public readonly email: string ) { super(); } } class UpdateUserCommand extends Command { public readonly __TAG = 'UpdateUserCommand'; constructor( public readonly userId: string, public readonly newName: string ) { super(); } } // Command handlers class CreateUserCommandHandler extends CommandHandler { public readonly __TAG = 'CreateUserCommandHandler'; constructor(private userRepository: UserRepository) { super(); } async handle(command: CreateUserCommand): Promise { const user = new UserAggregate(UUID.create(uuid()), command.name, command.email); await this.userRepository.save(user); } } class UpdateUserCommandHandler extends CommandHandler { public readonly __TAG = 'UpdateUserCommandHandler'; constructor(private userRepository: UserRepository) { super(); } async handle(command: UpdateUserCommand): Promise { const user = await this.userRepository.findById(command.userId); user.changeName(command.newName); await this.userRepository.save(user); } } // Setup const logger = { log: console.log }; const commandBus = new Bus(); // Add logging with correlation ID commandBus.use(new CommandLoggerMiddleware(logger, 'UserService-Requests')); // Register handlers commandBus.register(() => new CreateUserCommandHandler(userRepository)); commandBus.register(() => new UpdateUserCommandHandler(userRepository)); // Execute commands await commandBus.execute(new CreateUserCommand('Alice', 'alice@example.com')); await commandBus.execute(new UpdateUserCommand('user-123', 'Alice Smith')); ``` -------------------------------- ### Full EventBus Usage Example Source: https://github.com/evyweb/simple-ddd-toolkit/blob/master/_autodocs/api-reference/EventBus.md Demonstrates the complete setup and usage of the EventBus, including defining events, handlers, registering middleware, and dispatching events from a command handler. Ensure all necessary services like EmailService and Database are properly instantiated and available. ```typescript import { EventBus, EventLoggingMiddleware, Aggregate, UUID, DomainEvent, IEventHandler } from '@evyweb/simple-ddd-toolkit'; // Define events class UserCreatedEvent extends DomainEvent { public readonly __TAG = 'UserCreatedEvent'; constructor(userId: string, name: string) { super({ eventId: uuid(), payload: { userId, name } }); } } class UserNameChangedEvent extends DomainEvent { public readonly __TAG = 'UserNameChangedEvent'; constructor(userId: string, oldName: string, newName: string) { super({ eventId: uuid(), payload: { userId, newName }, metadata: { oldName } }); } } // Define event handlers class SendWelcomeEmailHandler implements IEventHandler { public readonly __TAG = 'UserCreatedEvent'; constructor(private emailService: EmailService) {} async handle(event: UserCreatedEvent): Promise { const { userId, name } = event.payload; await this.emailService.sendWelcome(name); } } class UpdateUserStatsHandler implements IEventHandler { public readonly __TAG = 'UserNameChangedEvent'; constructor(private db: Database) {} async handle(event: UserNameChangedEvent): Promise { await this.db.updateUserStats(event.payload.userId); } } // Setup and use const logger = { log: console.log }; const eventBus = new EventBus(); // Register middleware eventBus.use(new EventLoggingMiddleware(logger)); // Register handlers eventBus.on('UserCreatedEvent', () => new SendWelcomeEmailHandler(emailService)); eventBus.on('UserNameChangedEvent', () => new UpdateUserStatsHandler(db)); // In command handler class CreateUserCommandHandler extends CommandHandler { public readonly __TAG = 'CreateUserCommandHandler'; constructor( private userRepository: UserRepository, private eventBus: EventBus ) { super(); } async handle(command: CreateUserCommand): Promise { const user = UserAggregate.create(UUID.create(uuid()), command.name); await this.userRepository.save(user); await user.dispatchEvents(this.eventBus); } } ``` -------------------------------- ### Basic QueryLoggerMiddleware Setup Source: https://github.com/evyweb/simple-ddd-toolkit/blob/master/_autodocs/api-reference/QueryLoggerMiddleware.md Demonstrates the basic setup of QueryLoggerMiddleware with a custom logger and a unique identifier for the bus. All queries executed through this bus will be logged. ```typescript import { Bus, Query, QueryLoggerMiddleware } from '@evyweb/simple-ddd-toolkit'; const logger = { log: (message: string) => console.log(message) }; const queryBus = new Bus(); queryBus.use(new QueryLoggerMiddleware(logger, 'QueryBus')); // All queries executed through the bus will be logged ``` -------------------------------- ### Basic EventLoggingMiddleware Setup Source: https://github.com/evyweb/simple-ddd-toolkit/blob/master/_autodocs/api-reference/EventLoggingMiddleware.md Demonstrates the basic setup of EventLoggingMiddleware with a simple console logger. Ensure EventBus and EventLoggingMiddleware are imported. ```typescript import { EventBus, EventLoggingMiddleware } from '@evyweb/simple-ddd-toolkit'; const logger = { log: (message: string) => console.log(message) }; const eventBus = new EventBus(); eventBus.use(new EventLoggingMiddleware(logger)); // Now all events dispatched through the bus will be logged ``` -------------------------------- ### Install Simple DDD Toolkit Source: https://github.com/evyweb/simple-ddd-toolkit/blob/master/README.md Install the Simple DDD Toolkit using npm. This command adds the toolkit as a dependency to your project. ```bash npm install --save @evyweb/simple-ddd-toolkit ``` -------------------------------- ### Full Event Bus Setup with EventLoggingMiddleware Source: https://github.com/evyweb/simple-ddd-toolkit/blob/master/_autodocs/api-reference/EventLoggingMiddleware.md Illustrates a comprehensive EventBus setup including domain event definitions, event handlers, and command handling. The EventLoggingMiddleware is automatically invoked when events are dispatched. ```typescript import { EventBus, EventLoggingMiddleware, DomainEvent, IEventHandler, Aggregate, UUID } from '@evyweb/simple-ddd-toolkit'; // Event definitions class OrderPlacedEvent extends DomainEvent { public readonly __TAG = 'OrderPlacedEvent'; constructor(orderId: string, totalAmount: number, userId: string) { super({ eventId: generateId(), payload: { orderId, totalAmount, userId }, metadata: { timestamp: new Date().toISOString() } }); } } // Event handlers class SendOrderConfirmationHandler implements IEventHandler { public readonly __TAG = 'OrderPlacedEvent'; constructor(private emailService: EmailService) {} async handle(event: OrderPlacedEvent): Promise { const { orderId, userId } = event.payload; await this.emailService.sendOrderConfirmation(userId, orderId); } } class UpdateOrderMetricsHandler implements IEventHandler { public readonly __TAG = 'OrderPlacedEvent'; constructor(private analytics: Analytics) {} async handle(event: OrderPlacedEvent): Promise { const { totalAmount } = event.payload; await this.analytics.recordOrderValue(totalAmount); } } // Setup const logger = { log: console.log }; const eventBus = new EventBus(); // Logging is added automatically eventBus.use(new EventLoggingMiddleware(logger)); // Register handlers eventBus.on('OrderPlacedEvent', () => new SendOrderConfirmationHandler(emailService)); eventBus.on('OrderPlacedEvent', () => new UpdateOrderMetricsHandler(analytics)); // In a command handler class PlaceOrderCommandHandler extends CommandHandler { public readonly __TAG = 'PlaceOrderCommandHandler'; constructor( private orderRepository: OrderRepository, private eventBus: EventBus ) { super(); } async handle(command: PlaceOrderCommand): Promise { const order = new OrderAggregate(command.orderId, command.userId, command.items); order.addEvent( new OrderPlacedEvent( order.id(), order.getTotalAmount(), command.userId ) ); await this.orderRepository.save(order); // This dispatch will trigger EventLoggingMiddleware to log the event await order.dispatchEvents(this.eventBus); } } ``` -------------------------------- ### Basic CommandLoggerMiddleware Setup Source: https://github.com/evyweb/simple-ddd-toolkit/blob/master/_autodocs/api-reference/CommandLoggerMiddleware.md Instantiate the CommandLoggerMiddleware with a logger object and an instance name. All commands passing through this bus will be logged. ```typescript import { Bus, Command, CommandLoggerMiddleware } from '@evyweb/simple-ddd-toolkit'; const logger = { log: (message: string) => console.log(message) }; const commandBus = new Bus(); commandBus.use(new CommandLoggerMiddleware(logger, 'CommandBus-Instance1')); // All commands executed through the bus will be logged ``` -------------------------------- ### Express Integration with QueryLoggerMiddleware Source: https://github.com/evyweb/simple-ddd-toolkit/blob/master/_autodocs/api-reference/QueryLoggerMiddleware.md Illustrates how to use QueryLoggerMiddleware within an Express application by creating a request-scoped query bus. This example shows middleware setup, handler registration, and query execution within route handlers, including log output examples. ```typescript import { Request, Response, NextFunction } from 'express'; import { Bus, Query, QueryLoggerMiddleware } from '@evyweb/simple-ddd-toolkit'; // Express middleware to create a request-scoped query bus app.use((req: Request, res: Response, next: NextFunction) => { const requestId = req.id || uuid(); const queryBus = new Bus(); queryBus.use(new QueryLoggerMiddleware(logger, `HTTP-${requestId}`)); // Register all handlers queryBus.register(() => new GetUserByIdQueryHandler(userRepository)); queryBus.register(() => new SearchUsersQueryHandler(userRepository)); // Attach to request object req.queryBus = queryBus; next(); }); // Route handler app.get('/users/:id', async (req, res) => { const query = new GetUserByIdQuery(req.params.id); try { const user = await req.queryBus.execute(query); // Logs: [2024-02-01T10:30:45.123Z][HTTP-550e8400-...][GetUserByIdQuery] - {"userId":"user-123"} res.send(user); } catch (error) { res.status(404).send({ error: 'Not found' }); } }); app.get('/users/search', async (req, res) => { const query = new SearchUsersQuery( req.query.q as string, parseInt(req.query.limit as string) || 50 ); const results = await req.queryBus.execute(query); // Logs: [2024-02-01T10:30:46.456Z][HTTP-550e8400-...][SearchUsersQuery] - {"searchTerm":"alice","limit":50,"offset":0} res.send(results); }); ``` -------------------------------- ### Event Logging Format Example Source: https://github.com/evyweb/simple-ddd-toolkit/blob/master/_autodocs/api-reference/EventLoggingMiddleware.md An example of the structured log output generated by the EventLoggingMiddleware for a dispatched domain event. ```text [2024-01-28T01:06:59.782Z] Event "UserCreatedEvent" occurred with ID "550e8400-e29b-41d4-a716-446655440000". Payload: {"userId":"user-123","name":"Alice"} - Metadata: {"correlationId":"req-456"} ``` -------------------------------- ### Example: Adding a Command Logger Middleware Source: https://github.com/evyweb/simple-ddd-toolkit/blob/master/_autodocs/api-reference/Bus.md Shows how to instantiate and register a `CommandLoggerMiddleware`. This middleware logs messages before they are processed by handlers. ```typescript import { CommandLoggerMiddleware } from '@evyweb/simple-ddd-toolkit'; const logger = { log: (msg: string) => console.log(msg) }; const middleware = new CommandLoggerMiddleware(logger, 'RequestId-123'); bus.use(middleware); ``` -------------------------------- ### QueryLoggerMiddleware Log Example Source: https://github.com/evyweb/simple-ddd-toolkit/blob/master/_autodocs/api-reference/QueryLoggerMiddleware.md An example of the log output format generated by the QueryLoggerMiddleware, showing timestamp, middleware ID, query tag, and query data. ```text [2024-02-01T10:30:45.123Z][QueryBus-1][GetUserByIdQuery] - {"userId":"user-123"} ``` -------------------------------- ### Command Log Format Example Source: https://github.com/evyweb/simple-ddd-toolkit/blob/master/_autodocs/api-reference/CommandLoggerMiddleware.md Illustrates the expected format for command logs, including timestamp, middleware ID, command tag, and stringified command data. ```text [2024-02-01T10:30:45.123Z][RequestId-abc123][CreateUserCommand] - {"name":"Alice","email":"alice@example.com"} ``` -------------------------------- ### Example: Registering a Command Handler Source: https://github.com/evyweb/simple-ddd-toolkit/blob/master/_autodocs/api-reference/Bus.md Demonstrates how to register a `UpdateNameCommandHandler` for the `UpdateNameCommand`. Ensure the handler and command share the same `__TAG`. ```typescript import { Bus, Command, CommandHandler } from '@evyweb/simple-ddd-toolkit'; class UpdateNameCommand extends Command { readonly __TAG = 'UpdateNameCommand'; constructor(readonly name: string) { super(); } } class UpdateNameCommandHandler extends CommandHandler { readonly __TAG = 'UpdateNameCommandHandler'; async handle(command: UpdateNameCommand): Promise { console.log(`Updating name to: ${command.name}`); } } const bus = new Bus(); bus.register(() => new UpdateNameCommandHandler()); ``` -------------------------------- ### Example: Executing a Command Source: https://github.com/evyweb/simple-ddd-toolkit/blob/master/_autodocs/api-reference/Bus.md Demonstrates executing an `UpdateNameCommand` using the bus. The command will be processed by the registered `UpdateNameCommandHandler` after passing through any registered middleware. ```typescript const command = new UpdateNameCommand('Alice'); const result = await bus.execute(command); // Handler is called through middleware chain, then event handlers process it ``` -------------------------------- ### Example Logger Implementation and EventBus Usage Source: https://github.com/evyweb/simple-ddd-toolkit/blob/master/_autodocs/types.md Demonstrates creating a console logger and integrating it with an EventBus using middleware. Ensure EventBus and EventLoggingMiddleware are imported. ```typescript const consoleLogger: Logger = { log: (message: string) => console.log(message) }; const eventBus = new EventBus(); eventBus.use(new EventLoggingMiddleware(consoleLogger)); ``` -------------------------------- ### Implement CreateUserUseCase Source: https://github.com/evyweb/simple-ddd-toolkit/blob/master/_autodocs/types.md Example implementation of the IUseCase interface for creating a user. Requires UserRepository and EventBus dependencies. Handles optional request data and dispatches domain events. ```typescript interface CreateUserRequest { name: string; email: string; } class CreateUserUseCase implements IUseCase { constructor( private userRepository: UserRepository, private eventBus: EventBus ) {} async execute(request?: CreateUserRequest): Promise { if (!request) return; const user = UserAggregate.create(UUID.create(uuid()), request.name); await this.userRepository.save(user); await user.dispatchEvents(this.eventBus); } } ``` -------------------------------- ### Complex Command Log Output Example Source: https://github.com/evyweb/simple-ddd-toolkit/blob/master/_autodocs/api-reference/CommandLoggerMiddleware.md Shows the log format for a complex command, illustrating how nested or multiple properties within the command are serialized and logged. ```text [2024-02-01T10:30:46.456Z][RequestId-abc123][BulkUpdateProductsCommand] - {"productIds":["prod-1","prod-2","prod-3"],"updates":{"price":99.99,"stock":100},"reason":"promotion"} ``` -------------------------------- ### Register and Execute Command with Bus Source: https://github.com/evyweb/simple-ddd-toolkit/blob/master/_autodocs/api-reference/Command.md Instantiate a Bus for Commands, register a handler, create a command instance, and execute it using the bus. This example demonstrates basic command dispatching. ```typescript import { Bus } from '@evyweb/simple-ddd-toolkit'; const commandBus = new Bus(); commandBus.register(() => new CreateUserCommandHandler()); const command = new CreateUserCommand('Alice', 'alice@example.com'); await commandBus.execute(command); ``` -------------------------------- ### Example Payload Usage Source: https://github.com/evyweb/simple-ddd-toolkit/blob/master/_autodocs/types.md Demonstrates how to create a Payload object with specific domain event data, including identifiers, names, and timestamps. ```typescript const payload: Payload = { userId: '550e8400-e29b-41d4-a716-446655440000', oldName: 'Alice', newName: 'Bob', changedAt: new Date().toISOString() }; ``` -------------------------------- ### Example Usage of Metadata Type Source: https://github.com/evyweb/simple-ddd-toolkit/blob/master/_autodocs/types.md Demonstrates how to create a Metadata object with various contextual properties like userId, correlationId, and ipAddress. ```typescript const metadata: Metadata = { userId: 'user-123', correlationId: 'req-456', requestId: 'xyz-789', ipAddress: '192.168.1.1' }; ``` -------------------------------- ### User Entity Example Source: https://github.com/evyweb/simple-ddd-toolkit/blob/master/_autodocs/api-reference/Entity.md Demonstrates the creation and usage of a User entity, extending the base Entity class. Includes methods for creating a user, retrieving their name, and updating their name. ```typescript class User extends Entity<{ id: UUID; name: string }> { static create(id: UUID, name: string): User { return new User({ id, name }); } getName(): string { return this.get('name'); } } const user = User.create(UUID.create('550e8400-e29b-41d4-a716-446655440000'), 'Alice'); user.id(); // '550e8400-e29b-41d4-a716-446655440000' const user = User.create(uuid, 'Bob'); user.get('name'); // 'Bob' user.get('id'); // UUID object class User extends Entity<{ id: UUID; name: string }> { changeName(newName: string): void { this.set('name', newName); } } const user = User.create(uuid, 'Charlie'); user.changeName('David'); user.get('name'); // 'David' const uuid = UUID.create('550e8400-e29b-41d4-a716-446655440000'); const user1 = User.create(uuid, 'Eve'); const user2 = User.create(uuid, 'Frank'); user1.equals(user2); // true (same UUID, even though different name) const newUser = User.create(uuid, 'Grace'); newUser.isNew(); // true const existingUser = User.create(UUID.createFrom('550e8400-e29b-41d4-a716-446655440000'), 'Henry'); existingUser.isNew(); // false const user = User.create(uuid, 'Iris'); const data = user.toObject(); // data is frozen and cannot be modified ``` -------------------------------- ### Create a new Bus instance Source: https://github.com/evyweb/simple-ddd-toolkit/blob/master/_autodocs/api-reference/Bus.md Instantiates a new Bus with an empty handler registry and middleware chain. No specific setup is required beyond importing the Bus class. ```typescript constructor() ``` -------------------------------- ### Command with Nested Data Log Output Example Source: https://github.com/evyweb/simple-ddd-toolkit/blob/master/_autodocs/api-reference/CommandLoggerMiddleware.md Illustrates the log output for a command containing deeply nested data structures, such as an order with items and shipping address. ```text [2024-02-01T10:30:47.789Z][RequestId-abc123][PlaceOrderCommand] - {"userId":"user-123","items":[{"productId":"prod-456","quantity":2,"price":29.99}],"shippingAddress":{"street":"123 Main St","city":"Springfield"}} ``` -------------------------------- ### Email ValueObject with Get Method Source: https://github.com/evyweb/simple-ddd-toolkit/blob/master/_autodocs/api-reference/ValueObject.md Illustrates creating an 'Email' ValueObject and using the `get` method to retrieve a specific property ('address') from its internal data. This shows how to access immutable data safely. ```typescript class Email extends ValueObject<{ address: string }> { constructor(address: string) { super({ address }); } getAddress(): string { return this.get('address'); } } const email = new Email('user@example.com'); email.get('address'); // 'user@example.com' ``` -------------------------------- ### Color ValueObject Example Source: https://github.com/evyweb/simple-ddd-toolkit/blob/master/_autodocs/api-reference/ValueObject.md Demonstrates creating a concrete ValueObject subclass 'Color' and comparing instances for equality using the `equals` method. This highlights how ValueObjects are compared by their data attributes. ```typescript class Color extends ValueObject<{ red: number; green: number; blue: number }> { constructor(red: number, green: number, blue: number) { super({ red, green, blue }); } } const color1 = new Color(255, 0, 0); const color2 = new Color(255, 0, 0); color1.equals(color2); // true ``` -------------------------------- ### Custom Logger Implementations for EventLoggingMiddleware Source: https://github.com/evyweb/simple-ddd-toolkit/blob/master/_autodocs/api-reference/EventLoggingMiddleware.md Provides examples of custom logger implementations, FileLogger and StructuredLogger, that adhere to the Logger interface. These can be used with EventLoggingMiddleware for different logging strategies. ```typescript import { Logger } from '@evyweb/simple-ddd-toolkit'; class FileLogger implements Logger { constructor(private filePath: string) {} log(message: string): void { const timestamp = new Date().toISOString(); const logLine = `${timestamp} - ${message}\n`; appendToFile(this.filePath, logLine); } } class StructuredLogger implements Logger { constructor(private loggerClient: any) {} log(message: string): void { // Parse the message or extract relevant info this.loggerClient.info({ type: 'domain_event', message, timestamp: new Date() }); } } const eventBus = new EventBus(); eventBus.use(new EventLoggingMiddleware(new FileLogger('./events.log'))); eventBus.use(new EventLoggingMiddleware(new StructuredLogger(cloudLogger))); ``` -------------------------------- ### Example of Entity with UserData Source: https://github.com/evyweb/simple-ddd-toolkit/blob/master/_autodocs/types.md Illustrates how to define a UserData interface conforming to the EntityData constraint and extend the Entity class. This ensures entities have a UUID id. ```typescript interface UserData { id: UUID; name: string; email: string; } class User extends Entity { // Implementation } ``` -------------------------------- ### Complete Order Use Case Composition Source: https://github.com/evyweb/simple-ddd-toolkit/blob/master/_autodocs/api-reference/IUseCase.md Demonstrates composing multiple use cases to fulfill a complex business process. This example chains 'CreateOrderUseCase' and 'ProcessPaymentUseCase' to complete an order. ```typescript // Composition of multiple use cases class CompleteOrderUseCase { constructor( private createOrderUseCase: CreateOrderUseCase, private processPaymentUseCase: ProcessPaymentUseCase ) {} async execute(request: { userId: string; items: Array<{ productId: string; quantity: number }>; shippingAddress: Address; paymentMethod: PaymentMethod; }): Promise { // Create order await this.createOrderUseCase.execute({ userId: request.userId, items: request.items, shippingAddress: request.shippingAddress }); // Process payment const order = await orderRepository.findLatestByUserId(request.userId); await this.processPaymentUseCase.execute({ orderId: order.id(), paymentMethod: request.paymentMethod }); } } ``` -------------------------------- ### Defining and Using Custom Domain Errors Source: https://github.com/evyweb/simple-ddd-toolkit/blob/master/_autodocs/errors.md Shows how to extend DomainError to create specific domain error types like EmailAlreadyExistsError and InsufficientFundsError. Includes examples of catching these errors and using them with a Result pattern. ```typescript class EmailAlreadyExistsError extends DomainError { public readonly __TAG = 'EmailAlreadyExistsError'; constructor(email: string) { super(`Email ${email} is already registered`); } } class InsufficientFundsError extends DomainError { public readonly __TAG = 'InsufficientFundsError'; constructor(available: number, required: number) { super(`Available: ${available}, Required: ${required}`); } } // Catching domain errors try { const user = await userService.createUser(email); } catch (error) { if (error instanceof EmailAlreadyExistsError) { console.log('User chose an email that exists'); } } // Using with Result pattern class UserService { async createUser(email: string): Promise> { const existing = await this.userRepository.findByEmail(email); if (existing) { return Result.fail(new EmailAlreadyExistsError(email)); } // ... create user return Result.ok(user); } } ``` -------------------------------- ### Create Order Use Case with Transactions Source: https://github.com/evyweb/simple-ddd-toolkit/blob/master/_autodocs/api-reference/IUseCase.md Implement a use case that performs multiple operations within a single transaction to ensure data consistency. This example demonstrates validating products, creating an order, reserving inventory, persisting the order, and dispatching events. ```typescript interface CreateOrderRequest { userId: string; items: Array<{ productId: string; quantity: number }>; shippingAddress: Address; } class CreateOrderUseCase implements IUseCase { constructor( private orderRepository: OrderRepository, private productRepository: ProductRepository, private inventoryService: InventoryService, private eventBus: EventBus, private transactionManager: TransactionManager ) {} async execute(request: CreateOrderRequest): Promise { if (!request) return; return this.transactionManager.transaction(async () => { // Validate products exist and have inventory const products = await Promise.all( request.items.map(item => this.productRepository.findById(item.productId) ) ); // Create order aggregate const order = OrderAggregate.create( request.userId, request.items, request.shippingAddress ); // Reserve inventory for (const item of request.items) { await this.inventoryService.reserve(item.productId, item.quantity); } // Persist await this.orderRepository.save(order); // Dispatch events (will trigger inventory updates, notifications, etc.) await order.dispatchEvents(this.eventBus); }); } } ``` -------------------------------- ### Get Entity Properties Source: https://github.com/evyweb/simple-ddd-toolkit/blob/master/README.md Retrieve 'id' and 'name' from a User entity using the 'get' method. The 'id' is a UUID object; use 'get("value")' or the 'id()' shortcut to access its raw value. ```typescript const user = User.create({ id: UUID.create(), name: "John Doe" }); user.get("id"); // UUID user.get("name"); // 'John Doe' ``` ```typescript const userId = user.get("id").get("value"); ``` ```typescript const userId = user.id(); // Similar to user.get('id').get('value') ``` -------------------------------- ### Basic Use Case Implementation Source: https://github.com/evyweb/simple-ddd-toolkit/blob/master/_autodocs/api-reference/IUseCase.md Demonstrates a basic use case for creating a user, including domain logic, persistence, and event dispatching. Requires importing IUseCase and other necessary services. ```typescript import { IUseCase } from '@evyweb/simple-ddd-toolkit'; interface CreateUserRequest { name: string; email: string; password: string; } class CreateUserUseCase implements IUseCase { constructor( private userRepository: UserRepository, private passwordService: PasswordService, private eventBus: EventBus ) {} async execute(request: CreateUserRequest): Promise { if (!request) return; // Domain logic const user = UserAggregate.create( UUID.create(uuid()), request.name ); user.setEmail(request.email); user.setPassword(await this.passwordService.hash(request.password)); // Persistence await this.userRepository.save(user); // Event dispatch await user.dispatchEvents(this.eventBus); } } // Usage const useCase = new CreateUserUseCase(userRepository, passwordService, eventBus); await useCase.execute({ name: 'Alice', email: 'alice@example.com', password: 'secure-password' }); ``` -------------------------------- ### Get Value Object Properties Source: https://github.com/evyweb/simple-ddd-toolkit/blob/master/README.md Retrieve specific properties of a value object using the `get` method. Autocomplete suggestions are available for property names. ```typescript const color = Color.fromRGB({ red: 255, green: 255, blue: 255 }); color.get("red"); // 255 color.get("green"); // 255 color.get("blue"); // 255 ``` -------------------------------- ### get(key: Key) Source: https://github.com/evyweb/simple-ddd-toolkit/blob/master/_autodocs/api-reference/Entity.md Retrieves a property value from the entity's data. ```APIDOC ## get(key: Key) ### Description Retrieves a property value from the entity's data. ### Parameters #### Path Parameters - **key** (`Key extends keyof EntityData`) - Required - The property key to retrieve ### Returns `EntityData[Key]` - The value of the requested property ### Example ```typescript const user = User.create(uuid, 'Bob'); user.get('name'); // 'Bob' user.get('id'); // UUID object ``` ``` -------------------------------- ### Using EventBusPort with Different Implementations Source: https://github.com/evyweb/simple-ddd-toolkit/blob/master/_autodocs/api-reference/EventBusPort.md Demonstrates how to instantiate and configure both a production event bus with middleware and logging, and a mock event bus for testing. The same service can be used with either implementation. ```typescript // Production: real event bus const productionBus: EventBusPort = new EventBus(); productionBus.use(new EventLoggingMiddleware(productionLogger)); productionBus.on('UserCreatedEvent', () => new SendEmailHandler()); // Testing: mock event bus with no side effects const testBus: EventBusPort = new MockEventBus(); // Same service works with both const service = new UserService(productionBus); const testService = new UserService(testBus); ``` -------------------------------- ### Use the Command Bus to Execute a Command Source: https://github.com/evyweb/simple-ddd-toolkit/blob/master/_autodocs/README.md Demonstrates registering a command handler and executing a command using the Command Bus. Commands represent intent to change the system's state. ```typescript import { Bus, Command, CommandHandler } from '@evyweb/simple-ddd-toolkit'; class CreateUserCommand extends Command { public readonly __TAG = 'CreateUserCommand'; constructor(public readonly name: string) { super(); } } class CreateUserCommandHandler extends CommandHandler { public readonly __TAG = 'CreateUserCommandHandler'; async handle(command: CreateUserCommand): Promise { console.log(`Creating user: ${command.name}`); } } const bus = new Bus(); bus.register(() => new CreateUserCommandHandler()); await bus.execute(new CreateUserCommand('Alice')); ``` -------------------------------- ### Event Handler Dispatching Commands Source: https://github.com/evyweb/simple-ddd-toolkit/blob/master/_autodocs/api-reference/IEventHandler.md Show how an event handler can dispatch commands to other parts of the system, for example, sending an invoice after an order is completed. ```typescript import { Command, CommandHandler, Bus } from '@evyweb/simple-ddd-toolkit'; class SendInvoiceCommand extends Command { public readonly __TAG = 'SendInvoiceCommand'; constructor( public readonly orderId: string, public readonly userId: string ) { super(); } } class OrderCompletedEvent extends DomainEvent { public readonly __TAG = 'OrderCompletedEvent'; } class SendInvoiceHandler implements IEventHandler { public readonly __TAG = 'OrderCompletedEvent'; constructor(private readonly commandBus: Bus) {} async handle(event: OrderCompletedEvent): Promise { const { orderId, userId } = event.payload; const command = new SendInvoiceCommand(orderId, userId); await this.commandBus.execute(command); } } ``` -------------------------------- ### use Source: https://github.com/evyweb/simple-ddd-toolkit/blob/master/_autodocs/api-reference/EventBus.md Registers a middleware to be executed for all dispatched events. Middleware is processed in the order of registration. ```APIDOC ## use ### Description Registers a middleware to execute for all events. Middleware is executed in registration order. ### Method ```typescript use(middleware: IEventMiddleware): void ``` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Parameters - **middleware** (`IEventMiddleware`) - Required - The middleware to add to the chain. ### Request Example ```typescript import { EventBus, EventLoggingMiddleware } from '@evyweb/simple-ddd-toolkit'; const logger = { log: (msg: string) => console.log(msg) }; const eventBus = new EventBus(); eventBus.use(new EventLoggingMiddleware(logger)); ``` ### Response None ``` -------------------------------- ### Entity Instantiation via Static Factory Method Source: https://github.com/evyweb/simple-ddd-toolkit/blob/master/README.md Shows the correct way to create an Entity instance using its static factory method. ```typescript const user = User.create({ id: UUID.create(), name: "John Doe" }); ``` -------------------------------- ### Instantiate Value Object Using Constructor Source: https://github.com/evyweb/simple-ddd-toolkit/blob/master/README.md Shows how to directly use the constructor to create a value object instance. It is recommended to use static factory methods for validation. ```typescript export class Color extends ValueObject { constructor({ red, green, blue }: RGBColor) { // Validate the red, green, and blue values here super({ red, green, blue }); } // Other methods } ``` ```typescript const color = new Color({ red: 255, green: 0, blue: 0 }); ``` -------------------------------- ### Retrieve Internal UUID Data Source: https://github.com/evyweb/simple-ddd-toolkit/blob/master/_autodocs/api-reference/UUID.md The `get` instance method allows you to retrieve the internal data of the UUID object. You can access the 'value' (the UUID string) or the 'isNew' flag. ```typescript const uuid = UUID.create('550e8400-e29b-41d4-a716-446655440000'); uuid.get('value'); // '550e8400-e29b-41d4-a716-446655440000' uuid.get('isNew'); // true ``` -------------------------------- ### Implement a Domain Event Handler Source: https://github.com/evyweb/simple-ddd-toolkit/blob/master/_autodocs/api-reference/DomainEvent.md Shows how to implement the IEventHandler interface to handle specific domain events, like sending a welcome email when a UserCreatedEvent occurs. Includes registering the handler with an event bus. ```typescript import { IEventHandler } from '@evyweb/simple-ddd-toolkit'; class SendWelcomeEmailHandler implements IEventHandler { public readonly __TAG = 'UserCreatedEvent'; constructor(private readonly emailService: EmailService) {} async handle(event: UserCreatedEvent): Promise { const { userId, name, email } = event.payload; await this.emailService.send({ to: email, subject: 'Welcome!', body: `Hello ${name}, welcome to our service!` }); console.log(`Sent welcome email for user ${userId}`); } } // Register with event bus const eventBus = new EventBus(); eventBus.on('UserCreatedEvent', () => new SendWelcomeEmailHandler(emailService)); ``` -------------------------------- ### Get Error from Failed Result Source: https://github.com/evyweb/simple-ddd-toolkit/blob/master/_autodocs/api-reference/Result.md The `getError` method retrieves the error from a Result instance. It should only be called after confirming the result is a failure using `isFail()`, otherwise it throws an error. ```typescript const result = Result.fail(new Error('Operation failed')); if (result.isFail()) { const error = result.getError(); // Safe because we checked isFail() console.log(error.message); // 'Operation failed' } ``` -------------------------------- ### Register Query Handler with IOC Container Source: https://github.com/evyweb/simple-ddd-toolkit/blob/master/README.md Alternatively, register query handlers by resolving them from an IOC container. This example shows registration using a hypothetical `container` and `DI.LoadCharacterCreationDialogQueryHandler`. ```typescript queryBus.register(() => container.get(DI.LoadCharacterCreationDialogQueryHandler)); ``` -------------------------------- ### Bus Constructor Source: https://github.com/evyweb/simple-ddd-toolkit/blob/master/_autodocs/api-reference/Bus.md Creates a new Bus instance. It initializes an empty handler registry and middleware chain. ```APIDOC ## Constructor Bus ### Description Creates a new Bus instance with an empty handler registry and middleware chain. ### Signature ```typescript constructor() ``` ``` -------------------------------- ### Get Value from Successful Result Source: https://github.com/evyweb/simple-ddd-toolkit/blob/master/_autodocs/api-reference/Result.md The `getValue` method retrieves the success value from a Result instance. It should only be called after confirming the result is a success using `isOk()`, otherwise it throws an error. ```typescript const result = Result.ok(42); if (result.isOk()) { const value = result.getValue(); // Safe because we checked isOk() console.log(value); // 42 } ``` -------------------------------- ### Create and Use an Order Aggregate Source: https://github.com/evyweb/simple-ddd-toolkit/blob/master/README.md Demonstrates how to extend the Aggregate class to create a custom aggregate like Order. Includes methods for creating an order and adding items, with validation. ```typescript import { Aggregate, UUID } from "@evyweb/simple-ddd-toolkit"; interface OrderItem { productId: string; quantity: number; } interface OrderData { id: UUID; items: OrderItem[]; date: Date; } export class Order extends Aggregate { static create(orderData: OrderData): Order { // Validation rules here return new Order(orderData); } addItem(productId: string, quantity: number): void { if (this.get("items").length >= 10) { throw new Error("An order cannot contain more than 10 items."); } const item = {productId, quantity}; this.get("items").push(item); } } const order = await orderRepository.getById("order-id"); order.addItem("product1", 2); orderRepository.save(order); ``` -------------------------------- ### Using QueryHandler with Bus Source: https://github.com/evyweb/simple-ddd-toolkit/blob/master/_autodocs/api-reference/QueryHandler.md Demonstrates how to register QueryHandlers with a QueryBus and execute queries through it. ```typescript const queryBus = new Bus(); // Register handlers queryBus.register(() => new GetUserByIdQueryHandler(userRepository)); queryBus.register(() => new ListAllUsersQueryHandler(userRepository)); // Execute queries const user = await queryBus.execute( new GetUserByIdQuery('user-123') ); const users = await queryBus.execute( new ListAllUsersQuery(100) ); ``` -------------------------------- ### Query with Search Parameters Log Output Source: https://github.com/evyweb/simple-ddd-toolkit/blob/master/_autodocs/api-reference/QueryLoggerMiddleware.md This log entry demonstrates the output for a query that includes search parameters, such as a search term, limit, and offset. ```text [2024-02-01T10:30:46.456Z][QueryBus-1][SearchUsersQuery] - {"searchTerm":"alice","limit":50,"offset":0} ``` -------------------------------- ### Create an Aggregate with User Creation Event Source: https://github.com/evyweb/simple-ddd-toolkit/blob/master/_autodocs/README.md Illustrates creating an Aggregate Root for a user, including adding a domain event. Aggregates encapsulate entities and manage their consistency. ```typescript import { Aggregate, UUID, DomainEvent } from '@evyweb/simple-ddd-toolkit'; class UserCreatedEvent extends DomainEvent { public readonly __TAG = 'UserCreatedEvent'; } class UserAggregate extends Aggregate<{ id: UUID; name: string }> { static create(id: UUID, name: string): UserAggregate { const aggregate = new UserAggregate({ id, name }); aggregate.addEvent(new UserCreatedEvent({ eventId: '550e8400-e29b-41d4-a716-446655440000' })); return aggregate; } } ``` -------------------------------- ### Initialize EventLoggingMiddleware Source: https://github.com/evyweb/simple-ddd-toolkit/blob/master/_autodocs/api-reference/EventLoggingMiddleware.md Creates a new EventLoggingMiddleware instance. Requires a logger implementation to be passed during construction. ```typescript constructor(logger: Logger) ``` -------------------------------- ### Logged Use Case Decorator Source: https://github.com/evyweb/simple-ddd-toolkit/blob/master/_autodocs/api-reference/IUseCase.md A decorator pattern implementation for adding logging to any use case. It wraps an existing use case, logging its start, completion, or failure along with execution duration. ```typescript import { Logger } from '@evyweb/simple-ddd-toolkit'; class LoggedUseCase implements IUseCase { constructor( private useCase: IUseCase, private logger: Logger, private useCaseName: string ) {} async execute(request?: T): Promise { this.logger.log(`Starting use case: ${this.useCaseName}`); const startTime = Date.now(); try { await this.useCase.execute(request); const duration = Date.now() - startTime; this.logger.log(`Completed use case: ${this.useCaseName} in ${duration}ms`); } catch (error) { const duration = Date.now() - startTime; this.logger.log(`Failed use case: ${this.useCaseName} after ${duration}ms: ${error.message}`); throw error; } } } // Usage const createUserUseCase = new CreateUserUseCase(repo, service, bus); const logged = new LoggedUseCase(createUserUseCase, logger, 'CreateUserUseCase'); await logged.execute(request); ``` -------------------------------- ### Basic Entity Implementation Source: https://github.com/evyweb/simple-ddd-toolkit/blob/master/README.md Extend the Entity class and define a static create method for instantiation. Ensure necessary imports from the toolkit. ```typescript import { Entity } from "@evyweb/simple-ddd-toolkit"; import { UUID } from "@evyweb/simple-ddd-toolkit"; interface UserData { id: UUID; name: string; } export class User extends Entity { static create(userData: UserData): User { // Validation rules here return new User(userData); } } ``` -------------------------------- ### Register Middleware with Command and Query Buses Source: https://github.com/evyweb/simple-ddd-toolkit/blob/master/README.md Register custom middleware instances with the command bus and query bus using the 'use' method. Middleware executes in registration order. ```typescript commandBus.use(new CommandLoggerMiddleware(logger, "CommandLoggerMiddleware")); queryBus.use(new QueryLoggerMiddleware(logger, "QueryLoggerMiddleware")); ``` -------------------------------- ### EventLoggingMiddleware Constructor Source: https://github.com/evyweb/simple-ddd-toolkit/blob/master/_autodocs/api-reference/EventLoggingMiddleware.md Creates a new EventLoggingMiddleware that logs events using the provided logger. ```APIDOC ## Constructor ### Description Creates a new EventLoggingMiddleware that logs events using the provided logger. ### Parameters #### Path Parameters - **logger** (Logger) - Required - The logger interface to use for writing log messages ``` -------------------------------- ### Simple Event Handler Implementation Source: https://github.com/evyweb/simple-ddd-toolkit/blob/master/_autodocs/api-reference/IEventHandler.md Implement a basic event handler to react to a domain event and perform a simple action, such as sending a welcome email. ```typescript import { IEventHandler, DomainEvent } from '@evyweb/simple-ddd-toolkit'; class UserCreatedEvent extends DomainEvent { public readonly __TAG = 'UserCreatedEvent'; } class SendWelcomeEmailHandler implements IEventHandler { public readonly __TAG = 'UserCreatedEvent'; constructor(private readonly emailService: EmailService) {} async handle(event: UserCreatedEvent): Promise { const { email, name } = event.payload; await this.emailService.sendWelcome(email, name); } } ``` -------------------------------- ### Basic Result Usage with Type Guards Source: https://github.com/evyweb/simple-ddd-toolkit/blob/master/_autodocs/api-reference/Result.md Demonstrates creating a Result for a user creation operation and checking its outcome using `isOk()` and `isFail()` with type guards. ```typescript import { Result } from '@evyweb/simple-ddd-toolkit'; class UserService { async createUser(name: string, email: string): Promise> { try { const user = await this.userRepository.save({ name, email }); return Result.ok<{ id: string }, Error>({ id: user.id }); } catch (error) { return Result.fail<{ id: string }, Error>( new Error('Failed to create user') ); } } } const service = new UserService(); const result = await service.createUser('Alice', 'alice@example.com'); if (result.isOk()) { console.log('User created with ID:', result.getValue().id); } else { console.error('Error:', result.getError().message); } ``` -------------------------------- ### EventBus Constructor Source: https://github.com/evyweb/simple-ddd-toolkit/blob/master/_autodocs/api-reference/EventBus.md Creates a new EventBus instance. It initializes with an empty handler registry and middleware chain. ```APIDOC ## Constructor EventBus ### Description Creates a new EventBus instance with an empty handler registry and middleware chain. ### Signature ```typescript constructor() ``` ``` -------------------------------- ### Use the Event Bus to Dispatch an Event Source: https://github.com/evyweb/simple-ddd-toolkit/blob/master/_autodocs/README.md Shows how to subscribe to domain events and dispatch them using the Event Bus. Events represent something that has happened in the domain. ```typescript import { EventBus, DomainEvent, IEventHandler } from '@evyweb/simple-ddd-toolkit'; class UserCreatedEvent extends DomainEvent { public readonly __TAG = 'UserCreatedEvent'; } class SendWelcomeEmailHandler implements IEventHandler { public readonly __TAG = 'UserCreatedEvent'; async handle(event: UserCreatedEvent): Promise { const { userId } = event.payload; // Send welcome email } } const eventBus = new EventBus(); eventBus.on('UserCreatedEvent', () => new SendWelcomeEmailHandler()); const event = new UserCreatedEvent({ eventId: '550e8400-e29b-41d4-a716-446655440000', payload: { userId: 'user-123' } }); await eventBus.dispatch(event); ``` -------------------------------- ### execute Method Source: https://github.com/evyweb/simple-ddd-toolkit/blob/master/_autodocs/api-reference/IUseCase.md The `execute` method is the primary entry point for a use case. It takes an optional request object and performs the application-specific business logic, potentially interacting with domain aggregates, repositories, and other services. It returns a Promise that resolves when the use case is complete. ```APIDOC ## execute Method ### Description Executes the use case with the provided request. This is the main entry point for applying application-level business logic. ### Method Signature ```typescript execute(request?: Request): Promise ``` ### Parameters #### request - **request** (`Request`) - Optional - The request containing input data for the use case. ### Returns - `Promise` — Resolves when the use case completes. ### Implementation Notes - Must be async (returns a Promise). - Should orchestrate domain operations (aggregates, repositories, event bus). - Can throw errors or return results via Result pattern. - Typically doesn't return values (void); use Command/Query pattern for values. ``` -------------------------------- ### Implement Multiple Static Factory Methods for Value Objects Source: https://github.com/evyweb/simple-ddd-toolkit/blob/master/README.md Define various static factory methods to create value objects from different input formats. The 'from' prefix is a convention for methods that construct objects from specific representations. ```typescript const color1 = Color.fromRGB({ red: 255, green: 0, blue: 0 }); const color2 = Color.fromHEX("#FF0000"); ```