--- id: BasketDeletedCompletedEvent version: 1.0.0 name: Basket Deleted Complete summary: Represents a domain event that is published when reverse basket is completed badges: - content: "Broker:AMQP" backgroundColor: green textColor: white icon: ArrowPathIcon schemaPath: schema.json owners: - nhanxnguyen --- ## Overview The `PublishCompletedEvent` is an integration event that signals the successful completion of a reverse basket operation in the system. This event serves as a confirmation mechanism between the Basket and Ordering contexts, indicating that a basket has been successfully deleted and the associated order has been processed. It carries essential information about the completed operation, including the order and basket identifiers. ## Architecture ## Purpose This event serves several important purposes: - Confirms successful basket deletion operations - Maintains system consistency between Basket and Ordering contexts - Provides audit trail for completed operations - Facilitates proper state management in the system ## Event Structure The event contains the following key information: - `OrderId`: Unique identifier for the associated order - `BasketId`: Identifier of the successfully deleted basket - `TotalMoney`: Total monetary value of the basket at the time of deletion ## Usage This event is typically published when: - A basket deletion operation completes successfully - The system needs to confirm the transition from basket to order - Audit trails need to be maintained ## Success Handling When this event is published, the system should: 1. Log the successful completion 2. Update relevant system states 3. Trigger any necessary follow-up processes 4. Maintain audit records ## Raw Schema:schema.json { "id": "BasketDeletedCompletedEvent", "allOf": [ { "id": "integrationEvent", "type": "object", "additionalProperties": false, "properties": { "id": { "type": "string", "format": "guid", "x-parser-schema-id": "" }, "creationDate": { "type": "string", "format": "date-time", "x-parser-schema-id": "" } }, "x-parser-schema-id": "integrationEvent" }, { "type": "object", "additionalProperties": false, "properties": { "orderId": { "type": "string", "format": "guid", "x-parser-schema-id": "" }, "basketId": { "type": "string", "format": "guid", "x-parser-schema-id": "" }, "email": { "type": ["null", "string"], "x-parser-schema-id": "" }, "totalMoney": { "type": "number", "format": "decimal", "x-parser-schema-id": "" } }, "x-parser-schema-id": "" } ], "x-parser-schema-id": "basketDeletedFailedIntegrationEvent" } --- id: BasketDeletedFailedEvent version: 1.0.0 name: Deleted Basket Fail summary: Represents a failed basket deletion event in the system badges: - content: "Broker:AMQP" backgroundColor: green textColor: white icon: ArrowPathIcon schemaPath: schema.json owners: - nhanxnguyen --- ## Overview The `BasketDeletedIntegrationEvent` is an integration event that signals the failure of a reverse basket operation in the system. This event plays a crucial role in the error handling and recovery mechanisms between the Basket and Ordering contexts. It carries essential information about the failed operation, including the reverse basket identity, customer identity, and basket item collection. ## Architecture ## Purpose This event serves several important purposes: - Notifies the system about failed basket deletion attempts - Provides necessary data for error recovery and retry mechanisms - Maintains consistency between the Basket and Ordering contexts - Facilitates proper error handling and logging ## Event Structure The event contains the following key information: - `OrderId`: Unique identifier for the associated order - `BasketId`: Identifier of the basket that failed to delete - `Email`: Customer's email address (optional) - `TotalMoney`: Total monetary value of the basket ## Usage This event is typically published when: - A basket deletion operation fails due to system errors - There are issues with the integration between Basket and Ordering contexts - Recovery mechanisms need to be triggered ## Error Handling When this event is published, the system should: 1. Log the failure details 2. Attempt to recover the failed operation 3. Notify relevant stakeholders if necessary 4. Update system state accordingly ## Raw Schema:schema.json { "id": "BasketDeletedFailedIntegrationEvent", "allOf": [ { "id": "integrationEvent", "type": "object", "additionalProperties": false, "properties": { "id": { "type": "string", "format": "guid", "x-parser-schema-id": "" }, "creationDate": { "type": "string", "format": "date-time", "x-parser-schema-id": "" } }, "x-parser-schema-id": "integrationEvent" }, { "type": "object", "additionalProperties": false, "properties": { "basketId": { "type": "string", "format": "guid", "x-parser-schema-id": "" }, "email": { "type": "string", "x-parser-schema-id": "" }, "orderId": { "type": "string", "format": "guid", "x-parser-schema-id": "" }, "totalMoney": { "type": "number", "format": "double", "x-parser-schema-id": "" } }, "x-parser-schema-id": "" } ], "x-parser-schema-id": "BasketDeletedFailedIntegrationEvent" } --- id: BookUpdatedRatingFailedEvent version: 1.0.0 name: Book Updated Rating Fail summary: >- Represents a failed integration event when updating a book's rating in the system badges: - content: "Broker:AMQP" backgroundColor: green textColor: white icon: ArrowPathIcon schemaPath: schema.json owners: - nhanxnguyen --- ## Overview This event represents a failed integration event when updating a book's rating in the system. The `BookUpdatedRatingFailedIntegrationEvent` is a domain event that captures the failure to update a book's rating in the Catalog bounded context. It carries the necessary value objects including the book identity, the failed rating value, and the error message to notify the system about the failed operation. This event adheres to the ubiquitous language of our domain and serves as the contract between the Catalog and external systems, facilitating the transition from a successful rating update to a failed one. ## Architecture ## Purpose The `BookUpdatedRatingFailedIntegrationEvent` serves several important purposes in the system: 1. **Error Handling**: It provides a mechanism to handle cases where a book rating update fails, ensuring system consistency. 2. **Compensation**: It enables the Rating service to clean up any partially created feedback when the book rating update fails. 3. **System Resilience**: It helps maintain system integrity by allowing services to roll back or clean up failed operations. ## Usage This event is typically published in the following scenarios: 1. When attempting to update a book's rating but the book doesn't exist in the Catalog service 2. When there's a failure in the rating update process in the Catalog service 3. When the integration between Rating and Catalog services fails ### Event Flow 1. The Rating service creates a new feedback 2. The Catalog service attempts to update the book's rating 3. If the update fails, this event is published 4. The Rating service receives the event and deletes the associated feedback ## Event Structure The event contains the following key information: - `FeedbackId`: The unique identifier of the feedback that needs to be deleted - `Timestamp`: When the failure occurred ## Consumer Behavior The `BookUpdatedRatingFailedIntegrationEventHandler` in the Rating service: 1. Receives the event 2. Retrieves the feedback by ID 3. If the feedback exists: - Deletes the feedback - Saves the changes to the database 4. If the feedback doesn't exist, gracefully returns without taking action ## Related Events - `FeedbackCreatedIntegrationEvent`: The event that triggers the rating update process ## Raw Schema:schema.json { "id": "BookUpdatedRatingFailedEvent", "allOf": [ { "id": "integrationEvent", "type": "object", "additionalProperties": false, "properties": { "id": { "type": "string", "format": "guid", "x-parser-schema-id": "" }, "creationDate": { "type": "string", "format": "date-time", "x-parser-schema-id": "" } }, "x-parser-schema-id": "integrationEvent" }, { "type": "object", "additionalProperties": false, "properties": { "feedbackId": { "type": "string", "format": "guid", "x-parser-schema-id": "" } }, "x-parser-schema-id": "" } ], "x-parser-schema-id": "bookUpdatedRatingFailedIntegrationEvent" } --- id: CancelOrderCommand version: 1.0.0 name: Cancelled Order summary: Receive a message when an order is canceled badges: - content: Orchestrated textColor: orange backgroundColor: orange icon: CubeTransparentIcon - content: Notification textColor: blue backgroundColor: blue icon: BellIcon - content: "Broker:AMQP" backgroundColor: green textColor: white icon: ArrowPathIcon schemaPath: schema.json owners: - nhanxnguyen --- ## Overview The `CancelOrderCommand` is an integration event published by the Finance service when an order transitions to the Cancelled state. This command is part of the order processing saga and is used to notify other services about order cancellation, particularly for sending cancellation notifications to customers. ## Architecture ## Message Structure The command contains the following properties: | Property | Type | Description | | ------------ | --------- | ------------------------------------------------------------- | | `OrderId` | `Guid` | Unique identifier for the order being cancelled | | `Email` | `string?` | Customer's email address (optional) for notification purposes | | `TotalMoney` | `decimal` | Total monetary value of the cancelled order | ## Workflow Context This command is published by the Order State Machine when the following conditions are met: 1. The order is in the `Placed` state 2. An `OrderStatusChangedToCancelIntegrationEvent` is received 3. The order transitions to the `Cancelled` state 4. The customer's email is available (not null) ## Consuming Services This command is typically consumed by: - **Notification Service**: To send cancellation emails to customers - **Ordering Service**: To update the order status in the database ## Raw Schema:schema.json { "id": "CancelOrderCommand", "allOf": [ { "id": "integrationEvent", "type": "object", "additionalProperties": false, "properties": { "id": { "type": "string", "format": "guid", "x-parser-schema-id": "" }, "creationDate": { "type": "string", "format": "date-time", "x-parser-schema-id": "" } }, "x-parser-schema-id": "integrationEvent" }, { "type": "object", "additionalProperties": false, "properties": { "orderId": { "type": "string", "format": "guid", "x-parser-schema-id": "" }, "email": { "type": ["null", "string"], "x-parser-schema-id": "" }, "totalMoney": { "type": "number", "format": "decimal", "x-parser-schema-id": "" } }, "x-parser-schema-id": "" } ], "x-parser-schema-id": "cancelOrderCommand" } --- id: CleanUpSentEmailIntegrationEvent version: 1.0.0 name: Clean Up Sent Email summary: Receive a message to trigger cleanup of sent emails for system maintenance badges: - content: Orchestrated textColor: orange backgroundColor: orange icon: CubeTransparentIcon - content: Notification textColor: blue backgroundColor: blue icon: BellIcon - content: "Broker:AMQP" backgroundColor: green textColor: white icon: ArrowPathIcon - content: Scheduler textColor: purple backgroundColor: purple icon: ClockIcon schemaPath: schema.json owners: - nhanxnguyen --- ## Overview The `CleanUpSentEmailIntegrationEvent` is triggered by the Scheduler Service to initiate the cleanup process for sent emails in the notification system. This event is published daily at midnight to maintain system performance and storage efficiency by removing processed email records from the outbox. ### Event Flow 1. **Scheduler Service** publishes the event daily at midnight (cron: `0 0 * * *`) 2. **Notification Service** receives the event and processes it 3. System identifies and removes successfully sent email records from the outbox 4. Storage space is freed up and system performance is maintained ### Business Value - **System Performance**: Prevents database bloat by regularly cleaning up processed records - **Storage Optimization**: Reduces storage costs by removing unnecessary data - **Data Hygiene**: Maintains clean data architecture and improves query performance ## Schema ## Raw Schema:schema.json { "id": "CleanUpSentEmailIntegrationEvent", "description": "Represents an integration event to trigger cleanup of sent emails for system maintenance", "allOf": [ { "id": "integrationEvent", "type": "object", "description": "Base event structure containing common metadata for all integration events in the system", "additionalProperties": false, "properties": { "id": { "type": "string", "format": "guid", "description": "The unique identifier of the integration event", "example": "01961eb4-668d-7e7e-ae25-0fab379614f7", "x-parser-schema-id": "" }, "creationDate": { "type": "string", "format": "date-time", "description": "The creation date of the integration event", "example": "2021-01-01T00:00:00Z", "x-parser-schema-id": "" } }, "x-parser-schema-id": "integrationEvent" }, { "type": "object", "additionalProperties": false, "x-parser-schema-id": "" } ], "x-parser-schema-id": "CleanUpSentEmailIntegrationEvent" } --- id: CompleteOrderCommand version: 1.0.0 name: Completed Order summary: Receive a message when an order is completed badges: - content: Orchestrated textColor: orange backgroundColor: orange icon: CubeTransparentIcon - content: Notification textColor: blue backgroundColor: blue icon: BellIcon - content: "Broker:AMQP" backgroundColor: green textColor: white icon: ArrowPathIcon schemaPath: schema.json owners: - nhanxnguyen --- ## Overview The `CompleteOrderCommand` is an integration event published by the Finance service when an order transitions to the Completed state. This command is part of the order processing saga and is used to notify other services about order completion, particularly for sending confirmation notifications to customers. ## Architecture ## Message Structure The command contains the following properties: | Property | Type | Description | | ------------ | --------- | ------------------------------------------------------------- | | `OrderId` | `Guid` | Unique identifier for the order being completed | | `Email` | `string?` | Customer's email address (optional) for notification purposes | | `TotalMoney` | `decimal` | Total monetary value of the completed order | ## Workflow Context This command is published by the Order State Machine when the following conditions are met: 1. The order is in the `Placed` state 2. An `OrderStatusChangedToCompleteIntegrationEvent` is received 3. The order transitions to the `Completed` state 4. The customer's email is available (not null) ## Consuming Services This command is typically consumed by: - **Notification Service**: To send order completion emails to customers - **Ordering Service**: To update the order status in the database ## Raw Schema:schema.json { "id": "CompleteOrderCommand", "allOf": [ { "id": "integrationEvent", "type": "object", "additionalProperties": false, "properties": { "id": { "type": "string", "format": "guid", "x-parser-schema-id": "" }, "creationDate": { "type": "string", "format": "date-time", "x-parser-schema-id": "" } }, "x-parser-schema-id": "integrationEvent" }, { "type": "object", "additionalProperties": false, "properties": { "orderId": { "type": "string", "format": "guid", "x-parser-schema-id": "" }, "email": { "type": ["null", "string"], "x-parser-schema-id": "" }, "totalMoney": { "type": "number", "format": "decimal", "x-parser-schema-id": "" } }, "x-parser-schema-id": "" } ], "x-parser-schema-id": "completeOrderCommand" } --- id: DeleteBasketCompleteCommand version: 1.0.0 name: Basket Deleted Complete summary: Represents a domain event that is published when reverse basket is completed badges: - content: Orchestrated textColor: orange backgroundColor: orange icon: CubeTransparentIcon - content: Success Flow textColor: green backgroundColor: green icon: ArrowPathIcon - content: "Broker:AMQP" backgroundColor: green textColor: white icon: ArrowPathIcon schemaPath: schema.json owners: - nhanxnguyen --- ## Overview The `DeleteBasketCompleteCommand` is an integration event published by the Finance service when a basket is successfully deleted during the order processing workflow. This command is part of the normal success flow in the order processing saga and is used to notify other services about the successful basket cleanup, allowing them to proceed with subsequent steps in the order fulfillment process. ## Architecture ## Message Structure The command contains the following properties: | Property | Type | Description | | ------------ | --------- | ------------------------------------------ | | `OrderId` | `Guid` | Unique identifier for the associated order | | `TotalMoney` | `decimal` | Total monetary value of the order | ## Workflow Context This command is published by the Order State Machine when the following conditions are met: 1. The order is in the `Placed` state 2. A `BasketDeletedCompleteIntegrationEvent` is received, indicating successful basket deletion 3. The command is published to notify downstream services about the successful operation ## Success Flow This command plays an important role in the successful order processing flow: 1. When a basket is successfully deleted, the `BasketDeletedCompleteIntegrationEvent` is received 2. The state machine processes this event and publishes the `DeleteBasketCompleteCommand` 3. This command signals to downstream services that the basket has been successfully cleaned up 4. The order processing can continue to the next steps ## Consuming Services This command is typically consumed by: - **Ordering Service**: To update the order status and store in event store - **Notification Service**: To send order confirmation to the customer ## Raw Schema:schema.json { "id": "DeleteBasketCompleteCommand", "allOf": [ { "id": "integrationEvent", "type": "object", "additionalProperties": false, "properties": { "id": { "type": "string", "format": "guid", "x-parser-schema-id": "" }, "creationDate": { "type": "string", "format": "date-time", "x-parser-schema-id": "" } }, "x-parser-schema-id": "integrationEvent" }, { "type": "object", "additionalProperties": false, "properties": { "orderId": { "type": "string", "format": "guid", "x-parser-schema-id": "" }, "totalMoney": { "type": "number", "format": "decimal", "x-parser-schema-id": "" } }, "x-parser-schema-id": "" } ], "x-parser-schema-id": "deleteBasketCompleteCommand" } --- id: DeleteBasketFailedCommand version: 1.0.0 name: Deleted Basket Fail summary: Represents a failed integration event when deleting a basket in the system badges: - content: Orchestrated textColor: orange backgroundColor: orange icon: CubeTransparentIcon - content: Error Handling textColor: red backgroundColor: red icon: ExclamationCircleIcon - content: "Broker:AMQP" backgroundColor: green textColor: white icon: ArrowPathIcon schemaPath: schema.json owners: - nhanxnguyen --- ## Overview The `DeleteBasketFailedCommand` is an integration event published by the Finance service when a basket deletion operation fails during the order processing workflow. This command is part of the error handling mechanism in the order processing saga and is used to notify other services about the failure, allowing them to take appropriate recovery actions. ## Architecture ## Message Structure The command contains the following properties: | Property | Type | Description | | ------------ | --------- | ------------------------------------------------------------- | | `BasketId` | `Guid` | Unique identifier for the basket that failed to be deleted | | `Email` | `string?` | Customer's email address (optional) for notification purposes | | `OrderId` | `Guid` | Unique identifier for the associated order | | `TotalMoney` | `decimal` | Total monetary value of the basket/order | ## Workflow Context This command is published by the Order State Machine when the following conditions are met: 1. The order is in the `Placed` state 2. A `BasketDeletedFailedIntegrationEvent` is received 3. The order transitions to the `Failed` state 4. The command is published to notify downstream services ## Error Handling This command plays a critical role in the error handling flow of the order processing saga: 1. When a basket deletion fails, the saga transitions to the `Failed` state 2. The `DeleteBasketFailedCommand` is published to notify downstream services 3. Downstream services can implement recovery mechanisms or compensating transactions 4. The customer may be notified about the failure if an email address is available ## Consuming Services This command is typically consumed by: - **Notification Service**: To send failure notifications to customers - **Ordering Service**: To roll back the order ## Raw Schema:schema.json { "id": "DeleteBasketCompleteCommand", "allOf": [ { "id": "integrationEvent", "type": "object", "additionalProperties": false, "properties": { "id": { "type": "string", "format": "guid", "x-parser-schema-id": "" }, "creationDate": { "type": "string", "format": "date-time", "x-parser-schema-id": "" } }, "x-parser-schema-id": "integrationEvent" }, { "type": "object", "additionalProperties": false, "properties": { "basketId": { "type": "string", "format": "guid", "x-parser-schema-id": "" }, "email": { "type": ["null", "string"], "x-parser-schema-id": "" }, "orderId": { "type": "string", "format": "guid", "x-parser-schema-id": "" } }, "x-parser-schema-id": "" } ], "x-parser-schema-id": "deleteBasketFailedCommand" } --- id: FeedbackCreatedEvent version: 1.0.0 name: Feedback Created summary: >- Represents a successful integration event when creating a feedback in the system badges: - content: "Broker:AMQP" backgroundColor: green textColor: white icon: ArrowPathIcon schemaPath: schema.json owners: - nhanxnguyen --- ## Overview This event represents a successful integration event when creating a feedback in the system. The `FeedbackCreatedIntegrationEvent` is a domain event that captures the creation of a feedback in the Catalog bounded context. It carries the necessary value objects including the feedback identity, the feedback content, and the feedback rating to notify the system about the successful operation. This event adheres to the ubiquitous language of our domain and serves as the contract between the Catalog and external systems, facilitating the transition from a failed feedback creation to a successful one. ## Architecture ## Raw Schema:schema.json { "id": "FeedbackCreatedEvent", "allOf": [ { "id": "integrationEvent", "type": "object", "additionalProperties": false, "properties": { "id": { "type": "string", "format": "guid", "x-parser-schema-id": "" }, "creationDate": { "type": "string", "format": "date-time", "x-parser-schema-id": "" } }, "x-parser-schema-id": "integrationEvent" }, { "type": "object", "additionalProperties": false, "properties": { "bookId": { "type": "string", "format": "guid", "x-parser-schema-id": "" }, "rating": { "type": "integer", "format": "int32", "x-parser-schema-id": "" }, "feedbackId": { "type": "string", "format": "guid", "x-parser-schema-id": "" } }, "x-parser-schema-id": "" } ], "x-parser-schema-id": "feedbackCreatedIntegrationEvent" } --- id: FeedbackDeletedEvent version: 1.0.0 name: Feedback Deleted summary: >- Represents a successful integration event when deleting a feedback in the system badges: - content: "Broker:AMQP" backgroundColor: green textColor: white icon: ArrowPathIcon schemaPath: schema.json owners: - nhanxnguyen --- ## Overview This event represents a successful integration event when deleting a feedback in the system. The `FeedbackDeletedIntegrationEvent` is a domain event that captures the deletion of a feedback in the Catalog bounded context. It carries the necessary value objects including the feedback identity to notify the system about the successful operation. This event adheres to the ubiquitous language of our domain and serves as the contract between the Catalog and external systems, facilitating the transition from a failed feedback deletion to a successful one. ## Architecture ## Raw Schema:schema.json { "id": "FeedbackDeletedEvent", "allOf": [ { "id": "integrationEvent", "type": "object", "additionalProperties": false, "properties": { "id": { "type": "string", "format": "guid", "x-parser-schema-id": "" }, "creationDate": { "type": "string", "format": "date-time", "x-parser-schema-id": "" } }, "x-parser-schema-id": "integrationEvent" }, { "type": "object", "additionalProperties": false, "properties": { "bookId": { "type": "string", "format": "guid", "x-parser-schema-id": "" }, "rating": { "type": "integer", "format": "int32", "x-parser-schema-id": "" }, "feedbackId": { "type": "string", "format": "guid", "x-parser-schema-id": "" } }, "x-parser-schema-id": "" } ], "x-parser-schema-id": "feedbackDeletedIntegrationEvent" } --- id: OrderStatusChangedToCancelEvent version: 1.0.0 name: Order Status Changed To Cancel summary: Represents an integration event when an order status is changed to cancel badges: - content: Order Lifecycle textColor: orange backgroundColor: orange icon: CubeTransparentIcon - content: Status Change textColor: blue backgroundColor: blue icon: SparklesIcon - content: "Broker:AMQP" backgroundColor: green textColor: white icon: ArrowPathIcon schemaPath: schema.json owners: - nhanxnguyen --- ## Overview This event represents an integration event when an order status is changed to cancel. The `OrderStatusChangedToCancelIntegrationEvent` is a domain event that captures the change of an order's status to cancel in the Ordering bounded context. It carries the necessary value objects including the order identity, the new status, and the cancellation reason to notify the system about the status change. This event adheres to the ubiquitous language of our domain and serves as the contract between the Ordering and external systems, facilitating the transition from a pending order to a cancelled one. ## Architecture ## Event Structure The event contains the following key properties: | Property | Type | Description | | ------------ | --------- | ----------------------------------------------------- | | `OrderId` | `Guid` | Unique identifier for the order being cancelled | | `BasketId` | `Guid` | Identifier of the basket associated with the order | | `Email` | `string?` | Customer's email address (optional) for notifications | | `TotalMoney` | `decimal` | Total monetary value of the cancelled order | ## Workflow Context This event plays an important role in the order cancellation workflow: 1. It is published by the Ordering service when an order is cancelled 2. The Finance service receives this event and updates the order state machine 3. The event triggers the transition to the Cancelled state in the Finance service 4. It may initiate compensating transactions or recovery actions ## Cross-Service Communication This event facilitates communication between several bounded contexts: - **Ordering → Finance**: Notifies about order cancellation - **Finance → Notification**: May trigger cancellation notifications to customers ## Business Impact From a business perspective, this event represents several important aspects: - Customer decision to cancel an order - Opportunity to gather cancellation reasons for business intelligence - Trigger for potential customer retention actions - Signal to release reserved inventory or payment holds ## Raw Schema:schema.json { "id": "OrderStatusChangedToCancelEvent", "allOf": [ { "id": "integrationEvent", "type": "object", "additionalProperties": false, "properties": { "id": { "type": "string", "format": "guid", "x-parser-schema-id": "" }, "creationDate": { "type": "string", "format": "date-time", "x-parser-schema-id": "" } }, "x-parser-schema-id": "integrationEvent" }, { "type": "object", "additionalProperties": false, "properties": { "orderId": { "type": "string", "format": "guid", "x-parser-schema-id": "" }, "basketId": { "type": "string", "format": "guid", "x-parser-schema-id": "" }, "email": { "type": ["null", "string"], "x-parser-schema-id": "" }, "totalMoney": { "type": "number", "format": "decimal", "x-parser-schema-id": "" } }, "x-parser-schema-id": "" } ], "x-parser-schema-id": "orderStatusChangedToCancelIntegrationEvent" } --- id: OrderStatusChangedToCompleteEvent version: 1.0.0 name: Order Staus Changed to Complete summary: Represents an integration event when an order status is changed to complete badges: - content: Order Lifecycle textColor: green backgroundColor: green icon: CubeTransparentIcon - content: Status Change textColor: blue backgroundColor: blue icon: SparklesIcon - content: "Broker:AMQP" backgroundColor: green textColor: white icon: ArrowPathIcon schemaPath: schema.json owners: - nhanxnguyen --- ## Overview This event represents an integration event when an order status is changed to complete. The `OrderStatusChangedToCompleteIntegrationEvent` is a domain event that captures the change of an order's status to complete in the Ordering bounded context. It carries the necessary value objects including the order identity, the new status, and the completion reason to notify the system about the status change. This event adheres to the ubiquitous language of our domain and serves as the contract between the Ordering and external systems, facilitating the transition from a pending order to a completed one. ## Architecture ## Event Structure The event contains the following key properties: | Property | Type | Description | | ------------ | --------- | ----------------------------------------------------- | | `OrderId` | `Guid` | Unique identifier for the order being completed | | `BasketId` | `Guid` | Identifier of the basket associated with the order | | `Email` | `string?` | Customer's email address (optional) for notifications | | `TotalMoney` | `decimal` | Total monetary value of the completed order | ## Workflow Context This event plays an important role in the order completion workflow: 1. It is published by the Ordering service when an order is successfully completed 2. The Finance service receives this event and updates the order state machine 3. The event triggers the transition to the Completed state in the Finance service 4. It may initiate follow-up processes like customer notifications or analytics updates ## Cross-Service Communication This event facilitates communication between several bounded contexts: - **Ordering → Finance**: Notifies about order completion - **Finance → Notification**: May trigger completion notifications to customers ## Business Impact From a business perspective, this event represents several important aspects: - Successful completion of the order fulfillment process - Opportunity to gather customer satisfaction metrics - Trigger for post-purchase marketing activities - Signal for financial reconciliation and reporting ## Raw Schema:schema.json { "id": "OrderStatusChangedToCompleteEvent", "allOf": [ { "id": "integrationEvent", "type": "object", "additionalProperties": false, "properties": { "id": { "type": "string", "format": "guid", "x-parser-schema-id": "" }, "creationDate": { "type": "string", "format": "date-time", "x-parser-schema-id": "" } }, "x-parser-schema-id": "integrationEvent" }, { "type": "object", "additionalProperties": false, "properties": { "orderId": { "type": "string", "format": "guid", "x-parser-schema-id": "" }, "basketId": { "type": "string", "format": "guid", "x-parser-schema-id": "" }, "email": { "type": ["null", "string"], "x-parser-schema-id": "" }, "totalMoney": { "type": "number", "format": "decimal", "x-parser-schema-id": "" } }, "x-parser-schema-id": "" } ], "x-parser-schema-id": "orderStatusChangedToCompleteIntegrationEvent" } --- id: PlaceOrderCommand version: 1.0.0 name: Order Placed summary: Receive a message when an order is placed badges: - content: Orchestrated textColor: orange backgroundColor: orange icon: CubeTransparentIcon - content: "Broker:AMQP" backgroundColor: green textColor: white icon: ArrowPathIcon schemaPath: schema.json owners: - nhanxnguyen --- ## Overview The `PlaceOrderCommand` is a critical domain command that initiates the order creation process in the BookWorm e-commerce system. This command serves as the bridge between the Basket and Ordering bounded contexts, facilitating the transition from a transient shopping basket to a persistent order. ## Architecture ### Purpose - Initiates the order creation process - Transfers basket data to the ordering system - Triggers the deletion of the original basket - Ensures atomic transaction between basket deletion and order creation ### Command Structure ```json title="Event Message" { "basketId": "Guid", "email": "string?", "orderId": "Guid", "totalMoney": "decimal" } ``` ### Behavior When this command is processed: 1. The system attempts to delete the specified basket 2. Upon successful deletion, a `BasketDeletedCompleteIntegrationEvent` is published 3. If deletion fails, a `BasketDeletedFailedIntegrationEvent` is published 4. The command handler ensures concurrent message processing is limited to 1 message at a time ### Integration Points - **Channel**: `basket-place-order` - **Concurrency**: Limited to 1 concurrent message - **Related Events**: - `BasketDeletedCompleteIntegrationEvent` - `BasketDeletedFailedIntegrationEvent` ### Usage Context This command is typically triggered when: - A customer completes their shopping cart - The checkout process is initiated - The system needs to transition from basket to order state ### Error Handling The command includes built-in error handling: - Tracks failed basket deletions - Maintains order creation state - Provides feedback through integration events ## Raw Schema:schema.json { "id": "placeOrderCommand", "allOf": [ { "id": "integrationEvent", "type": "object", "additionalProperties": false, "properties": { "id": { "type": "string", "format": "guid", "x-parser-schema-id": "" }, "creationDate": { "type": "string", "format": "date-time", "x-parser-schema-id": "" } }, "x-parser-schema-id": "integrationEvent" }, { "type": "object", "additionalProperties": false, "properties": { "basketId": { "type": "string", "format": "guid", "x-parser-schema-id": "" }, "email": { "type": ["null", "string"], "x-parser-schema-id": "" }, "orderId": { "type": "string", "format": "guid", "x-parser-schema-id": "" }, "totalMoney": { "type": "number", "format": "decimal", "x-parser-schema-id": "" } }, "x-parser-schema-id": "" } ], "x-parser-schema-id": "placeOrderCommand" } --- id: ResendErrorEmailIntegrationEvent version: 1.0.0 name: Resend Error Email summary: Receive a message to trigger resending of failed emails badges: - content: Orchestrated textColor: orange backgroundColor: orange icon: CubeTransparentIcon - content: Notification textColor: blue backgroundColor: blue icon: BellIcon - content: "Broker:AMQP" backgroundColor: green textColor: white icon: ArrowPathIcon - content: Scheduler textColor: purple backgroundColor: purple icon: ClockIcon - content: High Priority textColor: red backgroundColor: red icon: ExclamationTriangleIcon schemaPath: schema.json owners: - nhanxnguyen --- ## Overview The `ResendErrorEmailIntegrationEvent` is triggered by the Scheduler Service to initiate the retry mechanism for failed email deliveries. This event is published hourly with high priority to ensure prompt recovery of email delivery failures and maintain customer communication reliability. ### Event Flow 1. **Scheduler Service** publishes the event hourly (cron: `0 * * * *`) with high priority 2. **Notification Service** receives the event and processes it 3. System identifies failed email records in the outbox 4. Failed emails are retried with parallel processing (max 5 concurrent operations) 5. Results are logged for monitoring and further analysis ### Business Value - **Customer Experience**: Ensures critical notifications reach customers even after initial failures - **System Reliability**: Provides automatic recovery mechanism for email delivery issues - **Communication Continuity**: Maintains consistent customer communication flow - **Error Monitoring**: Enables tracking and analysis of email delivery patterns ### Processing Details - **Retry Strategy**: Parallel processing with up to 5 concurrent email sends - **Error Handling**: Individual email failures don't block other retries - **Monitoring**: Success and failure counts are logged for analysis - **Performance**: Optimized for quick recovery without overwhelming the email service ## Schema ## Raw Schema:schema.json { "id": "ResendErrorEmailIntegrationEvent", "description": "Represents an integration event to trigger resending of failed emails", "allOf": [ { "id": "integrationEvent", "type": "object", "description": "Base event structure containing common metadata for all integration events in the system", "additionalProperties": false, "properties": { "id": { "type": "string", "format": "guid", "description": "The unique identifier of the integration event", "example": "01961eb4-668d-7e7e-ae25-0fab379614f7", "x-parser-schema-id": "" }, "creationDate": { "type": "string", "format": "date-time", "description": "The creation date of the integration event", "example": "2021-01-01T00:00:00Z", "x-parser-schema-id": "" } }, "x-parser-schema-id": "integrationEvent" }, { "type": "object", "additionalProperties": false, "x-parser-schema-id": "" } ], "x-parser-schema-id": "ResendErrorEmailIntegrationEvent" } --- id: UserCheckedOutEvent version: 1.0.0 name: User Checked Out summary: Represents an integration event when a user has completed the checkout process badges: - content: Checkout Flow textColor: green backgroundColor: green icon: ShoppingCartIcon - content: Order Initiation textColor: blue backgroundColor: blue icon: PlusCircleIcon - content: "Broker:AMQP" backgroundColor: green textColor: white icon: ArrowPathIcon schemaPath: schema.json owners: - nhanxnguyen --- ## Overview The `UserCheckedOutIntegrationEvent` is a critical integration event that signals the completion of a customer's checkout process in the BookWorm e-commerce system. This event serves as the primary trigger for initiating the order processing workflow across multiple services, particularly the Finance service which orchestrates the order state machine. ## Architecture ## Event Structure The event contains the following key properties: | Property | Type | Description | | ------------ | --------- | --------------------------------------------------------------------------- | | `OrderId` | `Guid` | Unique identifier for the newly created order | | `BasketId` | `Guid` | Identifier of the basket being converted to an order | | `Email` | `string?` | Customer's email address (optional) for notifications and order association | | `TotalMoney` | `decimal` | Total monetary value of the order | ## Workflow Context This event plays a pivotal role in the order processing workflow: 1. It is published by the Ordering service when a user completes checkout 2. The Finance service receives this event and initiates the order state machine 3. The event triggers the transition from basket to order in the system 4. It serves as the correlation identifier for the entire order processing saga ## Cross-Service Communication This event facilitates communication between several bounded contexts: - **Ordering → Finance**: Initiates the order processing saga - **Finance → Basket**: Signals that the basket should be marked as processed ## Business Impact From a business perspective, this event represents a critical moment in the customer journey: - Conversion of a shopping intent (basket) to a purchase commitment (order) - Beginning of the fulfillment process - Opportunity for order confirmation communications - Point of financial transaction initiation ## Raw Schema:schema.json { "id": "UserCheckedOutEvent", "allOf": [ { "id": "integrationEvent", "type": "object", "additionalProperties": false, "properties": { "id": { "type": "string", "format": "guid", "x-parser-schema-id": "" }, "creationDate": { "type": "string", "format": "date-time", "x-parser-schema-id": "" } }, "x-parser-schema-id": "integrationEvent" }, { "type": "object", "additionalProperties": false, "properties": { "orderId": { "type": "string", "format": "guid", "x-parser-schema-id": "" }, "basketId": { "type": "string", "format": "guid", "x-parser-schema-id": "" }, "email": { "type": ["null", "string"], "x-parser-schema-id": "" }, "totalMoney": { "type": "number", "format": "decimal", "x-parser-schema-id": "" } }, "x-parser-schema-id": "" } ], "x-parser-schema-id": "userCheckedOutIntegrationEvent" } --- id: AddBasketCommand version: 1.0.0 name: Create Basket summary: Create a new basket for a user schemaPath: request-body.json badges: - content: POST textColor: green backgroundColor: green icon: PlusIcon - content: "Basket" textColor: yellow backgroundColor: yellow icon: ShoppingCartIcon owners: - nhanxnguyen --- ## Overview This endpoint creates a new shopping basket for a user. In our domain model, a `Basket` represents an Aggregate Root in the Basket bounded context. Each Basket maintains its own identity and encapsulates a collection of `BasketItem` entities, which are part of the Basket's aggregate boundary. The `Basket` enforces invariants across its contained items and manages the lifecycle of the `BasketItem` entities through well-defined domain operations. When a basket is created, it establishes the consistency boundary for transactional operations related to the user's shopping experience. Each `BasketItem` captures the user's intent to purchase a specific book, maintaining quantity and price information while preserving the connection to the Catalog domain through book identifiers. ## Implementation Details The Create Basket operation is implemented using the CQRS pattern with a dedicated command handler: ```mermaid sequenceDiagram participant Client participant CreateBasketEndpoint participant CreateBasketValidator participant CreateBasketCommand participant ClaimsPrincipal participant CustomerBasket participant BasketRepository participant Redis Client->>CreateBasketEndpoint: POST /api/v1/baskets Note over Client,CreateBasketEndpoint: Authorization: Bearer {jwt-token} Note over Client,CreateBasketEndpoint: Request body with basket items CreateBasketEndpoint->>CreateBasketValidator: Validate command alt Validation fails CreateBasketValidator-->>CreateBasketEndpoint: Return validation errors CreateBasketEndpoint-->>Client: 400 Bad Request else Validation passes CreateBasketEndpoint->>CreateBasketCommand: Send(command) CreateBasketCommand->>ClaimsPrincipal: GetClaimValue(KeycloakClaimTypes.Subject) alt User not authenticated ClaimsPrincipal-->>CreateBasketCommand: null or empty userId CreateBasketCommand-->>CreateBasketEndpoint: Throw UnauthorizedAccessException CreateBasketEndpoint-->>Client: 401 Unauthorized else User authenticated ClaimsPrincipal-->>CreateBasketCommand: userId CreateBasketCommand->>CustomerBasket: new CustomerBasket(userId, items) alt Domain validation fails CustomerBasket-->>CreateBasketCommand: Throw BasketDomainException CreateBasketCommand-->>CreateBasketEndpoint: Propagate exception CreateBasketEndpoint-->>Client: 400 Bad Request else Domain validation passes CustomerBasket-->>CreateBasketCommand: Return valid basket CreateBasketCommand->>BasketRepository: UpdateBasketAsync(basket) BasketRepository->>Redis: Store basket data Redis-->>BasketRepository: Operation result alt Repository operation fails BasketRepository-->>CreateBasketCommand: null or invalid result CreateBasketCommand-->>CreateBasketEndpoint: Throw BasketCreatedException CreateBasketEndpoint-->>Client: 500 Internal Server Error else Repository operation succeeds BasketRepository-->>CreateBasketCommand: CustomerBasket with Id CreateBasketCommand-->>CreateBasketEndpoint: Return basket.Id CreateBasketEndpoint-->>Client: 201 Created with Location header end end end end ``` ### Key Components 1. **CreateBasketCommand**: Implements `ICommand` to create a new basket with items 2. **CreateBasketHandler**: Processes the command using repository pattern 3. **CreateBasketValidator**: Validates the command parameters using FluentValidation 4. **CustomerBasket**: Domain entity that encapsulates basket data and business rules 5. **BasketItem**: Value object representing an item in the basket 6. **IBasketRepository**: Repository interface for basket persistence operations ### Validation Rules The command validation enforces several business rules: - The items collection must not be empty - Each item must have a valid ID - Each item must have a quantity greater than zero The domain model enforces additional invariants: - Customer ID cannot be null - Basket must contain at least one item ## Architecture ## POST `(/api/v1/baskets)` ### Request Body ### Example Usage ```bash curl -X POST https://api.bookworm.com/api/v1/baskets \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{ "items": [ { "id": "0195e6d3-36e1-72f9-9c60-09a9f13a07e9", "quantity": 2 }, { "id": "0195e6d3-36e1-78af-9e8d-a295555ca961", "quantity": 1 } ] }' ``` ### Responses #### 201 Created - Returns the newly created basket ID. - Includes a Location header with the URL to the new resource - Format: `/api/v1/baskets/{guid}` #### 400 Bad Request Returned when the request validation fails. #### 401 Unauthorized Returned when: - The user is not authenticated - The authentication token is missing or invalid ## Implementation Notes - The endpoint is versioned (v1) and follows REST principles - Uses optimistic concurrency for basket updates - Implements domain-driven design patterns with `Basket` as an aggregate root - Provides immediate consistency for basket operations ## Raw Schema:request-body.json { "required": ["items"], "type": "object", "properties": { "items": { "type": "array", "items": { "required": ["id", "quantity"], "type": "object", "properties": { "id": { "type": "string", "description": "Product ID" }, "quantity": { "type": "integer", "format": "int32", "description": "Quantity of the product in the basket", "minimum": 1 } } } } } } --- id: CancelChatCommand version: 1.0.0 name: Cancel Chat summary: Cancel a chat badges: - content: DELETE textColor: red backgroundColor: red icon: XMarkIcon - content: "Chat" textColor: yellow backgroundColor: yellow icon: ChatBubbleBottomCenterTextIcon owners: - nhanxnguyen deprecated: date: 2025-11-11 message: | This query has been deprecated and removed from the API. It is no longer available for use. Use endpoints in `/ag-ui` instead. --- ## Overview The Cancel Chat endpoint allows users to terminate an active chat session in the BookWorm platform. This endpoint accepts a chat's unique identifier (GUID) and cancels the associated chat session if it exists. It's designed to provide users with the ability to gracefully end chat interactions when needed. The endpoint follows RESTful principles and returns a 204 No Content response upon successful cancellation, indicating that the request was processed successfully but no content needs to be returned. ## Architecture ## DELETE `/api/v1/chats/{id:guid}/cancel` Cancels a chat session by its unique identifier. ### Path Parameters | Name | Type | Description | | ---- | ---- | ------------------------------------------------- | | id | GUID | The unique identifier of the chat to be cancelled | ### Example Usage ```bash curl -X DELETE https://api.bookworm.com/api/v1/chats/{id}/cancel \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN" ``` ### Response #### 204 No Content Returns a 204 No Content response if the chat is successfully cancelled. This indicates that the request was processed successfully but there is no content to return. --- id: CancelOrderCommand version: 1.0.0 name: Cancel Order summary: Cancel order by order id badges: - content: PATCH textColor: orange backgroundColor: orange icon: PencilIcon - content: "Order" textColor: yellow backgroundColor: yellow icon: ListBulletIcon - content: "Idempotent" textColor: green backgroundColor: green icon: CheckCircleIcon - content: "Authenticated" textColor: blue backgroundColor: blue icon: UserIcon owners: - nhanxnguyen --- ## Overview The Order Cancellation operation is a critical boundary-crossing interaction within the Ordering domain. This endpoint empowers authenticated users to cancel an existing order that hasn't progressed beyond a cancellable state in its lifecycle. From a domain perspective, order cancellation represents an important state transition that triggers several domain events and side effects: 1. **State Validation**: The domain enforces business rules to verify if the order is eligible for cancellation based on its current state (only orders in "New" status can be cancelled). 2. **Domain Event Publication**: Upon successful cancellation, the `OrderCancelledEvent` is raised, which may trigger compensating transactions across bounded contexts. 3. **Aggregate Consistency**: The Order aggregate's invariants are preserved throughout the cancellation process, ensuring the order transitions to a cancelled state only when business rules permit. 4. **Notification Generation**: The cancellation typically initiates notification events to inform customers about their order status change. ## Implementation Details The Cancel Order operation is implemented using the CQRS pattern with a query handler that returns the updated order details: ```mermaid sequenceDiagram participant Client participant CancelOrderEndpoint participant CancelOrderCommand participant OrderRepository participant Order Client->>CancelOrderEndpoint: PATCH /api/v1/orders/{orderId}/cancel CancelOrderEndpoint->>CancelOrderCommand: Send(orderId) CancelOrderCommand->>OrderRepository: FirstOrDefaultAsync(OrderFilterSpec) OrderRepository-->>CancelOrderCommand: Order alt Order Not Found CancelOrderCommand-->>CancelOrderEndpoint: NotFoundException CancelOrderEndpoint-->>Client: 404 Not Found else Order Found CancelOrderCommand->>Order: MarkAsCanceled() Order->>Order: Status = Cancelled Order->>Order: RegisterDomainEvent(OrderCancelledEvent) CancelOrderCommand->>OrderRepository: SaveEntitiesAsync() CancelOrderCommand-->>CancelOrderEndpoint: OrderDetailDto CancelOrderEndpoint-->>Client: 200 OK with OrderDetailDto end ``` ### Key Components 1. **CancelOrderCommand**: Implements `IQuery` to retrieve and update the order 2. **OrderFilterSpec**: Ensures only orders in "New" status can be cancelled 3. **MarkAsCanceled**: Domain method that changes the order status and registers the domain event 4. **OrderCancelledEvent**: Domain event that triggers downstream processes ## API Endpoint The endpoint is configured with several important characteristics: - **Authentication**: Requires an authenticated user - **Idempotency**: Supports idempotent requests via the `WithIdempotency()` middleware - **Versioning**: Mapped to API version 1.0 ## Architecture ## PATCH `(/api/v1/orders/{orderId}/cancel)` ### Parameters - **orderId** (path) (required): The unique identifier of the order to cancel - **x-request-id** (header) (required): A unique key to ensure idempotent processing ### Example Usage ```bash curl -X PATCH "https://api.bookworm.com/api/v1/orders/{orderId}/cancel" \ -H "Authorization: Bearer " \ -H "x-request-id: " ``` ### Responses #### 200 OK Returns the cancelled order details. ### Example Response ```json title="200 OK" { "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "date": "2025-03-31T02:54:17.223Z", "status": "Cancelled", "total": 292.4, "items": [ { "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "name": "Book Name", "quantity": 10, "price": 29.24 } ] } ``` #### 404 Not Found ```json title="404 Not Found" { "type": "https://tools.ietf.org/html/rfc7231#section-6.5.4", "title": "Not Found", "status": 404, "detail": "Order with id {orderId} not found." } ``` #### 401 Unauthorized Returned when the request lacks valid authentication credentials. #### 400 Bad Request Returned when the order cannot be cancelled (e.g., already completed or cancelled). #### 409 Conflict Returned when the order is already cancelled. --- id: CompleteOrderCommand version: 1.0.0 name: Complete Order summary: Complete order by order id badges: - content: PATCH textColor: blue backgroundColor: blue icon: PencilIcon - content: "Order" textColor: yellow backgroundColor: yellow icon: ListBulletIcon - content: "Idempotent" textColor: green backgroundColor: green icon: CheckCircleIcon - content: "Authenticated" textColor: blue backgroundColor: blue icon: UserIcon owners: - nhanxnguyen --- ## Overview This endpoint marks an order as complete within the Ordering bounded context, representing the final state transition in the order fulfillment lifecycle. When an order is completed, it signifies that all items have been delivered to the customer and the business transaction is finalized. ## Domain Significance In our domain model, order completion is a crucial state transition that: - Validates that the order is in a valid state for completion (only orders in "New" status can be completed) - Triggers domain events for cross-service communication - Updates aggregate state while preserving invariants - Provides a consistent view of the completed order back to the client ## Implementation Details The Complete Order operation is implemented using the CQRS pattern with a query handler that returns the updated order details: ```mermaid sequenceDiagram participant Client participant CompleteOrderEndpoint participant CompleteOrderCommand participant OrderRepository participant Order Client->>CompleteOrderEndpoint: PATCH /api/v1/orders/{orderId}/complete CompleteOrderEndpoint->>CompleteOrderCommand: Send(orderId) CompleteOrderCommand->>OrderRepository: FirstOrDefaultAsync(OrderFilterSpec) OrderRepository-->>CompleteOrderCommand: Order alt Order Not Found CompleteOrderCommand-->>CompleteOrderEndpoint: NotFoundException CompleteOrderEndpoint-->>Client: 404 Not Found else Order Found CompleteOrderCommand->>Order: MarkAsCompleted() Order->>Order: Status = Completed Order->>Order: RegisterDomainEvent(OrderCompletedEvent) CompleteOrderCommand->>OrderRepository: SaveEntitiesAsync() CompleteOrderCommand-->>CompleteOrderEndpoint: OrderDetailDto CompleteOrderEndpoint-->>Client: 200 OK with OrderDetailDto end ``` ### Key Components 1. **CompleteOrderCommand**: Implements `IQuery` to retrieve and update the order 2. **OrderFilterSpec**: Ensures only orders in "New" status can be completed 3. **MarkAsCompleted**: Domain method that changes the order status and registers the domain event 4. **OrderCompletedEvent**: Domain event that triggers downstream processes ## Domain Events Upon successful completion, the system publishes an `OrderCompletedEvent` that notifies other bounded contexts about this state change. This event contains the entire Order aggregate, allowing downstream services to access all relevant order information. The event triggers several important cross-domain processes: - **Notification Service**: Sends a completion confirmation email to the customer - **Finance Service**: Finalizes payment processing and generates receipts - **Analytics Service**: Updates sales metrics and customer purchase history ## API Endpoint The endpoint is configured with several important characteristics: - **Authentication**: Requires an authenticated user - **Idempotency**: Supports idempotent requests via the `WithIdempotency()` middleware - **Versioning**: Mapped to API version 1.0 ## Architecture ## PATCH `(/api/v1/orders/{orderId}/complete)` ### Parameters - **orderId** (path) (required): The unique identifier of the order to complete - **x-request-id** (header) (required): A unique key to ensure idempotent processing ### Example Usage ```bash curl -X PATCH "https://api.bookworm.com/api/v1/orders/{orderId}/complete" \ -H "Authorization: Bearer " \ -H "x-request-id: " ``` ### Responses #### 200 OK Returns the completed order details. ### Example Response ```json title="200 OK" { "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "date": "2025-03-31T10:05:17.223Z", "status": "Completed", "total": 292.4, "items": [ { "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "name": "Book Name", "quantity": 10, "price": 29.24 } ] } ``` #### 404 Not Found ```json title="404 Not Found" { "type": "https://tools.ietf.org/html/rfc7231#section-6.5.4", "title": "Not Found", "status": 404, "detail": "Order with id {orderId} not found." } ``` #### 401 Unauthorized Returned when the request lacks valid authentication credentials. #### 400 Bad Request Returned when the order cannot be completed (e.g., already completed or cancelled). #### 409 Conflict Returned when the order is already completed. --- id: CreateAuthorCommand version: 1.0.0 name: Create Author summary: Create a new author in the catalog system schemaPath: request-body.json badges: - content: POST textColor: green backgroundColor: green icon: PlusIcon - content: "Author" textColor: yellow backgroundColor: yellow icon: UserIcon - content: "Admin Only" textColor: red backgroundColor: red icon: LockClosedIcon owners: - nhanxnguyen --- ## Overview This endpoint implements the command part of CQRS pattern to create a new Author entity in the Catalog bounded context. It follows Domain-Driven Design principles by encapsulating the author creation operation as a discrete business capability. ### Security - **Authorization**: Requires admin privileges (Policy: `Admin`) - **Authentication**: Required - **API Version**: 1.0 ### Implementation Details The endpoint follows these key principles: 1. **CQRS Pattern**: Implements the Command side for author creation 2. **Domain-Driven Design**: Encapsulates author creation as a domain operation 3. **Vertical Slice Architecture**: Contains all layers (API, Application, Domain, Infrastructure) within the Catalog bounded context ```mermaid sequenceDiagram participant Client participant CreateAuthorEndpoint participant CreateAuthorValidator participant CreateAuthorCommand participant AuthorRepository participant Author participant UnitOfWork Client->>CreateAuthorEndpoint: POST /api/v1/authors CreateAuthorEndpoint->>CreateAuthorValidator: Validate command alt Validation fails CreateAuthorValidator-->>CreateAuthorEndpoint: Return validation errors CreateAuthorEndpoint-->>Client: 400 Bad Request else Validation passes CreateAuthorEndpoint->>CreateAuthorCommand: Send(command) CreateAuthorCommand->>Author: new Author(name) CreateAuthorCommand->>AuthorRepository: AddAsync(author) CreateAuthorCommand->>UnitOfWork: SaveEntitiesAsync() AuthorRepository-->>CreateAuthorCommand: Return author.Id CreateAuthorCommand-->>CreateAuthorEndpoint: Return author.Id CreateAuthorEndpoint-->>Client: 201 Created with Location header end ``` ### Validation Rules - Author name is required (cannot be empty) - Author name has a maximum length constraint defined by `DataSchemaLength.Large` ### Technical Characteristics - **Idempotency**: Author creation operations are idempotent - **Transactional**: Changes are wrapped in a unit of work - **Response**: Returns the newly created author's GUID - **HTTP Method**: POST - **Endpoint**: `/api/v1/authors` ## Architecture ## POST `(/api/v1/authors)` ### Request Body #### Example Usage ```bash curl -X POST "https://api.bookworm.com/api/v1/authors/{id}" \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{ "name": "John Doe" }' ``` ### Response Details #### 200 Ok - Returns the newly created author's GUID #### 400 Bad Request Returned when: - Author name is empty - Author name exceeds maximum length - Invalid request format #### 401 Unauthorized Returned when the request lacks valid authentication credentials. #### 403 Forbidden Returned when the authenticated user lacks admin privileges. ## Raw Schema:request-body.json { "required": ["id", "name"], "type": "object", "properties": { "id": { "type": "string", "format": "uuid", "description": "Author ID" }, "name": { "type": "string", "description": "Author name", "minLength": 1, "maxLength": 100 } } } --- id: CreateBookCommand version: 1.0.0 name: Create Book summary: Create a book schemaPath: request-body.json badges: - content: POST textColor: green backgroundColor: green icon: PlusIcon - content: "Book" textColor: yellow backgroundColor: yellow icon: BookOpenIcon - content: "Admin Only" textColor: red backgroundColor: red icon: LockClosedIcon owners: - nhanxnguyen --- ## Overview The Create Book endpoint allows administrators to add new books to the Catalog domain. This operation follows Domain-Driven Design principles and our [event-driven CQRS pattern](https://foxminchan.github.io/BookWorm/architecture-decisions/adr-002-event-driven-cqrs) by: 1. Validating the book entity against domain rules and invariants 2. Creating a new aggregate root in the Books collection 3. Publishing a `BookCreatedEvent` that other bounded contexts can subscribe to 4. Automatically generating embeddings for semantic search using our [AI integration capabilities](https://foxminchan.github.io/BookWorm/architecture-decisions/adr-009-ai-integration) This endpoint represents the command side of our CQRS pattern implementation. The book entity becomes immediately available for queries after successful creation. :::note Book creation requires proper authentication with Admin privileges, as indicated by the badge. ::: ## Architecture ## POST `(/api/v1/books)` ### Request Body ### Example Usage ```bash curl -X POST "https://api.bookworm.com/api/v1/books" \ -H "Content-Type: multipart/form-data" \ -H "Authorization: Bearer " \ -F "name=Updated Book Name" \ -F "description=Updated Book Description" \ -F "price=19.99" \ -F "priceSale=14.99" \ -F "categoryId=123e4567-e89b-12d3-a456-426614174000" \ -F "publisherId=123e4567-e89b-12d3-a456-426614174000" \ -F "authorIds=123e4567-e89b-12d3-a456-426614174000,123e4567-e89b-12d3-a456-426614174001" \ -F "image=@/path/to/image.jpg" ``` ### Implementation Details The Create Book operation is implemented using the CQRS pattern with a dedicated command handler: ```mermaid sequenceDiagram participant Client participant CreateBookEndpoint participant CreateBookValidator participant PreCreateBookHandler participant CreateBookHandler participant BlobService participant BookRepository participant Book participant BookCreatedEvent participant BookUpsertEmbeddingHandler participant AiService participant QdrantClient Client->>CreateBookEndpoint: POST /api/v1/books CreateBookEndpoint->>CreateBookValidator: Validate command alt Validation fails CreateBookValidator-->>CreateBookEndpoint: Return validation errors CreateBookEndpoint-->>Client: 400 Bad Request else Validation passes CreateBookEndpoint->>PreCreateBookHandler: Process(command) alt Has Image PreCreateBookHandler->>BlobService: UploadFileAsync(image) BlobService-->>PreCreateBookHandler: Return imageUrl PreCreateBookHandler->>PreCreateBookHandler: Set ImageName end PreCreateBookHandler->>CreateBookHandler: Send(command) CreateBookHandler->>Book: new Book(name, description, image, price, priceSale, categoryId, publisherId, authorIds) Book->>Book: RegisterDomainEvent(new BookCreatedEvent()) CreateBookHandler->>BookRepository: AddAsync(book) CreateBookHandler->>BookRepository: SaveEntitiesAsync() BookRepository->>BookCreatedEvent: Publish event BookCreatedEvent->>BookUpsertEmbeddingHandler: Handle event BookUpsertEmbeddingHandler->>AiService: GetEmbeddingAsync(name + description) AiService-->>BookUpsertEmbeddingHandler: Return embedding BookUpsertEmbeddingHandler->>QdrantClient: UpsertAsync(collection, pointStruct) BookRepository-->>CreateBookHandler: Return book.Id CreateBookHandler-->>CreateBookEndpoint: Return book.Id CreateBookEndpoint-->>Client: 201 Created with Location header end ``` ### Key Components 1. **CreateBookCommand**: Implements `ICommand` to create a new book 2. **PreCreateBookHandler**: Processes image upload before book creation 3. **CreateBookHandler**: Processes the command using repository pattern 4. **CreateBookValidator**: Validates the command parameters using FluentValidation 5. **CreateBookEndpoint**: Maps the HTTP POST request to the command handler 6. **Book Entity**: Domain entity that encapsulates book data and business rules 7. **BookCreatedEvent**: Domain event raised when book is created 8. **BookUpsertEmbeddingHandler**: Handles semantic search embedding generation ### Technical Implementation The implementation uses several patterns and techniques: 1. **CQRS**: Separates the write model (command) from the read model (query) 2. **Repository Pattern**: The `IBookRepository` abstracts the data access layer 3. **Domain-Driven Design**: Uses domain entities and events to encapsulate business logic 4. **Minimal API**: Uses .NET's minimal API approach with endpoint mapping 5. **FluentValidation**: Validates the command parameters 6. **Event Sourcing**: Uses domain events for side effects (embedding generation) The command execution flow: 1. The endpoint receives the HTTP POST request with the book data 2. The validator ensures all business rules are satisfied 3. If an image is provided, it's uploaded to blob storage 4. The command handler creates a new Book entity 5. The entity registers a domain event 6. The repository persists the entity and publishes domain events 7. The embedding handler processes the book for semantic search 8. The endpoint returns a 201 Created response with the book ID and location header ### Validation Rules The endpoint enforces the following validation rules: - **Name**: Required, maximum length of 100 characters - **Description**: Optional, maximum length of 4000 characters - **Price**: Required, must be greater than 0 - **PriceSale**: Optional, must be greater than 0 and less than or equal to regular price - **CategoryId**: Required - **PublisherId**: Required - **AuthorIds**: Required, at least one author must be specified - **Image**: Optional, if provided: - Maximum file size: 1MB - Allowed file types: JPEG and PNG only ### File Upload The endpoint supports image uploads for books. Images are: - Processed asynchronously - Stored in blob storage - URL is saved with the book record ### Domain Events Upon successful creation, the following domain events are triggered: 1. `BookCreatedEvent`: Notifies other bounded contexts about the new book 2. Automatically triggers embedding generation for semantic search capabilities ### Responses #### 400 Bad Request #### 201 Created - Returns the newly created book ID - Includes a Location header with the URL to the new resource - Format: `/api/v1/books/{guid}` ## Raw Schema:request-body.json { "required": ["id", "name", "description", "price", "categoryId", "publisherId", "authorIds"], "type": "object", "properties": { "id": { "type": "string", "format": "uuid", "description": "Product ID" }, "name": { "type": "string", "description": "Product name", "minLength": 1, "maxLength": 100 }, "description": { "type": "string", "description": "Product description", "maxLength": 500 }, "price": { "type": "number", "description": "Product price", "minimum": 0 }, "priceSale": { "type": "number", "description": "Product sale price", "minimum": 0, "maximum": "ref:price" }, "categoryId": { "type": "string", "format": "uuid", "description": "Category ID" }, "publisherId": { "type": "string", "format": "uuid", "description": "Publisher ID" }, "authorIds": { "type": "array", "items": { "type": "string", "format": "uuid", "description": "Author ID" } }, "image": { "type": "string", "format": "binary", "description": "Product image" } } } --- id: CreateBuyerCommand version: 1.0.0 name: Create Buyer summary: Create buyer in the ordering domain badges: - content: POST textColor: green backgroundColor: green icon: PlusIcon - content: "Buyer" textColor: yellow backgroundColor: yellow icon: UserGroupIcon - content: "Authenticated" textColor: blue backgroundColor: blue icon: UserIcon owners: - nhanxnguyen --- ## Overview Create a new buyer in the Ordering bounded context. The Buyer represents a crucial aggregate root in the Ordering domain model that encapsulates customer purchasing identity and address information. This endpoint requires authentication, as the buyer is created based on the authenticated user's identity. ## Implementation Details The Create Buyer operation is implemented using the CQRS pattern with a command handler: ```mermaid sequenceDiagram participant Client participant CreateBuyerEndpoint participant CreateBuyerCommand participant BuyerRepository participant Buyer Client->>CreateBuyerEndpoint: POST /api/v1/buyers CreateBuyerEndpoint->>CreateBuyerCommand: Send(command) CreateBuyerCommand->>CreateBuyerCommand: Extract user ID from claims CreateBuyerCommand->>CreateBuyerCommand: Extract user name from claims CreateBuyerCommand->>Buyer: new Buyer(userId, name, street, city, province) Buyer->>Buyer: Create Address value object CreateBuyerCommand->>BuyerRepository: AddAsync(buyer) CreateBuyerCommand->>BuyerRepository: SaveEntitiesAsync() CreateBuyerCommand-->>CreateBuyerEndpoint: Buyer ID CreateBuyerEndpoint-->>Client: 201 Created with Buyer ID ``` ### Key Components 1. **CreateBuyerCommand**: Implements `ICommand` to create a new buyer and return its ID 2. **Address Value Object**: Encapsulates the buyer's address information (street, city, province) 3. **Buyer Aggregate**: The root entity that maintains the buyer's identity and address 4. **Claims Extraction**: The user's ID and name are extracted from the authentication claims ## Business Rules - The buyer ID is automatically derived from the authenticated user's identity - The buyer name is extracted from the authenticated user's claims - Address information (street, city, province) is required and validated - Each field has a maximum length constraint (defined in DataSchemaLength.Medium) - The buyer is created with an empty collection of orders ## Architecture ## POST `(/api/v1/buyers)` ### Request ### Example Usage ```bash curl -X POST "https://api.bookworm.com/api/v1/buyers" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer " \ -d '{ "street": "123 Main Street", "city": "New York", "province": "NY" }' ``` ### Responses #### 201 Created - Returns the ID of the newly created buyer. - Location header includes a versioned URL to the newly created resource - Format: `/api/v1/buyers/{guid}` #### 400 Bad Request Returned when the request validation fails. ```json title="400 Bad Request" { "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1", "title": "Bad Request", "status": 400, "errors": { "Street": ["The Street field is required."], "City": ["The City field is required."], "Province": ["The Province field is required."] } } ``` #### 401 Unauthorized Returned when the request lacks valid authentication credentials. #### 409 Conflict Returned when a buyer with the same ID already exists. --- id: CreateCategoryCommand version: 1.0.0 name: Create Category summary: Create a category schemaPath: request-body.json badges: - content: POST textColor: green backgroundColor: green icon: PlusIcon - content: "Category" textColor: yellow backgroundColor: yellow icon: TagIcon - content: "Admin Only" textColor: red backgroundColor: red icon: LockClosedIcon owners: - nhanxnguyen --- ## Overview The Create Category endpoint allows administrators to add new categories to the Catalog domain. This operation follows Domain-Driven Design principles by: 1. Validating the category entity against domain rules and invariants 2. Creating a new aggregate root in the Categories collection 3. Publishing a `CategoryCreatedEvent` that other bounded contexts can subscribe to This endpoint represents the command side of our CQRS pattern implementation. The category entity becomes immediately available for queries after successful creation. :::note Category creation requires proper authentication with Admin privileges, as indicated by the badge. ::: ## Implementation Details The Create Category operation is implemented using the CQRS pattern with a dedicated command handler: ```mermaid sequenceDiagram participant Client participant CreateCategoryEndpoint participant CreateCategoryValidator participant CreateCategoryHandler participant CategoryRepository participant Category participant CategoryCreatedEvent participant EventDispatcher Client->>CreateCategoryEndpoint: POST /api/v1/categories CreateCategoryEndpoint->>CreateCategoryValidator: Validate command alt Validation fails CreateCategoryValidator-->>CreateCategoryEndpoint: Return validation errors CreateCategoryEndpoint-->>Client: 400 Bad Request else Validation passes CreateCategoryEndpoint->>CreateCategoryHandler: Send(command) CreateCategoryHandler->>Category: new Category(name) Category->>Category: RegisterDomainEvent(new CategoryCreatedEvent()) CreateCategoryHandler->>CategoryRepository: AddAsync(category) CreateCategoryHandler->>CategoryRepository: SaveEntitiesAsync() CategoryRepository->>CategoryCreatedEvent: Publish event CategoryRepository-->>CreateCategoryHandler: Return category.Id CreateCategoryHandler-->>CreateCategoryEndpoint: Return category.Id CreateCategoryEndpoint-->>Client: 201 Created with Location header end ``` ### Key Components 1. **CreateCategoryCommand**: Implements `ICommand` to create a new category 2. **CreateCategoryHandler**: Processes the command using repository pattern 3. **CreateCategoryValidator**: Validates the command parameters using FluentValidation 4. **CreateCategoryEndpoint**: Maps the HTTP POST request to the command handler 5. **Category Entity**: Domain entity that encapsulates category data and business rules 6. **CategoryCreatedEvent**: Domain event raised when category is created ### Technical Implementation The implementation uses several patterns and techniques: 1. **CQRS**: Separates the write model (command) from the read model (query) 2. **Repository Pattern**: The `ICategoryRepository` abstracts the data access layer 3. **Domain-Driven Design**: Uses domain entities and events to encapsulate business logic 4. **Minimal API**: Uses .NET's minimal API approach with endpoint mapping 5. **FluentValidation**: Validates the command parameters The command execution flow: 1. The endpoint receives the HTTP POST request with the category data 2. The validator ensures all business rules are satisfied 3. The command handler creates a new Category entity 4. The entity registers a domain event 5. The repository persists the entity and publishes domain events 6. The endpoint returns a 201 Created response with the category ID and location header ### Security - This endpoint requires admin authorization through a policy-based security check - Only authenticated users with admin privileges can access this endpoint ### Validation Rules - Category name is required and cannot be empty - Category name has a maximum length defined by `DataSchemaLength.Medium` - Validation is handled by FluentValidation ### Technical Implementation - Uses CQRS pattern with Mediator pattern (`ISender`) - Returns a `201 Created` response with the new category's GUID - Location header includes a versioned URL to the newly created resource - Implements optimistic concurrency through Unit of Work pattern ## Architecture ## POST `(/api/v1/categories)` ### Request Body ### Example Usage ```bash curl -X POST https://api.bookworm.com/api/v1/categories \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{ "name": "New Category" }' ``` ### Responses #### 200 Ok - Returns the newly created category's GUID #### 400 Bad Request Returned when validation fails (e.g., empty name or name too long) #### 409 Conflict Returned when a category with the same name already exists #### 401 Unauthorized Returned when the request lacks valid authentication credentials #### 403 Forbidden Returned when the authenticated user lacks admin privileges ## Raw Schema:request-body.json { "required": ["name"], "type": "object", "properties": { "name": { "type": "string", "description": "Category name", "minLength": 1, "maxLength": 100 } } } --- id: CreateChatCommand version: 1.0.0 name: Create Chat summary: Create a new chat in the catalog system schemaPath: request-body.json badges: - content: POST textColor: green backgroundColor: green icon: PlusIcon - content: "Chat" textColor: yellow backgroundColor: yellow icon: ChatBubbleBottomCenterTextIcon owners: - nhanxnguyen deprecated: date: 2025-11-11 message: | This query has been deprecated and removed from the API. It is no longer available for use. Use endpoints in `/ag-ui` instead. --- ## Overview The Create Chat endpoint enables users to initiate a new chat session with the system. This endpoint accepts a text prompt and returns a unique identifier for the chat session. It's designed to facilitate interactive conversations and queries within the BookWorm platform. ## Architecture ## POST `/api/v1/chats` Creates a new chat in the catalog system and returns the chat's unique identifier. ### Request Body ### Example Usage ```bash curl -X POST https://api.bookworm.com/api/v1/chats \ -H "Content-Type: application/json" \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \ -d '{ "text": "What is the best selling book in BookWorm?" }' ``` ### Response #### 200 Ok Returns the newly created chat's GUID in the response body. ```json "123e4567-e89b-12d3-a456-426614174000" ``` ### API Version This endpoint is available in API version 1.0. ## Raw Schema:request-body.json { "required": ["text"], "type": "object", "properties": { "text": { "type": "string", "description": "The prompt text for the chat", "minLength": 1 } }, "isSchema": true } --- id: CreateFeedbackCommand version: 1.0.0 name: Create Feedback summary: Create feedback for a book badges: - content: POST textColor: green backgroundColor: green icon: PlusIcon - content: "Feedback" textColor: yellow backgroundColor: yellow icon: StarIcon owners: - nhanxnguyen --- ## Overview The `Create Feedback` endpoint allows users to submit reviews and ratings for books within the BookWorm ecosystem. This operation is part of the Rating domain's bounded context, responsible for managing user feedback and book evaluations. ## Implementation Details The Create Feedback operation is implemented using the CQRS pattern with a dedicated command handler: ```mermaid sequenceDiagram participant Client participant CreateFeedbackEndpoint participant CreateFeedbackValidator participant CreateFeedbackCommand participant FeedbackRepository participant Feedback participant FeedbackCreatedEvent participant EventDispatcher Client->>CreateFeedbackEndpoint: POST /api/v1/feedbacks CreateFeedbackEndpoint->>CreateFeedbackValidator: Validate command alt Validation fails CreateFeedbackValidator-->>CreateFeedbackEndpoint: Return validation errors CreateFeedbackEndpoint-->>Client: 400 Bad Request else Validation passes CreateFeedbackEndpoint->>CreateFeedbackCommand: Send(command) CreateFeedbackCommand->>Feedback: new Feedback(bookId, firstName, lastName, comment, rating) Feedback->>Feedback: RegisterDomainEvent(new FeedbackCreatedEvent()) CreateFeedbackCommand->>FeedbackRepository: AddAsync(feedback) CreateFeedbackCommand->>FeedbackRepository: SaveEntitiesAsync() FeedbackRepository->>FeedbackCreatedEvent: Publish event FeedbackCreatedEvent->>EventDispatcher: Dispatch to catalog FeedbackRepository-->>CreateFeedbackCommand: Return feedback.Id CreateFeedbackCommand-->>CreateFeedbackEndpoint: Return feedback.Id CreateFeedbackEndpoint-->>Client: 201 Created with Location header end ``` ### Key Components 1. **CreateFeedbackCommand**: Implements `ICommand` to create a new feedback 2. **CreateFeedbackHandler**: Processes the command using repository pattern 3. **CreateFeedbackValidator**: Validates the command parameters using FluentValidation 4. **CreateFeedbackEndpoint**: Maps the HTTP POST request to the command handler 5. **Feedback Entity**: Domain entity that encapsulates feedback data and business rules 6. **FeedbackCreatedEvent**: Domain event raised when feedback is created ## Domain Context In our domain model, `Feedback` represents a core aggregate that encapsulates: - Unique identifier (Id) - Book reference (BookId) - Customer information (FirstName, LastName) - Review content (Comment) - Rating score (numerical evaluation on a scale of 0-5) The Create Feedback command is a write operation that: 1. Validates the input parameters 2. Creates a new Feedback entity 3. Registers a FeedbackCreatedEvent domain event 4. Persists the entity to the repository 5. Returns the unique identifier of the created feedback ## Business Rules - BookId must be provided and valid - FirstName and LastName are required - Comment is optional but has a maximum length constraint - Rating must be between 0 and 5 (inclusive) - The operation raises a domain event that can trigger side effects in other bounded contexts ## Technical Implementation The implementation uses several patterns and techniques: 1. **CQRS**: Separates the write model (command) from the read model (query) 2. **Repository Pattern**: The `IFeedbackRepository` abstracts the data access layer 3. **Domain-Driven Design**: Uses domain entities and events to encapsulate business logic 4. **Minimal API**: Uses .NET's minimal API approach with endpoint mapping 5. **FluentValidation**: Validates the command parameters The command execution flow: 1. The endpoint receives the HTTP POST request with the feedback data 2. The validator ensures all business rules are satisfied 3. The command handler creates a new Feedback entity 4. The entity registers a domain event 5. The repository persists the entity and publishes domain events 6. The endpoint returns a 201 Created response with the feedback ID and location header ## Integration Points This endpoint interacts with other bounded contexts through domain events: - The Catalog service receives the FeedbackCreatedEvent to update book ratings - The UI components use this endpoint to submit user reviews - The Recommendation engine may analyze new feedback to update recommendations ## Request Body ```json title="Payload Example" { "bookId": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "firstName": "John", "lastName": "Doe", "comment": "Great book, highly recommended!", "rating": 5 } ``` ## Example Usage ```bash curl -X POST "https://api.bookworm.com/api/v1/feedbacks" \ -H "Content-Type: application/json" \ -d '{ "bookId": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "firstName": "John", "lastName": "Doe", "comment": "Great book, highly recommended!", "rating": 5 }' ``` ## Architecture ## POST `(/api/v1/feedbacks)` ### Request Body | Field | Type | Required | Description | | --------- | ------ | -------- | ----------------------------------------- | | bookId | Guid | Yes | The ID of the book to create feedback for | | firstName | string | Yes | The first name of the reviewer | | lastName | string | Yes | The last name of the reviewer | | comment | string | No | The feedback comment text | | rating | int | Yes | The rating score (0-5) | ### Validation Rules - bookId must not be empty - firstName must not be empty - lastName must not be empty - comment must not exceed the maximum length - rating must be between 0 and 5 (inclusive) ### Responses #### 200 Ok - Returns the ID of the newly created feedback. #### 400 Bad Request Returned when validation fails for the provided parameters. --- id: CreateOrderCommand version: 1.0.0 name: Create Order summary: Create order for a customer badges: - content: POST textColor: green backgroundColor: green icon: PlusIcon - content: "Order" textColor: yellow backgroundColor: yellow icon: ListBulletIcon - content: "Authenticated" textColor: blue backgroundColor: blue icon: UserIcon - content: "Idempotent" textColor: purple backgroundColor: purple icon: CheckCircleIcon owners: - nhanxnguyen --- ## Overview The Create Order endpoint represents a key aggregate root operation within the Ordering bounded context. This command follows the CQRS pattern and is responsible for initiating a new order in the system based on the authenticated user's shopping basket. ## Implementation Details The Create Order operation is implemented using the CQRS pattern with a command handler and pre-processor: ```mermaid sequenceDiagram participant Client participant CreateOrderEndpoint participant PreCreateOrderHandler participant BasketService participant BookService participant CreateOrderHandler participant LockProvider participant OrderRepository participant Order Client->>CreateOrderEndpoint: POST /api/v1/orders with Idempotency-Key CreateOrderEndpoint->>PreCreateOrderHandler: Process(CreateOrderCommand) PreCreateOrderHandler->>BasketService: GetBasket() BasketService-->>PreCreateOrderHandler: BasketItems PreCreateOrderHandler->>BookService: GetBooksByIdsAsync(bookIds) BookService-->>PreCreateOrderHandler: BookDetails PreCreateOrderHandler->>PreCreateOrderHandler: Create OrderItems from basket and book details PreCreateOrderHandler-->>CreateOrderHandler: Populated CreateOrderCommand CreateOrderHandler->>CreateOrderHandler: Extract user ID from claims CreateOrderHandler->>LockProvider: TryAcquireLockAsync(userId) LockProvider-->>CreateOrderHandler: Lock handle CreateOrderHandler->>Order: new Order(userId, null, orderItems) Order->>Order: Register OrderPlacedEvent CreateOrderHandler->>OrderRepository: AddAsync(order) OrderRepository-->>CreateOrderHandler: Order with ID CreateOrderHandler-->>CreateOrderEndpoint: Order ID CreateOrderEndpoint-->>Client: 201 Created with Order ID ``` ### Key Components 1. **CreateOrderCommand**: Implements `ICommand` to create a new order and return its ID 2. **PreCreateOrderHandler**: Pre-processes the command by retrieving basket items and book details 3. **BasketService**: Provides the current user's basket items 4. **BookService**: Provides book details for pricing information 5. **Distributed Lock**: Ensures only one order can be created at a time for a given user 6. **OrderPlacedEvent**: Domain event triggered when a new order is created ## Business Rules - Orders are created automatically from the user's current basket - The buyer ID is automatically derived from the authenticated user's identity - Order items are created from basket items with pricing from the Catalog service - Each order item must have a quantity greater than zero - Each order item must have a price greater than or equal to zero - Initial order status is set to "New" upon creation - Total order value is calculated as the sum of all item prices multiplied by quantities - Distributed locking prevents concurrent order creation for the same user ## Domain Events Upon successful order creation, the following domain event is published: - `OrderPlacedEvent`: Contains the complete order information for downstream processing ## Idempotency The Create Order endpoint supports idempotency through the use of an Idempotency-Key header. This ensures that retrying the same request will not create duplicate orders. ## Architecture ## POST `(/api/v1/orders)` ### Request This endpoint does not require a request body as it automatically uses the authenticated user's current basket. ### Headers - **x-request-id** (required): A unique identifier for the request to ensure idempotency ### Example Usage ```bash curl -X POST "https://api.bookworm.com/api/v1/orders" \ -H "Authorization: Bearer " \ -H "x-request-id: " ``` ### Responses #### 201 Created - Returns the ID of the newly created order - Location header includes a versioned URL to the newly created resource - Format: `/api/v1/orders/{guid}` #### 400 Bad Request Returned when the request validation fails or when the basket is empty. #### 401 Unauthorized Returned when the request lacks valid authentication credentials. #### 409 Conflict Returned when another process is already creating an order for the same user or when a request with the same Idempotency-Key has already been processed. --- id: CreatePublisherCommand version: 1.0.0 name: Create Publisher summary: Create a publisher schemaPath: request-body.json badges: - content: POST textColor: green backgroundColor: green icon: PlusIcon - content: "Publisher" textColor: yellow backgroundColor: yellow icon: BuildingOfficeIcon - content: "Admin Only" textColor: red backgroundColor: red icon: LockClosedIcon owners: - nhanxnguyen --- ## Overview Creates a new Publisher entity in the Catalog domain. Publishers are aggregate roots that represent book publishing companies within our bounded context. This endpoint follows Domain-Driven Design principles by implementing the Command pattern through Mediator. The command is validated using FluentValidation before being processed by the domain service. ## Implementation Details The Create Publisher operation is implemented using the CQRS pattern with a dedicated command handler: ```mermaid sequenceDiagram participant Client participant CreatePublisherEndpoint participant CreatePublisherValidator participant CreatePublisherCommand participant PublisherRepository participant Publisher participant PublisherCreatedEvent participant EventDispatcher Client->>CreatePublisherEndpoint: POST /api/v1/publishers Note over CreatePublisherEndpoint: Validates admin authorization CreatePublisherEndpoint->>CreatePublisherValidator: Validate command alt Validation fails CreatePublisherValidator-->>CreatePublisherEndpoint: Return validation errors CreatePublisherEndpoint-->>Client: 400 Bad Request else Validation passes CreatePublisherEndpoint->>CreatePublisherCommand: Send(command) CreatePublisherCommand->>Publisher: new Publisher(name) Publisher->>Publisher: RegisterDomainEvent(new PublisherCreatedEvent()) CreatePublisherCommand->>PublisherRepository: AddAsync(publisher) CreatePublisherCommand->>PublisherRepository: SaveEntitiesAsync() PublisherRepository->>PublisherCreatedEvent: Publish event PublisherCreatedEvent->>EventDispatcher: Dispatch to catalog PublisherRepository-->>CreatePublisherCommand: Return publisher.Id CreatePublisherCommand-->>CreatePublisherEndpoint: Return publisher.Id CreatePublisherEndpoint-->>Client: 201 Created with Location header end ``` ### Key Components 1. **CreatePublisherCommand**: Implements `ICommand` to create a new publisher 2. **CreatePublisherHandler**: Processes the command using repository pattern 3. **CreatePublisherValidator**: Validates the command parameters using FluentValidation 4. **CreatePublisherEndpoint**: Maps the HTTP POST request to the command handler 5. **Publisher Entity**: Domain entity that encapsulates publisher data and business rules 6. **PublisherCreatedEvent**: Domain event raised when publisher is created ## Domain Context In our domain model, `Publisher` represents a core aggregate that encapsulates: - Unique identifier (Id) - Name of the publishing company - Associated metadata and business rules The Create Publisher command is a write operation that: 1. Validates the input parameters 2. Creates a new Publisher entity 3. Registers a PublisherCreatedEvent domain event 4. Persists the entity to the repository 5. Returns the unique identifier of the created publisher ## Business Rules - Name must be provided and cannot be empty - Name must not exceed the maximum length limit - The operation raises a domain event that can trigger side effects in other bounded contexts ## Technical Implementation The implementation uses several patterns and techniques: 1. **CQRS**: Separates the write model (command) from the read model (query) 2. **Repository Pattern**: The `IPublisherRepository` abstracts the data access layer 3. **Domain-Driven Design**: Uses domain entities and events to encapsulate business logic 4. **Minimal API**: Uses .NET's minimal API approach with endpoint mapping 5. **FluentValidation**: Validates the command parameters The command execution flow: 1. The endpoint receives the HTTP POST request with the publisher data 2. The validator ensures all business rules are satisfied 3. The command handler creates a new Publisher entity 4. The entity registers a domain event 5. The repository persists the entity and publishes domain events 6. The endpoint returns a 201 Created response with the publisher ID and location header ## Authorization This endpoint requires admin privileges. Users must have the Admin role to access this endpoint. ## Architecture ## POST `(/api/v1/publishers)` ### Request Body ### Example Usage ```bash curl -X POST https://api.bookworm.com/api/v1/publisers \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{ "name": "New Publisher" }' ``` #### Validation Rules - Name field is required and cannot be empty - Name must not exceed the large data schema length limit ### Responses #### 200 Ok - Returns the newly created publisher's ID (GUID) #### 400 Bad Request Returned when: - The request body fails validation rules - The name field is missing or empty - The name exceeds the maximum length limit #### 401 Unauthorized Returned when the request lacks valid authentication credentials. #### 403 Forbidden Returned when the authenticated user lacks admin privileges. ## Raw Schema:request-body.json { "required": ["id", "name"], "type": "object", "properties": { "name": { "type": "string", "description": "Publisher name", "minLength": 1, "maxLength": 100 } } } --- id: DeleteAuthorCommand version: 1.0.0 name: Delete Author summary: Delete an author from the catalog badges: - content: DELETE textColor: red backgroundColor: red icon: XMarkIcon - content: "Author" textColor: yellow backgroundColor: yellow icon: UserIcon - content: "Admin Only" textColor: red backgroundColor: red icon: LockClosedIcon owners: - nhanxnguyen --- ## Overview This endpoint allows for the removal of an Author entity from the Catalog domain. Following Domain-Driven Design principles, this operation: - Respects the Author aggregate boundary - Enforces domain invariants and business rules - Validates that the author can be safely removed without violating referential integrity - Returns proper error responses if the operation violates any domain constraints - Requires administrative privileges to execute The implementation uses CQRS (Command Query Responsibility Segregation) pattern with a `DeleteAuthorCommand` processed by Mediator, maintaining separation between the command interface and the domain logic. :::warning This endpoint requires administrative privileges. Only users with the Admin policy can perform this operation. ::: :::note[Important Validation Rules] 1. Authors with associated books cannot be deleted until those relationships are removed first 2. The author ID must exist in the system 3. The author ID must be a valid GUID format ::: ## Architecture ### Sequence Diagram ```mermaid sequenceDiagram participant Client participant API as DeleteAuthorEndpoint participant Validator as DeleteAuthorValidator participant Command as DeleteAuthorCommand participant Handler as DeleteAuthorHandler participant Repository as IAuthorRepository participant Database Client->>API: DELETE /api/v1/authors/{id} Note over API: Authorization check (Admin only) alt Unauthorized User API-->>Client: 401 Unauthorized else Not Admin Role API-->>Client: 403 Forbidden else Admin User API->>Command: Create DeleteAuthorCommand(id) API->>Handler: Send(DeleteAuthorCommand) Handler->>Validator: Validate(command) alt Invalid Author ID Format Validator-->>Handler: Validation Failed Handler-->>API: Validation Exception API-->>Client: 400 Bad Request else Valid Format Validator->>Repository: Check for associated books alt Has Associated Books Repository-->>Validator: Books found Validator-->>Handler: Validation Failed Handler-->>API: Validation Exception API-->>Client: 400 Bad Request (Cannot delete author with books) else No Associated Books Repository-->>Validator: No associated books Validator-->>Handler: Validation successful Handler->>Repository: GetByIdAsync(id) alt Author Not Found Repository-->>Handler: null Handler-->>API: NotFoundException API-->>Client: 404 Not Found else Author Exists Repository-->>Handler: Author entity Handler->>Repository: Delete(author) Handler->>Repository: UnitOfWork.SaveEntitiesAsync() Repository->>Database: Delete author record Database-->>Repository: Confirmation Repository-->>Handler: Success Handler-->>API: Unit.Value API-->>Client: 204 No Content end end end end ``` ## DELETE `(/api/v1/authors/{id})` ### Authorization Requires Admin role/policy to access this endpoint. ### Parameters - **id** (path) (required) - Format: GUID - Example: "a1e1b3b4-1b1b-4b1b-9b1b-1b1b1b1b1b1b" ### Request Body No request body required for this endpoint. ### Example Usage ```bash curl -X DELETE "https://api.bookworm.com/api/v1/authors/{id}" \ -H "Authorization: Bearer " ``` ### Responses #### 204 No Content Successful deletion of the author. #### 404 Not Found Returned when the specified author ID does not exist in the system. #### 401 Unauthorized Returned when the user is not authenticated. #### 403 Forbidden Returned when the authenticated user does not have administrative privileges. ### Implementation Details The deletion process follows these steps: 1. **API Endpoint (DeleteAuthorEndpoint)**: - Receives DELETE request at `/api/v1/authors/{id}` - Enforces Admin policy authorization - Creates a DeleteAuthorCommand with the author ID - Sends the command to the mediator 2. **Validation (DeleteAuthorValidator)**: - Validates the author ID format - Checks if the author has any associated books using the BookAuthorFilterSpec - Rejects deletion if books are associated with the author 3. **Command Handling (DeleteAuthorHandler)**: - Retrieves the author entity from the repository - Throws NotFoundException if author doesn't exist - Calls Delete method on the repository - Commits changes through UnitOfWork pattern 4. **Repository Operations**: - Removes the author from the database - Ensures domain events are dispatched if applicable - Returns 204 No Content upon successful deletion This implementation adheres to Domain-Driven Design principles by: - Encapsulating business logic in the domain model - Using the repository pattern to abstract data access - Implementing CQRS with command/handler separation - Validating business rules before executing operations ### Error Handling The endpoint implements comprehensive error handling: - Domain-specific validation through `DeleteAuthorValidator` - Not Found exceptions for non-existent authors - Business rule validation preventing deletion of authors with books --- id: DeleteBasketCommand version: 1.0.0 name: Delete Basket summary: Delete a basket by its unique identifier badges: - content: DELETE textColor: red backgroundColor: red icon: XMarkIcon - content: "Basket" textColor: yellow backgroundColor: yellow icon: ShoppingCartIcon owners: - nhanxnguyen --- ## Overview This endpoint allows you to delete a shopping basket by its unique identifier. This operation is an essential part of the Basket domain's lifecycle management. ### Business Rules - Basket must exist to be deleted - Only the authenticated owner of the basket can delete their own basket - Deleting a basket will permanently remove all items within it - The basket ID is automatically derived from the authenticated user's ID ### Authentication This endpoint requires authentication. The user's identity is verified using Keycloak authentication: - A valid authentication token must be provided - The user's ID is extracted from the `sub` claim in the JWT token - Unauthorized access will result in a 401 Unauthorized response ### Implementation Details The delete operation is handled by the `DeleteBasketCommandHandler` which: - Validates user authentication - Verifies basket ownership - Removes the basket from the repository - Uses the user's ID from claims as the basket identifier ```mermaid sequenceDiagram participant Client participant DeleteBasketEndpoint participant DeleteBasketCommand participant ClaimsPrincipal participant BasketRepository participant Redis Client->>DeleteBasketEndpoint: DELETE /api/v1/baskets Note over Client,DeleteBasketEndpoint: Authorization: Bearer {jwt-token} DeleteBasketEndpoint->>DeleteBasketCommand: Send(new DeleteBasketCommand()) DeleteBasketCommand->>ClaimsPrincipal: GetClaimValue(KeycloakClaimTypes.Subject) alt User not authenticated ClaimsPrincipal-->>DeleteBasketCommand: null or empty userId DeleteBasketCommand-->>DeleteBasketEndpoint: Throw UnauthorizedAccessException DeleteBasketEndpoint-->>Client: 401 Unauthorized else User authenticated ClaimsPrincipal-->>DeleteBasketCommand: userId DeleteBasketCommand->>BasketRepository: GetBasketAsync(userId) BasketRepository->>Redis: Get basket data alt Basket not found Redis-->>BasketRepository: null BasketRepository-->>DeleteBasketCommand: null DeleteBasketCommand-->>DeleteBasketEndpoint: Throw NotFoundException DeleteBasketEndpoint-->>Client: 404 Not Found else Basket exists Redis-->>BasketRepository: Basket data BasketRepository-->>DeleteBasketCommand: CustomerBasket DeleteBasketCommand->>BasketRepository: DeleteBasketAsync(userId) BasketRepository->>Redis: Remove basket data Redis-->>BasketRepository: Operation result BasketRepository-->>DeleteBasketCommand: true DeleteBasketCommand-->>DeleteBasketEndpoint: Unit.Value DeleteBasketEndpoint-->>Client: 204 No Content end end ``` ## Architecture ## DELETE `(/api/v1/baskets)` ### Request No request body is required. The basket ID is automatically determined from the authenticated user's identity. ### Example Usage ```bash curl -X DELETE https://api.bookworm.com/api/v1/baskets/{basketId} \ -H "Authorization: Bearer " ``` ### Responses #### 204 No Content Successful deletion of the basket. #### 401 Unauthorized Returned when: - No authentication token is provided - Invalid authentication token - Token has expired #### 404 Not Found Returned when: - The basket does not exist for the authenticated user ### Error Examples ```json title="401 Unauthorized" { "type": "https://tools.ietf.org/html/rfc7235#section-3.1", "title": "Unauthorized", "status": 401, "detail": "User is not authenticated." } ``` ```json title="404 Not Found" { "type": "https://tools.ietf.org/html/rfc7231#section-6.5.4", "title": "Not Found", "status": 404, "detail": "Basket with id {userId} not found." } ``` --- id: DeleteBookCommand version: 1.0.0 name: Delete Book summary: Delete a book if it exists badges: - content: DELETE textColor: red backgroundColor: red icon: XMarkIcon - content: "Book" textColor: yellow backgroundColor: yellow icon: BookOpenIcon - content: "Admin Only" textColor: red backgroundColor: red icon: LockClosedIcon owners: - nhanxnguyen --- ## Overview The DELETE operation performs a soft delete of a book entity from the catalog domain. This endpoint enforces proper domain boundaries by validating the book's existence and ensuring there are no constraint violations before deletion. The operation is idempotent - multiple identical requests will have the same effect as a single request, aligning with REST architectural constraints. **Authorization**: Requires "ADMIN" role to maintain aggregate integrity. **Domain Validation**: - Validates book exists before deletion - Performs a soft delete by marking the book as deleted rather than removing it from the database - Returns 404 Not Found if the book doesn't exist ## Technical Details ### Implementation Notes - Uses soft delete pattern through `ISoftDelete` interface - Maintains audit trail through `AuditableEntity` base class - Preserves data integrity while marking records as deleted - Handled by `DeleteBookHandler` using CQRS pattern with Mediator ### Error Handling The service implements robust error handling: - Returns 404 if book is not found - Returns 400 for invalid GUID format - Returns 403 if user lacks admin privileges ## Architecture ## Implementation Details The DELETE operation is implemented using the following components: ```mermaid sequenceDiagram participant Client participant Endpoint participant Handler participant Repository participant Book Client->>Endpoint: DELETE /api/v1/books/{id} Note over Endpoint: Validates admin authorization Endpoint->>Handler: Send(DeleteBookCommand) Handler->>Repository: GetByIdAsync(id) Repository-->>Handler: Book or null alt Book exists Handler->>Book: Delete() Note over Book: Sets IsDeleted = true Handler->>Repository: SaveEntitiesAsync() Handler-->>Endpoint: Unit.Value Endpoint-->>Client: 204 No Content else Book not found Handler-->>Endpoint: NotFoundException Endpoint-->>Client: 404 Not Found end ``` ## DELETE `(/api/v1/books/{id})` ### Parameters - **id** (path) (required) - Format: GUID - Example: "123e4567-e89b-12d3-a456-426614174000" ### Request Body ### Example Usage ```bash curl -X DELETE "https://api.bookworm.com/api/v1/books/{id}" \ -H "Authorization: Bearer " ``` ### Responses #### 204 No Content Successful deletion of the book. #### 400 Bad Request Invalid request format or constraints violation. #### 404 Not Found Book with specified ID does not exist. #### 403 Forbidden User does not have admin privileges. --- id: DeleteBuyerCommand version: 1.0.0 name: Delete Buyer summary: Delete buyer by buyer id if exists badges: - content: DELETE textColor: red backgroundColor: red icon: XMarkIcon - content: "Buyer" textColor: yellow backgroundColor: yellow icon: UserGroupIcon - content: "Admin Only" textColor: red backgroundColor: red icon: LockClosedIcon owners: - nhanxnguyen --- ## Overview This endpoint allows for the removal of a Buyer aggregate from the system. In the Ordering domain, a Buyer represents a customer who has placed one or more orders. This operation is restricted to administrators only. ## Architecture ### Domain Significance Within the bounded context of the Ordering domain, the Buyer aggregate root contains important customer information related to ordering, including: - Address information - Order history references - Payment methods ### Command Handling When this endpoint is called: 1. A `DeleteBuyerCommand` is dispatched via the CQRS pattern with the Buyer ID 2. The command handler retrieves the Buyer aggregate from the repository 3. If the Buyer is not found, a `NotFoundException` is thrown 4. If found, the Buyer is marked for deletion in the repository 5. Changes are persisted through the Unit of Work pattern ### Security Considerations This endpoint is protected by the Admin authorization policy, ensuring that only administrators can delete buyer records. This is implemented through the `.RequireAuthorization(Authorization.Policies.Admin)` middleware. ### Consistency Considerations This operation maintains aggregate consistency through the Unit of Work pattern, ensuring that all changes are committed atomically. The operation either succeeds completely or fails without partial updates. ### Error Handling - If the buyer with the specified ID does not exist, a 404 Not Found response is returned - Authorization failures result in a 401 Unauthorized or 403 Forbidden response - Other exceptions are handled by the global exception handler ## DELETE `(/api/v1/buyers/{id})` ### Parameters - **id** (path) (required): The buyer's unique identifier (GUID format) ### Example Usage ```bash curl -X DELETE "https://api.bookworm.com/api/v1/buyers/{id}" \ -H "Authorization: Bearer " ``` ### Responses #### 204 No Content The buyer was successfully deleted. No content is returned in the response body. #### 404 Not Found ```json title="404 Not Found" { "type": "https://tools.ietf.org/html/rfc7231#section-6.5.4", "title": "Not Found", "status": 404, "detail": "Buyer with ID {id} not found." } ``` #### 401 Unauthorized Returned when the request lacks valid authentication credentials. #### 403 Forbidden Returned when the authenticated user does not have administrator privileges. --- id: DeleteCategoryCommand version: 1.0.0 name: Delete Category summary: Delete a category from the catalog badges: - content: DELETE textColor: red backgroundColor: red icon: XMarkIcon - content: "Category" textColor: yellow backgroundColor: yellow icon: TagIcon - content: "Admin Only" textColor: red backgroundColor: red icon: LockClosedIcon owners: - nhanxnguyen --- ## Overview This endpoint allows for the removal of a Category entity from the Catalog domain. Following Domain-Driven Design principles, this operation: - Respects the Category aggregate boundary - Enforces domain invariants and business rules - Validates that the category can be safely removed without violating referential integrity - Returns proper error responses if the operation violates any domain constraints - Requires administrative privileges to execute The implementation uses CQRS (Command Query Responsibility Segregation) pattern with a `DeleteCategoryCommand` processed by Mediator, maintaining separation between the command interface and the domain logic. :::warning This endpoint requires administrative privileges. Only users with the Admin policy can perform this operation. ::: :::note[Important Validation Rules] 1. Categories with associated products cannot be deleted until those relationships are removed first 2. The category ID must exist in the system 3. The category ID must be a valid GUID format ::: ## Architecture ### Sequence Diagram ```mermaid sequenceDiagram participant Client participant API participant CatalogService Client->>API: DELETE /categories/{id} API->>CatalogService: DeleteCategoryCommand CatalogService-->>API: 204 No Content API-->>Client: 204 No Content ``` ## Example Usage ```bash curl -X DELETE "https://api.bookworm.com/api/v1/categories/123e4567-e89b-12d3-a456-426614174000" \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN" ``` ### Responses #### 204 No Content Successful deletion of the author. #### 404 Not Found Returned when the specified author ID does not exist in the system. #### 401 Unauthorized Returned when the user is not authenticated. #### 403 Forbidden Returned when the authenticated user does not have administrative privileges. --- id: DeleteChatCommand version: 1.0.0 name: Delete Chat summary: Delete a chat badges: - content: DELETE textColor: red backgroundColor: red icon: XMarkIcon - content: "Chat" textColor: yellow backgroundColor: yellow icon: ChatBubbleBottomCenterTextIcon owners: - nhanxnguyen deprecated: date: 2025-10-05 message: | This command has been deprecated and removed from the API. It is no longer available for use. --- ## Overview The Delete Chat endpoint allows users to permanently remove a chat session from the BookWorm platform. This endpoint accepts a chat's unique identifier (GUID) and deletes the associated chat session if it exists. It is designed to provide users with the ability to manage their chat history effectively. The endpoint follows RESTful principles and returns a 204 No Content response upon successful deletion, indicating that the request was processed successfully but no content needs to be returned. ## Architecture ## DELETE `/api/v1/chats/{id:guid}` Deletes a chat session by its unique identifier. ### Path Parameters | Name | Type | Description | | ---- | ---- | ----------------------------------------------- | | id | GUID | The unique identifier of the chat to be deleted | ### Example Usage ```bash curl -X DELETE https://api.bookworm.com/api/v1/chats/{id} \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN" ``` ### Response #### 204 No Content Returns a 204 No Content response if the chat is successfully deleted. This indicates that the request was processed successfully but there is no content to return. ### 404 Not Found Indicates that the specified chat identifier does not exist in the system. This response is returned when attempting to delete a chat that has already been removed or never existed. --- id: DeleteFeedbackCommand version: 1.0.0 name: Delete Feedback summary: Delete feedback for a book in the Rating Service badges: - content: DELETE textColor: red backgroundColor: red icon: XMarkIcon - content: "Feedback" textColor: yellow backgroundColor: yellow icon: StarIcon - content: "Admin Only" textColor: blue backgroundColor: blue icon: LockClosedIcon owners: - nhanxnguyen --- ## Overview The Delete Feedback operation allows removing customer feedback from the Rating domain. This endpoint is part of the Rating bounded context and operates on the Feedback aggregate. ## Implementation Details The Delete Feedback operation is implemented using the CQRS pattern with a dedicated command handler: ```mermaid sequenceDiagram participant Client participant DeleteFeedbackEndpoint participant DeleteFeedbackCommand participant FeedbackRepository participant Feedback participant FeedbackDeletedEvent participant EventDispatcher Client->>DeleteFeedbackEndpoint: DELETE /api/v1/feedbacks/{id} Note over Client,DeleteFeedbackEndpoint: Requires Admin authorization DeleteFeedbackEndpoint->>DeleteFeedbackCommand: Send(new DeleteFeedbackCommand(id)) DeleteFeedbackCommand->>FeedbackRepository: GetByIdAsync(id) FeedbackRepository-->>DeleteFeedbackCommand: Feedback entity alt Feedback not found DeleteFeedbackCommand-->>DeleteFeedbackEndpoint: Throw NotFoundException else Feedback found DeleteFeedbackCommand->>Feedback: Remove() Feedback->>Feedback: RegisterDomainEvent(new FeedbackDeletedEvent()) DeleteFeedbackCommand->>FeedbackRepository: Delete(feedback) DeleteFeedbackCommand->>FeedbackRepository: SaveEntitiesAsync() FeedbackRepository->>FeedbackDeletedEvent: Publish event FeedbackDeletedEvent->>EventDispatcher: Dispatch to catalog-feedback-deleted channel DeleteFeedbackCommand-->>DeleteFeedbackEndpoint: Unit.Value DeleteFeedbackEndpoint-->>Client: 204 No Content end ``` ### Key Components 1. **DeleteFeedbackCommand**: Implements `ICommand` to delete a feedback by ID 2. **Admin Authorization**: Endpoint is protected with the Admin policy 3. **Domain Events**: `FeedbackDeletedEvent` is raised when feedback is removed 4. **Integration Events**: Events are dispatched to the catalog service for rating recalculation ## Domain Context Within our domain model, Feedback represents a customer's opinion about a book, which is an important entity in our system. The Feedback aggregate contains: - Book reference (BookId) - Customer information (FirstName, LastName) - Rating value (0-5 scale) - Comment text Deleting feedback is a domain operation that: 1. Retrieves the feedback entity from the repository 2. Calls the `Remove()` method on the Feedback aggregate 3. Raises a `FeedbackDeletedEvent` with the book ID, rating, and feedback ID 4. Physically removes the feedback entity from the repository 5. Publishes an integration event to notify the Catalog service ## Command Flow When a DELETE request is received: 1. The `DeleteFeedbackEndpoint` validates the request and authorization 2. The `DeleteFeedbackCommand` is dispatched through the mediator 3. The command handler retrieves the feedback entity from the repository 4. If the feedback is not found, a `NotFoundException` is thrown 5. The `Remove()` method is called on the feedback entity, which registers a domain event 6. The feedback is removed from the repository 7. The unit of work is saved, which publishes the domain events 8. The `FeedbackDeletedEventHandler` dispatches an integration event to the Catalog service 9. A 204 No Content response is returned to the client ## Business Rules - Only administrators can delete feedback (enforced by the Admin policy) - Feedback must exist to be deleted - Deleting feedback triggers recalculation of the aggregate rating for the associated book This operation maintains the integrity of our Rating domain while allowing administrators to manage inappropriate content within the system. ## Architecture ## DELETE `(/api/v1/feedbacks/{id})` ### Path Parameters - **id** (path) (required): The unique identifier of the feedback to delete ### Example Usage ```bash curl -X DELETE "https://api.bookworm.com/api/v1/feedbacks/{feedbackId}" \ -H "Authorization: Bearer " ``` ### Responses #### 204 No Content Returned when the feedback is successfully deleted. #### 401 Unauthorized Returned when the request lacks valid authentication credentials. #### 403 Forbidden Returned when the authenticated user does not have administrator privileges. #### 404 Not Found Returned when the feedback with the specified ID does not exist. --- id: DeleteOrderCommand version: 1.0.0 name: Delete Order summary: Delete order by order id if exists badges: - content: DELETE textColor: red backgroundColor: red icon: XMarkIcon - content: "Order" textColor: yellow backgroundColor: yellow icon: ListBulletIcon - content: "Admin Only" textColor: red backgroundColor: red icon: LockClosedIcon owners: - nhanxnguyen --- ## Overview This endpoint allows administrators to delete an order from the system. In alignment with Domain-Driven Design principles, order deletion is handled as a domain operation that respects the Order aggregate boundary and implements a soft-delete pattern. ### Domain Considerations - **Soft Delete Pattern**: The Order entity implements the `ISoftDelete` interface, setting a flag rather than physically removing records - **Aggregate Consistency**: The deletion operation ensures the entire Order aggregate maintains its consistency through the Unit of Work pattern - **Domain Events**: The operation may trigger domain events for cross-service communication ### Implementation Details When an order is deleted: 1. The command handler retrieves the Order aggregate by ID 2. If the order doesn't exist, a `NotFoundException` is thrown 3. The `Delete()` method on the Order entity sets the `IsDeleted` flag to true 4. Changes are persisted through the Unit of Work pattern ### Security Considerations This endpoint is protected by the Admin authorization policy, ensuring that only administrators can delete orders. This is implemented through the `.RequireAuthorization(Authorization.Policies.Admin)` middleware. ## Architecture ## DELETE `(/api/v1/orders/{id})` ### Parameters - **id** (path) (required): The order's unique identifier (GUID format) ### Example Usage ```bash curl -X DELETE "https://api.bookworm.com/api/v1/orders/{id}" \ -H "Authorization: Bearer " ``` ### Responses #### 204 No Content The order was successfully deleted. No content is returned in the response body. #### 404 Not Found ```json title="404 Not Found" { "type": "https://tools.ietf.org/html/rfc7231#section-6.5.4", "title": "Not Found", "status": 404, "detail": "Order with id {id} not found." } ``` #### 401 Unauthorized Returned when the request lacks valid authentication credentials. #### 403 Forbidden Returned when the authenticated user does not have administrator privileges. --- id: DeletePublisherCommand version: 1.0.0 name: Delete Publisher summary: Delete a publisher from the catalog badges: - content: DELETE textColor: red backgroundColor: red icon: XMarkIcon - content: "Publisher" textColor: yellow backgroundColor: yellow icon: BuildingOfficeIcon - content: "Admin Only" textColor: red backgroundColor: red icon: LockClosedIcon owners: - nhanxnguyen --- ## Overview This endpoint allows for the removal of a Publisher entity from the Catalog domain. Following Domain-Driven Design principles, this operation: - Respects the Publisher aggregate boundary - Enforces domain invariants and business rules - Validates that the publisher can be safely removed without violating referential integrity - Returns proper error responses if the operation violates any domain constraints - Requires administrative privileges to execute The implementation uses CQRS (Command Query Responsibility Segregation) pattern with a `DeletePublisherCommand` processed by Mediator, maintaining separation between the command interface and the domain logic. :::warning This endpoint requires administrative privileges. Only users with the Admin policy can perform this operation. ::: :::note[Important Validation Rules] 1. Publishers with associated books cannot be deleted until those relationships are removed first 2. The publisher ID must exist in the system 3. The publisher ID must be a valid GUID format ::: ## Architecture ### Sequence Diagram ```mermaid sequenceDiagram participant Client participant API participant CatalogService Client->>API: DELETE /publishers/{id} API->>CatalogService: DeletePublisherCommand CatalogService-->>API: 204 No Content API-->>Client: 204 No Content ``` ## Example Usage ```bash curl -X DELETE "https://api.bookworm.com/api/v1/publishers/123e4567-e89b-12d3-a456-426614174000" \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN" ``` ### Responses #### 204 No Content Successful deletion of the author. #### 404 Not Found Returned when the specified author ID does not exist in the system. #### 401 Unauthorized Returned when the user is not authenticated. #### 403 Forbidden Returned when the authenticated user does not have administrative privileges. --- id: UpdateAuthorCommand version: 1.0.0 name: Update Author summary: Update an author's information schemaPath: request-body.json badges: - content: PUT textColor: orange backgroundColor: orange icon: PencilIcon - content: "Author" textColor: yellow backgroundColor: yellow icon: UserIcon - content: "Admin Only" textColor: red backgroundColor: red icon: LockClosedIcon owners: - nhanxnguyen --- ## Overview Update an existing author in the catalog domain. This endpoint adheres to Domain-Driven Design principles by: - Enforcing domain rules and invariants during update operations - Validating author data against the bounded context constraints - Generating domain events upon successful updates - Maintaining aggregate consistency The operation is idempotent and will return appropriate error responses if the author ID does not exist or if business rules are violated. Updates will be propagated through domain events to maintain consistency across the system. ## Security - Requires administrative privileges (Admin policy) - Authentication is required ## Technical Details ### Validation Rules - Author ID must be a valid GUID and cannot be empty - Author name is required - Author name must not exceed the large data schema length ### Implementation Notes - Uses CQRS pattern with Mediator implementation - Performs optimistic concurrency control through unit of work - Implements proper domain event handling - Returns 204 No Content on successful update ## Architecture ## PUT `(/api/v1/authors/{id})` ### Parameters - **id** (path) (required) - Format: GUID - Description: Unique identifier of the author to update ### Request Body ### Example Usage ```bash curl -X PUT "https://api.bookworm.com/api/v1/authors" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer " \ -d '{ "id": "01961eb4-668d-7e7e-ae25-0fab379614f7", "name": "Updated Author Name" }' ``` ### Responses #### 204 No Content Returned when the author is successfully updated. #### 400 Bad Request Returned when: - The request body fails validation - The author name format is invalid - The author name exceeds maximum length #### 404 Not Found Returned when the specified author ID does not exist in the system. #### 401 Unauthorized Returned when the request lacks valid authentication credentials. #### 403 Forbidden Returned when the authenticated user lacks administrative privileges. ## Raw Schema:request-body.json { "required": ["id", "name"], "type": "object", "properties": { "name": { "type": "string", "description": "Author name", "minLength": 1, "maxLength": 100 } } } --- id: UpdateBasketCommand version: 1.0.0 name: Update Basket summary: Update a basket schemaPath: request-body.json badges: - content: PUT textColor: orange backgroundColor: orange icon: PencilSquareIcon - content: "Basket" textColor: yellow backgroundColor: yellow icon: ShoppingCartIcon owners: - nhanxnguyen --- ## Overview This endpoint updates an existing basket entity or creates a new one if it doesn't exist. The basket represents an aggregate root in our domain model that maintains its own consistency boundaries. The operation follows the Repository Pattern for persistence and triggers domain events when the basket state changes. This aligns with our DDD approach by: 1. Treating the basket as a complete aggregate with items as value objects 2. Maintaining invariants during the update operation 3. Encapsulating business rules within the domain model 4. Ensuring atomic updates to maintain data consistency 5. Publishing domain events for cross-boundary communication When a basket is updated, the system publishes integration events that other bounded contexts (like Catalog and Ordering) can subscribe to if needed. ## Implementation Details The Update Basket operation is implemented using the CQRS pattern with a dedicated command handler: ```mermaid sequenceDiagram participant Client participant UpdateBasketEndpoint participant UpdateBasketValidator participant UpdateBasketCommand participant ClaimsPrincipal participant CustomerBasket participant BasketRepository participant Redis Client->>UpdateBasketEndpoint: PUT /api/v1/baskets Note over Client,UpdateBasketEndpoint: Authorization: Bearer {jwt-token} Note over Client,UpdateBasketEndpoint: Request body with basket items UpdateBasketEndpoint->>UpdateBasketValidator: Validate command alt Validation fails UpdateBasketValidator-->>UpdateBasketEndpoint: Return validation errors UpdateBasketEndpoint-->>Client: 400 Bad Request else Validation passes UpdateBasketEndpoint->>UpdateBasketCommand: Send(command) UpdateBasketCommand->>ClaimsPrincipal: GetClaimValue(KeycloakClaimTypes.Subject) alt User not authenticated ClaimsPrincipal-->>UpdateBasketCommand: null or empty userId UpdateBasketCommand-->>UpdateBasketEndpoint: Throw UnauthorizedAccessException UpdateBasketEndpoint-->>Client: 401 Unauthorized else User authenticated ClaimsPrincipal-->>UpdateBasketCommand: userId UpdateBasketCommand->>BasketRepository: GetBasketAsync(userId) BasketRepository->>Redis: Get basket data alt Basket not found Redis-->>BasketRepository: null BasketRepository-->>UpdateBasketCommand: null UpdateBasketCommand-->>UpdateBasketEndpoint: Throw NotFoundException UpdateBasketEndpoint-->>Client: 404 Not Found else Basket exists Redis-->>BasketRepository: Basket data BasketRepository-->>UpdateBasketCommand: CustomerBasket UpdateBasketCommand->>CustomerBasket: Update(items) Note over CustomerBasket: Clear existing items and add new ones UpdateBasketCommand->>BasketRepository: UpdateBasketAsync(basket) BasketRepository->>Redis: Store updated basket data Redis-->>BasketRepository: Operation result BasketRepository-->>UpdateBasketCommand: Updated CustomerBasket UpdateBasketCommand-->>UpdateBasketEndpoint: Unit.Value UpdateBasketEndpoint-->>Client: 204 No Content end end end ``` ### Key Components 1. **UpdateBasketCommand**: Implements `ICommand` to update an existing basket with new items 2. **UpdateBasketHandler**: Processes the command using repository pattern 3. **UpdateBasketValidator**: Validates the command parameters using FluentValidation 4. **CustomerBasket**: Domain entity that encapsulates basket data and business rules 5. **BasketItem**: Value object representing an item in the basket 6. **IBasketRepository**: Repository interface for basket persistence operations ### Technical Implementation The update operation follows these steps: 1. **Authentication**: Extracts the user ID from the JWT token claims 2. **Basket Retrieval**: Fetches the existing basket using the user ID as the key 3. **Domain Update**: Calls the `Update` method on the `CustomerBasket` entity 4. **Persistence**: Saves the updated basket to the repository 5. **Response**: Returns a 204 No Content response on success The implementation enforces several key principles: - **Command Validation**: Ensures the request contains valid data before processing - **Domain Encapsulation**: The basket entity controls its own state changes - **Repository Abstraction**: Data access is abstracted behind a repository interface - **Error Handling**: Specific exceptions for different error scenarios ## Authentication This endpoint requires authentication. The user must be logged in with a valid JWT token containing a Keycloak subject claim. The basket is associated with the authenticated user's ID. ## Validation Rules The following validation rules are enforced: - The basket items collection must not be empty - For each basket item: - Book ID must not be empty - Quantity must be greater than 0 ## Architecture ## PUT `(/api/v1/baskets)` ### Request Body ### Example Usage ```bash curl -X PUT https://api.bookworm.com/api/v1/baskets \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{ "items": [ { "id": "0195e52d-4f5b-7280-9a17-ec3bf0a5f733", "quantity": 2 }, { "id": "0195e52d-4f5b-7a69-8489-2c0985141a63", "quantity": 1 } ] }' ``` ### Responses #### 204 No Content Returned when the basket is successfully updated. #### 400 Bad Request Returned when the request validation fails. #### 401 Unauthorized Returned when: - The user is not authenticated - The authentication token is missing or invalid #### 404 Not Found Returned when the basket for the authenticated user cannot be found. ## Error Handling The service handles various error scenarios: 1. **Validation Errors**: Returns 400 Bad Request with detailed validation messages 2. **Authentication Errors**: Returns 401 Unauthorized if user is not properly authenticated 3. **Not Found Errors**: Returns 404 if the basket doesn't exist ## Implementation Notes - The endpoint is versioned (v1) and follows REST principles - Uses optimistic concurrency for basket updates - Implements domain-driven design patterns with `Basket` as an aggregate root - Provides immediate consistency for basket operations ## Raw Schema:request-body.json { "required": ["items"], "type": "object", "properties": { "items": { "type": "array", "items": { "required": ["id", "quantity"], "type": "object", "properties": { "id": { "type": "string", "description": "The ID of the product to add to the basket" }, "quantity": { "type": "integer", "format": "int32", "description": "The quantity of the product to add to the basket" } } } } } } --- id: UpdateBookCommand version: 1.0.0 name: Update Book summary: Update a book schemaPath: request-body.json badges: - content: PUT textColor: orange backgroundColor: orange icon: PencilIcon - content: "Book" textColor: yellow backgroundColor: yellow icon: BookOpenIcon - content: "Admin Only" textColor: red backgroundColor: red icon: LockClosedIcon owners: - nhanxnguyen --- ## Overview This endpoint enables the modification of an existing book entity in the catalog domain. Following Domain-Driven Design principles, it updates the aggregate root (Book) while maintaining its invariants and business rules. The operation is idempotent and will return appropriate error responses if the book ID does not exist or if business rules are violated. Updates will be propagated through domain events to maintain consistency across the system. This endpoint respects the domain boundaries of the Catalog service and enables the modification of book attributes while preserving the entity's identity and integrity. ## Architecture ## PUT `(/api/v1/books/{id})` ### Parameters - **id** (path) (required) - The unique identifier of the book to update (GUID format) ### Request Body The request should be sent as `multipart/form-data` with the following fields: ### Example Usage ```bash curl -X PUT "https://api.bookworm.com/api/v1/books" \ -H "Content-Type: multipart/form-data" \ -H "Authorization: Bearer " \ -F "Id=01961eb4-668d-7e7e-ae25-0fab379614f7" \ -F "name=Updated Book Name" \ -F "description=Updated Book Description" \ -F "price=19.99" \ -F "priceSale=14.99" \ -F "categoryId=123e4567-e89b-12d3-a456-426614174000" \ -F "publisherId=123e4567-e89b-12d3-a456-426614174000" \ -F "authorIds=123e4567-e89b-12d3-a456-426614174000,123e4567-e89b-12d3-a456-426614174001" \ -F "image=@/path/to/image.jpg" ``` ### Validation Rules The following validation rules are applied to the request: - **Name**: Required, maximum length of 100 characters - **Description**: Optional, maximum length of 4000 characters - **Price**: Required, must be greater than 0 - **PriceSale**: Optional, must be greater than 0 and less than or equal to the regular price - **CategoryId**: Required, must be a valid GUID - **PublisherId**: Required, must be a valid GUID - **AuthorIds**: Required, must be a non-empty array of valid GUIDs - **Image**: Optional, if provided: - Maximum file size: 1MB (1048576 bytes) - Allowed file types: JPEG and PNG only ### Responses #### 204 No Content The book was successfully updated. No content is returned in the response body. #### 400 Bad Request The request is invalid. This can occur when: - Validation rules are violated - The provided data is in an incorrect format - The image file exceeds size limits or is of an unsupported type #### 404 Not Found The book with the specified ID does not exist in the system. #### 401 Unauthorized The request is not authenticated. This endpoint requires admin privileges. ### Domain Events Upon successful update, the following domain events are triggered: - `BookUpdatedEvent`: When the book's name or description is modified - The book's embedding in the vector database is automatically updated to reflect the changes ## Raw Schema:request-body.json { "required": ["id", "name", "description", "price", "categoryId", "publisherId", "authorIds"], "type": "object", "properties": { "id": { "type": "string", "format": "uuid", "description": "Product ID" }, "name": { "type": "string", "description": "Product name", "minLength": 1, "maxLength": 100 }, "description": { "type": "string", "description": "Product description", "maxLength": 500 }, "price": { "type": "number", "description": "Product price", "minimum": 0 }, "priceSale": { "type": "number", "description": "Product sale price", "minimum": 0, "maximum": "ref:price" }, "categoryId": { "type": "string", "format": "uuid", "description": "Category ID" }, "publisherId": { "type": "string", "format": "uuid", "description": "Publisher ID" }, "authorIds": { "type": "array", "items": { "type": "string", "format": "uuid", "description": "Author ID" } }, "image": { "type": "string", "format": "binary", "description": "Product image" }, "isRemoveImage": { "type": "boolean", "description": "Is remove image" } } } --- id: UpdateBuyerAddressCommand version: 1.0.0 name: Update Buyer Address summary: Update buyer address by buyer id if exists badges: - content: PATCH textColor: purple backgroundColor: purple icon: PencilSquareIcon - content: "Buyer" textColor: yellow backgroundColor: yellow icon: UserGroupIcon - content: "Authenticated" textColor: green backgroundColor: green icon: UserIcon owners: - nhanxnguyen --- ## Overview This endpoint allows authenticated users to update their address information within the Ordering bounded context. In DDD terms, the Buyer is an aggregate root within the Ordering domain that contains Address as a value object. The operation respects the invariants of the Buyer aggregate by ensuring that address changes are validated and consistently applied. Address updates are significant domain events as they can affect shipping options, tax calculations, and order fulfillment processes. ## Implementation Details When a buyer's address is updated: 1. The system retrieves the buyer ID from the authenticated user's claims 2. The buyer entity is fetched from the repository 3. If the buyer doesn't exist, a `NotFoundException` is thrown 4. The `UpdateAddress` method on the Buyer entity is called with the new address details 5. Changes are persisted through the Unit of Work pattern ## Validation Rules - Street is required (maximum length: 50 characters) - City is required (maximum length: 50 characters) - Province is required (maximum length: 50 characters) ## Architecture ## PATCH `(/api/v1/buyers/address)` ### Request ### Example Usage ```bash curl -X PATCH "https://api.bookworm.com/api/v1/buyers/address" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer " \ -d '{ "street": "123 Main Street", "city": "New York", "province": "NY" }' ``` ### Responses #### 200 OK Return buyer details with updated address. Example response: ```json title="200 OK" { "id": "123e4567-e89b-12d3-a456-426655440000", "name": "John Doe", "email": "john.doe@example.com", "address": { "street": "123 Main Street", "city": "New York", "province": "NY" } } ``` #### 400 Bad Request Returned when the request validation fails. ```json title="400 Bad Request" { "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1", "title": "Bad Request", "status": 400, "errors": { "Street": ["The Street field is required."], "City": ["The City field is required."], "Province": ["The Province field is required."] } } ``` #### 404 Not Found ```json title="404 Not Found" { "type": "https://tools.ietf.org/html/rfc7231#section-6.5.4", "title": "Not Found", "status": 404, "detail": "Buyer with id {id} not found." } ``` #### 401 Unauthorized Returned when the request lacks valid authentication credentials. --- id: UpdateCategoryCommand version: 1.0.0 name: Update Category summary: Update a category schemaPath: request-body.json badges: - content: PUT textColor: orange backgroundColor: orange icon: PencilIcon - content: "Category" textColor: yellow backgroundColor: yellow icon: TagIcon - content: "Admin Only" textColor: red backgroundColor: red icon: LockClosedIcon owners: - nhanxnguyen --- ## Overview This endpoint allows modification of an existing category entity within the Catalog domain. It follows Domain-Driven Design principles by ensuring that the category update operation maintains domain integrity and business rules. The operation performs the following: 1. Validates the incoming category update command against domain invariants 2. Retrieves the existing category aggregate from the repository 3. Updates the category properties while preserving aggregate consistency 4. Persists changes through the repository pattern Note that this endpoint enforces proper domain authorization and validation rules. The operation is transactional and will either succeed completely or fail without partial updates. ## Authentication & Authorization This endpoint requires authentication and admin-level authorization. The request must include a valid authentication token with admin privileges. ## Architecture ## PUT `(/api/v1/categories/{id})` ### Parameters - **id** (path) (required) - The unique identifier (GUID) of the category to update ### Request Body ### Example Usage ```bash curl -X PUT "https://api.bookworm.com/api/v1/categories" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer " \ -d '{ "id": "01961eb4-668d-7e7e-ae25-0fab379614f7", "name": "Updated Category Name" }' ``` ### Validation Rules The following validation rules are applied to the request: - Category ID must not be empty - Category name must not be empty - Category name must not exceed the maximum length (Medium) ### Responses #### 400 Bad Request Returned when the request body fails validation or contains invalid data. #### 404 Not Found Returned when the specified category ID does not exist in the system. #### 401 Unauthorized Returned when the request lacks valid authentication credentials. #### 403 Forbidden Returned when the authenticated user does not have admin privileges. #### 204 No Content Returned when the category is successfully updated. ## Error Handling The endpoint implements proper error handling for various scenarios: - Invalid input data is rejected with appropriate validation messages - Non-existent categories return a 404 error - Unauthorized access attempts are rejected - Database errors are handled gracefully - All operations are wrapped in a transaction to ensure data consistency ## Raw Schema:request-body.json { "required": ["id", "name"], "type": "object", "properties": { "name": { "type": "string", "description": "Category name", "minLength": 1, "maxLength": 100 } } } --- id: UpdateChatCommand version: 1.0.0 name: Update Chat summary: Update an existing chat in the catalog system schemaPath: request-body.json badges: - content: PUT textColor: green backgroundColor: green icon: PencilSquareIcon - content: "Chat" textColor: yellow backgroundColor: yellow icon: ChatBubbleBottomCenterTextIcon owners: - nhanxnguyen deprecated: date: 2025-10-05 message: | This command has been deprecated and removed from the API. It is no longer available for use. --- ## Overview The Update Chat endpoint allows users to modify an existing chat session in the catalog system. This endpoint accepts a chat identifier and a text prompt, updating the chat with the new information. It is designed to enhance the interactivity of conversations within the BookWorm platform. ## Architecture ## PUT `/api/v1/chats` Updates an existing chat in the catalog system and returns the updated chat's unique identifier. ### Request Body ### Example Usage ```bash curl -X PUT https://api.bookworm.com/api/v1/chats/123e4567-e89b-12d3-a456-426614174000 \ -H "Content-Type: application/json" \ -d '{ "id": "123e4567-e89b-12d3-a456-426614174000", "prompt": { "text": "What is the capital of France?" } }' ``` ### Response #### 204 No Content Indicates that the chat was successfully updated. No content is returned in the response body. ### 404 Not Found Indicates that the specified chat identifier does not exist in the system. ## Raw Schema:request-body.json { "required": ["id"], "type": "object", "properties": { "id": { "type": "string", "format": "uuid", "description": "The unique identifier of the chat to be updated" }, "prompt": { "type": "object", "required": ["text"], "properties": { "text": { "type": "string", "description": "The text of the prompt to be updated" } } } }, "isSchema": true } --- id: UpdatePublisherCommand version: 1.0.0 name: Update Publisher summary: Update a publisher schemaPath: request-body.json badges: - content: PUT textColor: orange backgroundColor: orange icon: PencilIcon - content: "Publisher" textColor: yellow backgroundColor: yellow icon: BuildingOfficeIcon - content: "Admin Only" textColor: red backgroundColor: red icon: LockClosedIcon owners: - nhanxnguyen --- ## Overview Update an existing publisher in the catalog domain. This endpoint follows Domain-Driven Design principles, where the publisher entity is part of the catalog bounded context. When a publisher is updated, the command is validated against business rules before being processed by the domain service. The operation is idempotent and ensures data integrity through optimistic concurrency control. If the publisher doesn't exist, the system will return an appropriate error response rather than creating a new entity, maintaining the command-query separation principle. This endpoint supports the catalog domain's business capability to manage publisher information as a core domain concept. ## Architecture ## PUT `(/api/v1/publishers/{id})` ### Parameters - **id** (path) (required) - The unique identifier of the publisher to update (GUID format) ### Request Body ### Example Usage ```bash curl -X PUT "https://api.bookworm.com/api/v1/publishers" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer " \ -d '{ "id": "01961eb4-668d-7e7e-ae25-0fab379614f7", "name": "Updated Publisher Name" }' ``` ### Authorization This endpoint requires admin-level authorization to access. The request must include a valid authentication token with the appropriate admin privileges. ### Validation Rules The following validation rules are applied to the request: 1. Publisher ID: - Must not be empty - Must be a valid GUID format 2. Publisher Name: - Must not be empty - Maximum length: Medium (as defined in DataSchemaLength) ### Responses #### 400 Bad Request Returned when the request validation fails. This includes: - Invalid publisher ID format - Empty or invalid publisher name - Name exceeds maximum length #### 401 Unauthorized Returned when the request is not authenticated or the user lacks admin privileges. #### 404 Not Found Returned when the specified publisher ID does not exist in the system. #### 204 No Content Returned when the publisher is successfully updated. The response body is empty. ### Error Handling The endpoint implements proper error handling for various scenarios: - Validation errors are returned as 400 Bad Request - Authentication/Authorization errors return 401 Unauthorized - Non-existent publishers return 404 Not Found - Server errors return appropriate 5xx status codes ### Versioning This endpoint is available in API version 1.0. ## Raw Schema:request-body.json { "required": ["id", "name"], "type": "object", "properties": { "name": { "type": "string", "description": "Publisher name", "minLength": 1, "maxLength": 100 } } } --- id: ChatStream version: 1.0.0 name: Chat Stream summary: Real-time chat stream for BookWorm badges: - content: WebSocket textColor: orange backgroundColor: orange icon: WifiIcon - content: Chat textColor: yellow backgroundColor: yellow icon: BookOpenIcon owners: - nhanxnguyen deprecated: date: 2025-11-11T00:00:00.000Z message: > This query has been deprecated and removed from the API. It is no longer available for use. Use endpoints in `/ag-ui` instead. --- ## Overview The Chat Stream service provides a real-time WebSocket connection for users to interact with the BookWorm platform. It allows users to send and receive messages in real-time, enabling dynamic conversations and interactions with the system. ## Implementation Details The Chat Stream service is built on a microservices architecture, utilizing various agents to process user messages and generate responses. ```mermaid flowchart TD HandoffStart["HandoffStart (Start)"] Router["Router"] Language["Language"] Summarize["Summarize"] Sentiment["Sentiment"] Book["Book"] Rating["Rating"] MCP["MCP Server"] HandoffEnd["HandoffEnd"] HandoffStart --> Router Router --> Language Router --> Summarize Router --> Sentiment Router --> Book Router --> HandoffEnd Language --> Book Language --> HandoffEnd Summarize --> Book Summarize --> HandoffEnd Sentiment --> Book Sentiment --> Router Sentiment --> HandoffEnd Book --MCP--> MCP Book --A2A--> Rating Book --> Router Book --> HandoffEnd ``` ## Architecture ## WebSocket `wss://api.bookworm.com/api/v1/chats/stream` The Chat Stream service uses WebSocket for real-time communication. Users can connect to the WebSocket endpoint to send and receive messages. ### Example Usage ```js title="chat-stream.js" const connection = new signalR.HubConnectionBuilder() .withUrl("wss://api.bookworm.com/api/v1/chats/stream", { accessTokenFactory: () return "YOUR_ACCESS_TOKEN"; }) .withAutomaticReconnect() .build(); await connection.start(); connection.on("ReceiveMessage", (message) => { console.log("Received message:", message); }); connection.onclose(() => { console.log("Connection closed"); }); connection.onreconnected(() => { console.log("Reconnected to the chat stream"); }); ``` --- id: GetBasketGrpc version: 1.0.0 name: Get Basket (gRPC) summary: Get a basket by user via gRPC badges: - content: GET textColor: blue backgroundColor: blue icon: MagnifyingGlassIcon - content: gRPC textColor: purple backgroundColor: purple icon: ServerIcon - content: Basket textColor: yellow backgroundColor: yellow icon: ShoppingCartIcon owners: - nhanxnguyen --- ## Overview This gRPC endpoint follows Domain-Driven Design principles to retrieve a specific Basket aggregate root from the Orders bounded context by its user identifier. The operation is implemented as a query that doesn't modify state, adhering to CQRS patterns. The query handler maps the domain entity to a BasketDto response through an auto-mapper profile, ensuring that domain implementation details remain encapsulated. The endpoint respects the aggregate boundaries and only exposes data appropriate for the presentation layer. ## Architecture ## Usage ### Call GetBasket using grpcurl You can use [grpcurl](https://github.com/fullstorydev/grpcurl) to call the `GetBasket` method of the `BasketGrpcService` defined in your `basket.proto`: ```bash grpcurl -plaintext \ -d '{}' \ -H "Authorization: Bearer " \ localhost:5001 \ BasketApi.BasketGrpcService/GetBasket ``` - This endpoint does not require any request fields (uses `google.protobuf.Empty`). - Adjust the host/port (`localhost:5001`) as needed for your environment. - The response will be a `BasketResponse` message as defined in your proto: ```json { "id": "...", "items": [ { "id": "...", "quantity": 2 } // ... more items ... ] } ``` - The `Authorization` header is used to pass the JWT token for user authentication. Replace `` with a valid JWT token. --- id: GetBasketQuery version: 1.0.0 name: Get Basket summary: Get a basket by user badges: - content: GET textColor: blue backgroundColor: blue icon: MagnifyingGlassIcon - content: "Basket" textColor: yellow backgroundColor: yellow icon: ShoppingCartIcon owners: - nhanxnguyen --- ## Overview This endpoint retrieves a user's shopping basket based on their identity. In our domain model, a Basket is an aggregate root that contains a collection of BasketItems, each representing a book the user intends to purchase. ## Implementation Details The Get Basket operation is implemented using the CQRS pattern with a dedicated query handler and post-processor: ```mermaid sequenceDiagram participant Client participant GetBasketEndpoint participant GetBasketQuery participant ClaimsPrincipal participant BasketRepository participant Redis participant PostGetBasketHandler participant BookService participant CatalogService Client->>GetBasketEndpoint: GET /api/v1/baskets Note over Client,GetBasketEndpoint: Authorization: Bearer {jwt-token} GetBasketEndpoint->>GetBasketQuery: Send(new GetBasketQuery()) GetBasketQuery->>ClaimsPrincipal: GetClaimValue(KeycloakClaimTypes.Subject) alt User not authenticated ClaimsPrincipal-->>GetBasketQuery: null or empty userId GetBasketQuery-->>GetBasketEndpoint: Throw UnauthorizedAccessException GetBasketEndpoint-->>Client: 401 Unauthorized else User authenticated ClaimsPrincipal-->>GetBasketQuery: userId GetBasketQuery->>BasketRepository: GetBasketAsync(userId) BasketRepository->>Redis: Get basket data alt Basket not found Redis-->>BasketRepository: null BasketRepository-->>GetBasketQuery: null GetBasketQuery-->>GetBasketEndpoint: Throw NotFoundException GetBasketEndpoint-->>Client: 404 Not Found else Basket exists Redis-->>BasketRepository: Basket data BasketRepository-->>GetBasketQuery: CustomerBasket GetBasketQuery->>GetBasketQuery: ToCustomerBasketDto() GetBasketQuery-->>PostGetBasketHandler: CustomerBasketDto loop For each basket item PostGetBasketHandler->>BookService: GetBookByIdAsync(bookId) BookService->>CatalogService: gRPC call to get book details CatalogService-->>BookService: BookResponse BookService-->>PostGetBasketHandler: BookResponse PostGetBasketHandler->>PostGetBasketHandler: Enrich item with book details end PostGetBasketHandler-->>GetBasketEndpoint: Enriched CustomerBasketDto GetBasketEndpoint-->>Client: 200 OK with basket data end end ``` ### Key Components 1. **GetBasketQuery**: Implements `IQuery` to retrieve a user's basket 2. **GetBasketHandler**: Processes the query using repository pattern 3. **PostGetBasketHandler**: Post-processor that enriches basket items with book details from the Catalog service 4. **BookService**: gRPC client for the Catalog service 5. **CustomerBasketDto**: Data transfer object representing the basket and its items ### Technical Implementation The query execution follows these steps: 1. **Authentication**: Extracts the user ID from the JWT token claims 2. **Basket Retrieval**: Fetches the basket from Redis using the user ID as the key 3. **DTO Conversion**: Converts the domain entity to a DTO 4. **Data Enrichment**: Post-processes the DTO to add book details from the Catalog service 5. **Response**: Returns the enriched basket DTO to the client The implementation includes several notable features: - **Cross-Service Data Enrichment**: Basket items are enriched with book details from the Catalog service - **gRPC Communication**: Uses gRPC for efficient inter-service communication - **Post-Processing**: Uses Mediator's post-processing pipeline for separation of concerns - **Error Handling**: Specific exceptions for different error scenarios ### Domain Context Within our bounded context, the basket represents the current selection of items a user has chosen but not yet purchased. The basket is identified by a unique user identifier and maintains the state of the user's shopping session. ### Business Rules - Each user can have only one active basket - Basket items contain references to catalog items (books) with quantity - Prices are stored in the basket to maintain price consistency during the shopping session - Anonymous users' baskets are tracked via temporary identifiers - The basket is automatically populated with current book information from the catalog service ### Use Cases - Initial page load for returning users - Checkout process initiation - Basket summary display - Price verification before checkout ### Integration Points This endpoint is consumed by the web UI and integrates with the Catalog service to fetch current book information. ## Architecture ## GET `(/api/v1/baskets)` ### Request Body No request body is required. The basket ID is automatically determined from the authenticated user's identity. ### Example Usage ```bash curl -X GET "https:///api.bookworm.com/api/v1/baskets" \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ ``` ### Responses #### 200 OK Returns the user's basket details, including items and their prices. The response includes the basket ID and a list of items with their quantities and prices. #### Example Response: ```json title="200 OK" { "id": "0195e531-cc9d-7925-ba84-b9588bb3653d", "items": [ { "id": "0195e531-cc9d-71f1-ad7f-57cfc0712f1b", "quantity": 2, "name": "The Great Gatsby", "price": 29.99, "priceSale": 24.99 } ] } ``` #### 401 Unauthorized The user is not authenticated. The request must include a valid authentication token. #### 404 Not Found The basket for the authenticated user was not found. ### Error Handling The endpoint handles the following error scenarios: - Missing or invalid authentication token - Basket not found for the user - Book information not found in the catalog service - Invalid request parameters ### Security - Requires authentication via Bearer token - User can only access their own basket - All requests must be made over HTTPS ### Rate Limiting - Standard rate limits apply - Maximum 100 requests per minute per user ### Caching - Response is not cached - Each request fetches fresh data from the catalog service --- id: GetBookGrpc version: 1.0.0 name: Get Book (gRPC) summary: Get a book by ID via gRPC badges: - content: GET textColor: blue backgroundColor: blue icon: MagnifyingGlassIcon - content: gRPC textColor: purple backgroundColor: purple icon: ServerIcon - content: Book textColor: yellow backgroundColor: yellow icon: BookOpenIcon owners: - nhanxnguyen deprecated: date: 2025-06-20T00:00:00.000Z message: > This endpoint (`GetBookGrpc`) is currently not utilized by any service in BookWorm. For retrieving books, consider using the `ListBookGrpc` endpoint, which is designed for listing and querying books efficiently. --- ## Overview This gRPC endpoint follows Domain-Driven Design principles to retrieve a specific Book aggregate root from the Catalog bounded context by its unique identifier. The operation is implemented as a query that doesn't modify state, adhering to CQRS patterns. The query handler maps the domain entity to a BookDto response through an auto-mapper profile, ensuring that domain implementation details remain encapsulated. The endpoint respects the aggregate boundaries and only exposes data appropriate for the presentation layer. ### Key Features - **High Performance**: gRPC binary protocol for efficient data transfer - **Strong Typing**: Protocol buffer definitions ensure type safety - **Streaming Support**: Capable of server streaming for bulk operations - **Cross-Platform**: Works with any gRPC-compatible client ## Architecture ## Usage ### Call GetBook using grpcurl You can use [grpcurl](https://github.com/fullstorydev/grpcurl) to call the `GetBook` method of the `BookGrpcService` defined in your `book.proto`: ```bash grpcurl -plaintext \ -d '{"bookId": ""}' \ localhost:5001 \ CatalogApi.BookGrpcService/GetBook ``` - Replace `` with the actual book ID (string). - Adjust the host/port (`localhost:5001`) as needed for your environment. - The response will be a `BookResponse` message as defined in your proto: ```json title="200 OK" { "id": "", "name": "Atomic Habits", "price": { "units": 12, "nanos": 0 }, "priceSale": { "units": 10, "nanos": 0 }, "status": "InStock" } ``` --- id: GetBookQuery version: 1.0.0 name: Get Book summary: Get a book by ID badges: - content: GET textColor: blue backgroundColor: blue icon: MagnifyingGlassIcon - content: "Book" textColor: yellow backgroundColor: yellow icon: BookOpenIcon owners: - nhanxnguyen --- ## Overview This endpoint follows Domain-Driven Design principles to retrieve a specific Book aggregate root from the Catalog bounded context by its unique identifier. The operation is implemented as a query that doesn't modify state, adhering to CQRS patterns. The query handler maps the domain entity to a BookDto response through an auto-mapper profile, ensuring that domain implementation details remain encapsulated. The endpoint respects the aggregate boundaries and only exposes data appropriate for the presentation layer. If the requested book doesn't exist, the service returns a Not Found response rather than a null object, following the Fail Fast principle. This endpoint is also integrated with our distributed caching strategy to optimize read-heavy operations and reduce database load. ## Architecture ## Behavior The endpoint implements the following behavior: 1. Validates the provided GUID format for the book ID 2. Queries the book repository to fetch the book entity 3. If the book is not found, returns a 404 Not Found response 4. Maps the domain entity to a DTO using the configured auto-mapper 5. Returns the book data with a 200 OK status code ## Implementation Details The endpoint is implemented using: - CQRS pattern with a dedicated query handler - Domain-Driven Design principles - Repository pattern for data access - Auto-mapping for entity-to-DTO conversion - Proper error handling with domain-specific exceptions ## GET `(/api/v1/books/{id})` ### Parameters - **id** (path) (required) - The unique identifier (GUID) of the book to retrieve ### Request Body No request body is required for this endpoint. ### Example Usage ```bash curl -X GET "https://api.bookworm.com/api/v1/books/0195e692-600b-715e-a17b-b3a8faf4ed07" ``` ### Responses #### 200 OK #### 404 Not Found Returns when the requested book ID does not exist in the system. ### Success Response ```json title="200 OK" { "id": "0195e692-600b-715e-a17b-b3a8faf4ed07", "name": "The Great Gatsby", "description": "A classic novel by F. Scott Fitzgerald.", "imageUrl": "URL_ADDRESS.com/great-gatsby.jpg", "price": 10.99, "discountPrice": 9.99, "status": "InStock", "authors": [ { "id": "0195e692-600b-7290-a47f-982b9d7f15f3", "name": "F. Scott Fitzgerald" }, { "id": "0195e692-600b-7d32-99b0-ae7abd82a863", "name": "John Smith" } ], "category": { "id": "0195e692-600b-7424-a426-dac7227720fe", "name": "Fiction" }, "publisher": { "id": "0195e692-600b-78b3-9b7f-3e342d9f2815", "name": "Scribner" }, "averageRating": 4.5, "totalReviews": 100 } ``` ### Error Response ```json title="404 Not Found" { "type": "https://tools.ietf.org/html/rfc7231#section-6.5.4", "title": "Not Found", "status": 404, "detail": "Book with id 123e4567-e89b-12d3-a456-426614174000 not found." } ``` --- id: GetBuyerQuery version: 1.0.0 name: Get Buyer summary: Get current buyer badges: - content: GET textColor: green backgroundColor: green icon: MagnifyingGlassIcon - content: "Buyer" textColor: yellow backgroundColor: yellow icon: UserGroupIcon - content: "Authenticated" textColor: blue backgroundColor: blue icon: UserIcon - content: "Feature Flag" textColor: purple backgroundColor: purple icon: FlagIcon owners: - nhanxnguyen --- ## Overview This endpoint retrieves the current buyer's information from the Ordering bounded context. In our domain model, a Buyer represents a customer entity with purchasing capabilities and order history. ## Implementation Details The Get Buyer operation is implemented using the CQRS pattern with a dedicated query handler: ```mermaid sequenceDiagram participant Client participant GetBuyerEndpoint participant FeatureManager participant GetBuyerQuery participant BuyerRepository Client->>GetBuyerEndpoint: GET /api/v1/buyers/me GetBuyerEndpoint->>FeatureManager: IsEnabledAsync(EnableAddress) FeatureManager-->>GetBuyerEndpoint: Feature flag status GetBuyerEndpoint->>GetBuyerQuery: Send(query) GetBuyerQuery->>GetBuyerQuery: Check user roles alt User has Admin role GetBuyerQuery->>GetBuyerQuery: Use provided ID else Regular user GetBuyerQuery->>GetBuyerQuery: Extract user ID from claims end GetBuyerQuery->>BuyerRepository: GetByIdAsync(buyerId) BuyerRepository-->>GetBuyerQuery: Buyer entity GetBuyerQuery->>GetBuyerQuery: Map to BuyerDto GetBuyerQuery-->>GetBuyerEndpoint: BuyerDto alt Address feature enabled GetBuyerEndpoint-->>Client: 200 OK with full buyer data else Address feature disabled GetBuyerEndpoint->>GetBuyerEndpoint: Remove address data GetBuyerEndpoint-->>Client: 200 OK with partial buyer data end ``` ### Key Components 1. **GetBuyerQuery**: Implements `IQuery` to retrieve a buyer by ID 2. **Role-Based Access Control**: Admins can retrieve any buyer, regular users can only retrieve their own data 3. **Feature Flag**: The `EnableAddress` feature flag controls whether address information is included in the response 4. **BuyerDto**: Lightweight projection of the Buyer aggregate for read operations ## Domain Significance Within the Ordering domain, the Buyer aggregate is responsible for: - Maintaining buyer identity and preferences - Tracking order history and purchasing patterns - Managing payment methods and shipping addresses ## Technical Implementation This endpoint follows the CQRS pattern using a dedicated query handler that: 1. Authenticates the current user and checks their roles 2. For admin users, uses the provided buyer ID; for regular users, extracts the ID from their claims 3. Retrieves the corresponding Buyer aggregate from the repository 4. Maps the domain entity to a DTO for the response 5. Conditionally includes or excludes address information based on feature flag ## Administrative Access Administrators can access specific buyer information by providing an optional query parameter: - `?id={buyerId}`: Retrieve a specific buyer by their unique identifier (admin only) When accessed by regular users, the endpoint always returns the buyer information associated with the authenticated user's identity, regardless of any ID parameter provided. ## Architecture ## GET `(/api/v1/buyers/me)` ### Query Parameters - **id** (query) (optional): The buyer ID to retrieve (only used when the requester has admin role) ### Example Usage #### Regular User ```bash curl -X GET "https://api.bookworm.com/api/v1/buyers/me" \ -H "Authorization: Bearer " ``` #### Admin User ```bash curl -X GET "https://api.bookworm.com/api/v1/buyers/me?id={buyerId}" \ -H "Authorization: Bearer " ``` ### Responses #### 200 OK Returns the buyer information. #### Example Response ```json title="200 OK" { "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "name": "John Doe", "address": "123 Main St, New York, NY" } ``` #### 401 Unauthorized Returned when the request lacks valid authentication credentials. #### 403 Forbidden Returned when a non-admin user attempts to access another user's buyer information. #### 404 Not Found Returned when the requested buyer does not exist. --- id: GetChatQuery version: 1.0.0 name: Get Chat summary: Retrieve chat messages for a specific chat session badges: - content: GET textColor: blue backgroundColor: blue icon: MagnifyingGlassIcon - content: "Chat" textColor: yellow backgroundColor: yellow icon: BookOpenIcon owners: - nhanxnguyen deprecated: date: 2025-10-05 message: | This query has been deprecated and removed from the API. It is no longer available for use. --- ## Overview The Get Chat query allows users to retrieve chat messages for a specific chat session. This is useful for displaying the conversation history in the user interface or for processing chat data. ## Architecture ## Behavior The Get Chat query follows these steps: 1. Validates the provided chat ID format. 2. Queries the chat repository to fetch the chat session and its messages. 3. If the chat session is not found, returns a 404 Not Found response. 4. Maps the chat session and messages to a DTO using the configured auto-mapper. 5. Returns the chat data with a 200 OK status code. # GET `/api/v1/chats/{id}` ### Parameters - **id** (path) (required) - The unique identifier (GUID) of the chat session to retrieve. ### Request Body This query does not require a request body. ### Response #### 200 OK #### 404 Not Found --- id: GetMcpComponentsQuery version: 1.0.0 name: Get MCP Components summary: Retrieve available MCP tools and components via JSON-RPC over HTTP badges: - content: POST textColor: green backgroundColor: green icon: MagnifyingGlassIcon - content: "MCP" textColor: purple backgroundColor: purple icon: WrenchScrewdriverIcon - content: "JSON-RPC" textColor: blue backgroundColor: blue icon: CodeBracketIcon - content: "Server-Sent Events" textColor: orange backgroundColor: orange icon: RadioIcon owners: - nhanxnguyen --- ## Overview The `Get MCP Components` endpoint provides access to Model Context Protocol (MCP) tools and components within the Integration bounded context. This operation follows the MCP streamable protocol (`mcp-streamable-1.0`) and returns responses as Server-Sent Events for real-time streaming capabilities. The endpoint enables interaction with various MCP tools for managing and querying BookWorm catalog data, including book search operations and catalog management functions. ## Implementation Details The Get MCP Components operation is implemented using JSON-RPC over HTTP with streaming response support: ```mermaid sequenceDiagram participant Client participant McpToolsEndpoint participant JsonRpcProcessor participant McpToolsHandler participant CatalogService participant EventStream Client->>McpToolsEndpoint: POST /mcp Note over Client,McpToolsEndpoint: Content-Type: application/json
Accept: text/event-stream McpToolsEndpoint->>JsonRpcProcessor: Process JSON-RPC request JsonRpcProcessor->>JsonRpcProcessor: Validate request format JsonRpcProcessor->>JsonRpcProcessor: Extract method & params alt Invalid JSON-RPC format JsonRpcProcessor-->>McpToolsEndpoint: 400 Bad Request McpToolsEndpoint-->>Client: Error response else Valid request JsonRpcProcessor->>McpToolsHandler: Execute MCP method alt Tool invocation McpToolsHandler->>CatalogService: Query catalog data CatalogService-->>McpToolsHandler: Catalog response McpToolsHandler->>McpToolsHandler: Process tool result else List tools McpToolsHandler->>McpToolsHandler: Get available tools end McpToolsHandler->>EventStream: Start SSE stream EventStream-->>Client: event: message
data: {"jsonrpc":"2.0","id":1,"result":{...}} loop For each result chunk McpToolsHandler->>EventStream: Send data chunk EventStream-->>Client: event: message
data: {chunk} end McpToolsHandler->>EventStream: Complete stream EventStream-->>Client: event: complete
data: {} end ``` ### Key Components 1. **JSON-RPC Processor**: Handles JSON-RPC 1.0/2.0 protocol validation and routing 2. **MCP Tools Handler**: Processes MCP method invocations and tool executions 3. **Event Stream Manager**: Manages Server-Sent Events streaming using `mcp-streamable-1.0` protocol 4. **Catalog Service Integration**: Provides access to BookWorm catalog data 5. **Tool Registry**: Maintains available MCP tools and their capabilities ### Technical Implementation The operation execution follows these steps: 1. **Request Validation**: Validates JSON-RPC format and required fields (`method`, `params`, `jsonrpc`, `id`) 2. **Method Routing**: Routes the request to appropriate MCP tool or system method 3. **Tool Execution**: Executes the requested MCP tool with provided arguments 4. **Stream Initialization**: Starts Server-Sent Events stream with `text/event-stream` content type 5. **Response Streaming**: Streams results in real-time using MCP streamable protocol 6. **Stream Completion**: Sends completion event to close the stream ## POST `(/mcp)` ### Request Body The endpoint accepts JSON-RPC formatted requests with the following structure: | Field | Type | Required | Description | | ------- | ------- | -------- | ----------------------------------------------- | | method | string | Yes | Name of the MCP method to invoke | | params | object | Yes | Method parameters containing tool-specific args | | jsonrpc | string | Yes | JSON-RPC version ("1.0" or "2.0") | | id | integer | Yes | Unique request identifier | #### Parameters Object | Field | Type | Required | Description | | --------- | ------ | -------- | -------------------------------- | | name | string | No | Target component or tool name | | arguments | object | No | Key-value arguments for the tool | ### Examples Usage #### List Available Tools ```bash title="Example Request" curl -X POST "https://mcp.bookworm.com/mcp" \ -H "Content-Type: application/json" \ -H "Accept: text/event-stream" \ -d '{ "method": "tools/list", "params": {}, "jsonrpc": "2.0", "id": 1 }' ``` #### Search Books Tool ```bash title="Example Request" curl -X POST "https://mcp.bookworm.com/mcp" \ -H "Content-Type: application/json" \ -H "Accept: text/event-stream" \ -d '{ "method": "tools/call", "params": { "name": "search_books", "arguments": { "query": "fantasy novels", "limit": 10 } }, "jsonrpc": "2.0", "id": 2 }' ``` #### Get Catalog Information ```bash title="Example Request" curl -X POST "https://mcp.bookworm.com/mcp" \ -H "Content-Type: application/json" \ -H "Accept: text/event-stream" \ -d '{ "method": "tools/call", "params": { "name": "get_catalog_info", "arguments": { "include_stats": true } }, "jsonrpc": "2.0", "id": 3 }' ``` ### Responses #### 200 OK (Server-Sent Events) Successful streaming response with MCP data: ``` Content-Type: text/event-stream event: message data: {"jsonrpc":"2.0","id":1,"result":{"status":"ok","tools":[{"name":"search_books","description":"Search for books in the catalog"}]}} event: message data: {"jsonrpc":"2.0","id":1,"result":{"data":"Additional result chunk"}} event: complete data: {} ``` #### 400 Bad Request When the JSON-RPC request is malformed: ```json { "error": "Bad Request: The POST body did not contain a valid JSON-RPC message.", "code": 400, "jsonrpc": "2.0", "id": 1 } ``` #### 406 Not Acceptable When the client doesn't accept Server-Sent Events: ```json { "error": "Not Acceptable: Client must accept both application/json and text/event-stream", "code": 406, "jsonrpc": "2.0", "id": 1 } ``` ## MCP Protocol Details The endpoint implements the **MCP streamable protocol version 1.0** (`mcp-streamable-1.0`) with the following characteristics: ### Event Types - **`message`**: Contains JSON-RPC response data - **`error`**: Indicates an error occurred during processing - **`complete`**: Signals the end of the stream ### Available Tools The MCP Tools service provides the following tools for BookWorm integration: | Tool Name | Description | Arguments | | ------------------ | ------------------------------------------ | ----------------------------------------- | | `search_books` | Search for books in the BookWorm catalog | `query`, `limit`, `filters` | | `get_book_details` | Retrieve detailed information about a book | `book_id` | | `list_categories` | Get available book categories | `include_counts` | | `get_catalog_info` | Get catalog statistics and information | `include_stats`, `include_recent_updates` | | `list_authors` | Retrieve authors from the catalog | `limit`, `search` | ### Error Handling Errors are streamed as part of the Server-Sent Events response: ``` event: error data: {"jsonrpc":"2.0","id":1,"error":{"code":-32602,"message":"Invalid params","data":"Tool 'unknown_tool' not found"}} event: complete data: {} ``` ## Security Considerations - The endpoint validates JSON-RPC format to prevent injection attacks - Tool execution is sandboxed within the MCP context - Rate limiting may be applied to prevent abuse - Authentication may be required depending on the tool being invoked ## Integration Points This query integrates with: - **Catalog Service**: For book search and catalog operations - **Event Streaming Infrastructure**: For real-time response delivery - **MCP Tool Registry**: For tool discovery and execution - **BookWorm API Gateway**: For routing and load balancing --- id: GetOrderQuery version: 1.0.0 name: Get Order summary: Get order detail by order id badges: - content: GET textColor: green backgroundColor: green icon: MagnifyingGlassIcon - content: "Order" textColor: yellow backgroundColor: yellow icon: ListBulletIcon - content: "Authenticated" textColor: blue backgroundColor: blue icon: UserIcon - content: "Role-Based" textColor: purple backgroundColor: purple icon: UserGroupIcon owners: - nhanxnguyen --- ## Overview The `Get Order` endpoint provides access to a complete Order aggregate within the Ordering bounded context. This read operation aligns with the Query side of our CQRS pattern implementation, retrieving a fully hydrated Order aggregate root along with its associated value objects and entities. ## Implementation Details The Get Order operation is implemented using the CQRS pattern with a dedicated query handler and post-processor: ```mermaid sequenceDiagram participant Client participant GetOrderEndpoint participant GetOrderQuery participant OrderRepository participant OrderFilterSpec participant PostGetOrderHandler participant BookService Client->>GetOrderEndpoint: GET /api/v1/orders/{id} GetOrderEndpoint->>GetOrderQuery: Send(new GetOrderQuery(id)) GetOrderQuery->>GetOrderQuery: Check user roles alt User has Admin role GetOrderQuery->>OrderRepository: GetByIdAsync(id) else Regular user GetOrderQuery->>GetOrderQuery: Extract user ID from claims GetOrderQuery->>OrderFilterSpec: Create specification with order ID and buyer ID GetOrderQuery->>OrderRepository: FirstOrDefaultAsync(OrderFilterSpec) end OrderRepository-->>GetOrderQuery: Order entity alt Order not found GetOrderQuery-->>GetOrderEndpoint: Throw NotFoundException else Order found GetOrderQuery->>GetOrderQuery: Map to OrderDetailDto GetOrderQuery-->>PostGetOrderHandler: OrderDetailDto PostGetOrderHandler->>BookService: Get book details for each order item BookService-->>PostGetOrderHandler: Book details PostGetOrderHandler->>PostGetOrderHandler: Enrich order items with book names PostGetOrderHandler-->>GetOrderEndpoint: Enriched OrderDetailDto GetOrderEndpoint-->>Client: 200 OK with order details end ``` ### Key Components 1. **GetOrderQuery**: Implements `IQuery` to retrieve an order by ID 2. **Role-Based Access Control**: Admins can retrieve any order, regular users can only retrieve their own orders 3. **OrderFilterSpec**: Specification pattern implementation for filtering orders by ID and buyer ID 4. **PostGetOrderHandler**: Post-processor that enriches order items with book details from the Catalog service 5. **OrderDetailDto**: Detailed projection of the Order aggregate including order items ## Domain Model Within our domain model, an Order represents a crucial business document that encapsulates the entire purchasing transaction. The Order aggregate maintains several important invariants and contains: - Order header information with customer identity and creation date - Collection of OrderItem entities representing the purchased products with quantities and prices - Order status reflecting its position in the fulfillment lifecycle (New, Cancelled, Completed) - Total price calculated as the sum of all item prices multiplied by quantities ## Technical Implementation This endpoint follows the CQRS pattern using a dedicated query handler that: 1. Authenticates the current user and checks their roles 2. For admin users, retrieves the order directly by ID 3. For regular users, applies a specification that ensures they can only access their own orders 4. Maps the domain entity to a DTO for the response 5. Enriches the response with additional data from the Catalog service ## Authorization Context Access to orders is governed by domain policies that determine which orders a particular user can view: - **Regular Users**: Can only view their own orders (enforced by filtering on buyer ID) - **Administrators**: Can view any order in the system This endpoint serves various domain use cases including order tracking, fulfillment processing, and customer service inquiries. It enforces access control to ensure that only authorized contexts can retrieve order information. As per DDD principles, the internal domain model is not exposed directly - instead, a DTO representation is returned that contains all necessary information while preserving the integrity of our domain boundaries. ## Architecture ## GET `(/api/v1/orders/{id})` ### Path Parameters - **id** (path) (required): The unique identifier of the order to retrieve ### Example Usage #### Regular User ```bash curl -X GET "https://api.bookworm.com/api/v1/orders/{orderId}" \ -H "Authorization: Bearer " ``` #### Admin User ```bash curl -X GET "https://api.bookworm.com/api/v1/orders/{orderId}" \ -H "Authorization: Bearer " ``` ### Responses #### 200 OK Returns the complete order details including items. #### Example Response ```json title="200 OK" { "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "date": "2025-03-30T15:30:45Z", "total": 59.97, "status": "New", "items": [ { "id": "8a1b6a9c-7d3e-4f5a-9b2c-1d3e5f7a9b2c", "name": "The Great Gatsby", "quantity": 1, "price": 14.99 }, { "id": "7c2f9b8a-6d5e-4c3b-2a1d-0f9e8d7c6b5a", "name": "To Kill a Mockingbird", "quantity": 3, "price": 14.99 } ] } ``` #### 401 Unauthorized Returned when the request lacks valid authentication credentials. #### 403 Forbidden Returned when a non-admin user attempts to access another user's order. #### 404 Not Found Returned when the requested order does not exist or is not accessible by the current user. --- id: GetOrderStateMachineQuery version: 1.0.0 name: Get Order State Machine summary: Get order state machine badges: - content: GET textColor: blue backgroundColor: blue icon: MagnifyingGlassIcon - content: "OrderState" textColor: yellow backgroundColor: yellow icon: CubeTransparentIcon owners: - nhanxnguyen deprecated: date: 2025-07-28 message: | This query has been deprecated and removed from the API. It is no longer available for use. --- ## Overview This API endpoint provides access to the Order State Machine visualization, which is a core component of the Finance service following Domain-Driven Design principles. The endpoint returns a DOT format representation of the state machine that can be rendered into a visual graph. The Order State Machine represents the lifecycle of an order as it moves through various states (Placed, Completed, Cancelled, Failed) in the Finance bounded context. It is implemented using MassTransit's state machine framework. ## Implementation Details The endpoint is implemented using: - **Mediator**: For handling the query via the CQRS pattern - **MassTransit Visualizer**: For generating the state machine graph - **Minimal API**: For exposing the HTTP endpoint ### Technical Flow 1. The endpoint receives a GET request at `/api/v1/order-state-machine` 2. The request is routed to the `GetOrderStateEndpoint` handler 3. The handler sends a `GetOrderStateQuery` via Mediator 4. The `GetOrderStateHandler` creates a new instance of `OrderStateMachine` 5. The handler uses `StateMachineGraphvizGenerator` to generate a DOT file representation 6. The DOT format string is returned to the client ## Security This endpoint is secured with the Admin policy, requiring administrative privileges to access. This is enforced through the `.RequireAuthorization(Authorization.Policies.Admin)` directive. ## Use Cases This endpoint is particularly useful for: - **Debugging**: Visualizing the current state machine configuration - **Documentation**: Generating up-to-date diagrams of the order workflow - **Development**: Understanding the possible state transitions - **Monitoring**: Verifying the correct configuration of the state machine ## Architecture ## GET `(/api/v1/order-state-machine)` ### Example Usage ```bash curl -X GET https://api.bookworm.com/api/v1/order-state-machine \ -H "Authorization: Bearer " ``` ### Responses #### 200 OK The endpoint returns a plain text response containing the DOT format representation of the state machine graph. This can be used with Graphviz or similar tools to render a visual representation of the state machine. ### Example Response ```dot digraph { graph [layout=dot rankdir=LR] node [shape=circle] Initial [shape=diamond] Placed [shape=circle] Completed [shape=circle] Cancelled [shape=circle] Failed [shape=circle] Initial -> Placed [label="OrderPlaced"] Placed -> Failed [label="BasketDeletedFailed"] Placed -> Completed [label="OrderCompleted"] Placed -> Cancelled [label="OrderCancelled"] } ``` --- id: GetOrderSummaryOrder version: 1.0.0 name: Get Order Summary summary: Get order summary from event store badges: - content: GET textColor: green backgroundColor: green icon: MagnifyingGlassIcon - content: "Order" textColor: yellow backgroundColor: yellow icon: ListBulletIcon - content: "Event Sourcing" textColor: blue backgroundColor: blue icon: CubeTransparentIcon - content: "Projection" textColor: purple backgroundColor: purple icon: ClipboardDocumentListIcon owners: - nhanxnguyen --- ## Overview This endpoint retrieves the current state of an Order aggregate by replaying events from the event store, following our event sourcing pattern within the Ordering bounded context. ## Implementation Details The Order Summary query is implemented using event sourcing and projections: ```mermaid sequenceDiagram participant Client participant SummaryOrderEndpoint participant Marten participant OrderSummaryInfo participant EventStore Client->>SummaryOrderEndpoint: GET /api/v1/orders/{id}/summary SummaryOrderEndpoint->>Marten: querySession.Json.WriteById(id) Marten->>EventStore: Retrieve events for order ID EventStore-->>Marten: Events (DeleteBasketComplete, OrderCancelled, OrderCompleted) Marten->>OrderSummaryInfo: Apply events to build projection alt DeleteBasketCompleteCommand event OrderSummaryInfo->>OrderSummaryInfo: Set Status = New, Set TotalPrice else OrderCancelledEvent event OrderSummaryInfo->>OrderSummaryInfo: Set Status = Cancelled, Set TotalPrice else OrderCompletedEvent event OrderSummaryInfo->>OrderSummaryInfo: Set Status = Completed, Set TotalPrice end Marten-->>SummaryOrderEndpoint: OrderSummaryInfo projection SummaryOrderEndpoint-->>Client: 200 OK with order summary ``` ### Key Components 1. **OrderSummaryInfo**: A lightweight projection model that captures essential order information 2. **MultiStreamProjection**: Projects events from multiple streams into a single read model 3. **Event Handlers**: Apply specific events to update the projection state 4. **Marten**: Document database with event sourcing capabilities that manages the projection ## Technical Implementation The Order Summary endpoint demonstrates several advanced patterns: 1. **Event Sourcing**: The system stores all domain events and reconstructs state by replaying them 2. **CQRS (Command Query Responsibility Segregation)**: Separate read and write models 3. **Projections**: Transform event streams into optimized read models 4. **Multi-Stream Aggregation**: Combine events from different streams to build a cohesive view This endpoint demonstrates the CQRS principle by providing a dedicated read model optimized for client consumption. The Order aggregate rebuilds its state by sequentially applying all recorded domain events associated with the specified order ID. ## Architecture ## GET `(/api/v1/orders/{id}/summary)` ### Path Parameters - **id** (path) (required): The unique identifier of the order to retrieve summary for ### Example Usage ```bash curl -X GET "https://api.bookworm.com/api/v1/orders/{orderId}/summary" ``` ### Responses #### 200 OK Returns a lightweight summary of the order's current state. #### 404 Not Found Returned when no events exist for the specified order ID. --- id: GetPublisherQuery version: 1.0.0 name: List Publishers summary: List all publishers badges: - content: GET textColor: blue backgroundColor: blue icon: MagnifyingGlassIcon - content: "Publisher" textColor: blue backgroundColor: blue icon: BuildingOfficeIcon owners: - nhanxnguyen --- ## Overview Get all publishers in the system. This endpoint retrieves a comprehensive list of book publishers from our catalog domain. Publishers represent important aggregate roots in our domain model that have relationships with multiple book entities. This query operation implements the Repository pattern to efficiently fetch publisher data while maintaining domain boundaries. The operation follows CQRS principles by using a dedicated read model query handler that optimizes for performance when retrieving this frequently accessed reference data. The response includes essential publisher metadata while maintaining proper encapsulation of internal domain concepts. ## Architecture ## GET `(/api/v1/publishers)` ### Description This endpoint returns a list of all publishers in the system. It's designed to be fast and efficient, making it suitable for populating dropdowns, filters, and other UI elements that require publisher data. ### Query Parameters None. This endpoint does not accept any query parameters. ### Request Body No request body is required for this endpoint. ### Example Usage ```bash curl -X GET "https://api.bookworm.com/api/v1/publishers" ``` ### Responses #### 200 OK Returns a list of publishers with their details. #### Example Response ```json title="200 OK" { "publishers": [ { "id": "1", "name": "Penguin Random House" }, { "id": "2", "name": "HarperCollins" } ] } ``` --- id: ListAuthorsQuery version: 1.0.0 name: List Authors summary: Get all authors badges: - content: GET textColor: blue backgroundColor: blue icon: MagnifyingGlassIcon - content: "Author" textColor: yellow backgroundColor: yellow icon: UserIcon owners: - nhanxnguyen --- ## Overview This endpoint retrieves a comprehensive list of all authors available in the catalog domain. The implementation follows Domain-Driven Design principles, separating the concern of querying authors from the domain model through a dedicated query handler. The query accesses the repository abstraction rather than directly querying the database, maintaining proper encapsulation of the persistence layer. The returned authors are mapped to DTOs using AutoMapper, ensuring that domain entities remain protected from external exposure. This endpoint supports catalog bounded context operations that require author information, such as book browsing and filtering. It provides a paginated response to handle potentially large datasets efficiently. ## Use Cases - Displaying author lists in the UI for book browsing - Populating author selection dropdowns in forms - Supporting author-based filtering and search functionality - Integration with other services requiring author information ## Architecture ## GET `(/api/v1/authors)` ### Request Body No request body required for this endpoint. ### Example Usage ```bash curl -X GET "https://api.bookworm.com/api/v1/authors" ``` ### Responses #### 200 OK ### Example Response ```json title="200 OK" [ { "id": "a1e1b3b4-1b1b-4b1b-9b1b-1b1b1b1b1b1b", "name": "John Doe" }, { "id": "b1e1b3b4-1b1b-4b1b-9b1b-1b1b1b1b1b1b", "name": "Jane Smith" }, { "id": "c1e1b3b4-1b1b-4b1b-9b1b-1b1b1b1b1b1b", "name": "Alice Johnson" } ] ``` ### Rate Limiting This endpoint is subject to standard API rate limiting. Please refer to the API documentation for specific rate limit details. ### Error Handling The endpoint may return the following error responses: - 429 Too Many Requests: Rate limit exceeded - 500 Internal Server Error: Server-side processing error ### Performance Considerations - The response is paginated to handle large datasets efficiently - Results are cached at the application level for improved performance - Response times may vary based on the total number of authors in the system ### Security - Requires authentication - Implements proper authorization checks - Data is sanitized before transmission --- id: ListBooksGrpc version: 1.0.0 name: List Books (gRPC) summary: List all books via gRPC badges: - content: GET textColor: blue backgroundColor: blue icon: MagnifyingGlassIcon - content: gRPC textColor: purple backgroundColor: purple icon: ServerIcon - content: Book textColor: yellow backgroundColor: yellow icon: BookOpenIcon owners: - nhanxnguyen --- ## Overview This gRPC endpoint follows Domain-Driven Design principles to retrieve a list of Book aggregate roots from the Catalog bounded context. The operation is implemented as a query that doesn't modify state, adhering to CQRS patterns. The query handler maps the domain entities to a list of BookDto responses through an auto-mapper profile, ensuring that domain implementation details remain encapsulated. The endpoint respects the aggregate boundaries and only exposes data appropriate for the presentation layer. ## Architecture ## Usage ### Call ListBooks using grpcurl You can use [grpcurl](https://github.com/fullstorydev/grpcurl) to call the `GetBooks` (or `ListBooks`) method of the `BookGrpcService` defined in your `book.proto`: ```bash grpcurl -plaintext \ -d '{"bookIds": ["", ""]}' \ localhost:5001 \ CatalogApi.BookGrpcService/GetBooks ``` - Replace ``, ``, etc. with actual book IDs (strings). To list all books, you may pass an empty array or omit the field if supported by your implementation. - Adjust the host/port (`localhost:5001`) as needed for your environment. - The response will be a `BooksResponse` message as defined in your proto: ```json title="200 OK" { "books": [ { "id": "...", "name": "...", "price": { "units": 12, "nanos": 0 }, "priceSale": { "units": 10, "nanos": 0 }, "status": "InStock" } // ... more books ... ] } ``` --- id: ListBooksQuery version: 1.0.0 name: List Books summary: Get all books with advanced filtering and pagination badges: - content: GET textColor: blue backgroundColor: blue icon: MagnifyingGlassIcon - content: "Book" textColor: yellow backgroundColor: yellow icon: BookOpenIcon owners: - nhanxnguyen --- ## Overview Get all books in the system with advanced filtering, sorting, and pagination support. This endpoint retrieves a collection of book aggregates from the Catalog bounded context, applying domain filters and returning DTOs that comply with our anti-corruption layer patterns. The endpoint respects the read model separation in our CQRS implementation, using dedicated read models optimized for query performance. Results can be filtered by various domain attributes including genre, author, and publication status. Each book in the returned collection contains core domain properties while maintaining a clear separation between entity identity and value objects according to DDD principles. ## Architecture ## GET `(/api/v1/books)` ### Query Parameters | Parameter | Type | Required | Default | Description | | ------------ | ----------- | -------- | ------- | --------------------------------------------------------------------------------------------- | | pageIndex | integer | No | 1 | The page number to retrieve (1-based) | | pageSize | integer | No | 10 | Number of items per page | | orderBy | string | No | Name | Property to sort results by. Valid values: `Name`, `OriginalPrice`, `DiscountPrice`, `Status` | | isDescending | boolean | No | false | Whether to sort results in descending order | | search | string | No | null | Search term to filter books by (uses semantic search) | | minPrice | decimal | No | null | Minimum price filter | | maxPrice | decimal | No | null | Maximum price filter | | categoryId | array[Guid] | No | null | Filter by specific category IDs | | publisherId | array[Guid] | No | null | Filter by specific publisher IDs | | authorIds | array[Guid] | No | null | Filter by specific author IDs | ### Examples Usage #### Basic ```bash title="Example Request" curl -X GET "https://api.bookworm.com/api/v1/books" ``` #### With Pagination ```bash title="Example Request" curl -X GET "https://api.bookworm.com/api/v1/books?pageIndex=2&pageSize=20" ``` #### With Search and Filters ```bash title="Example Request" curl -X GET "https://api.bookworm.com/api/v1/books?search=fantasy&minPrice=10&maxPrice=50&categoryId=123e4567-e89b-12d3-a456-426614174000" ``` #### With Sorting ```bash title="Example Request" curl -X GET "https://api.bookworm.com/api/v1/books?orderBy=Price.OriginalPrice&isDescending=true" ``` ### Responses #### 400 Bad Request When the request parameters are invalid: #### 200 OK Returns a paged result containing the filtered books: #### Example Response ```json title="200 OK" { "pageIndex": 1, "pageSize": 10, "totalItems": 20, "totalPages": 2, "hasNextPage": true, "hasPreviousPage": false, "items": [ { "id": "0195e692-600b-715e-a17b-b3a8faf4ed07", "name": "The Great Gatsby", "description": "A classic novel by F. Scott Fitzgerald.", "imageUrl": "URL_ADDRESS.com/great-gatsby.jpg", "price": 10.99, "discountPrice": 9.99, "status": "InStock", "authors": [ { "id": "0195e692-600b-7290-a47f-982b9d7f15f3", "name": "F. Scott Fitzgerald" }, { "id": "0195e692-600b-7d32-99b0-ae7abd82a863", "name": "John Smith" } ], "category": { "id": "0195e692-600b-7424-a426-dac7227720fe", "name": "Fiction" }, "publisher": { "id": "0195e692-600b-78b3-9b7f-3e342d9f2815", "name": "Scribner" }, "averageRating": 4.5, "totalReviews": 100 } ] } ``` ### Notes - The endpoint uses semantic search when a search term is provided - All filters can be combined to create complex queries - Results are always paginated to ensure optimal performance - The response includes metadata about the total number of items and pages - Books are returned with their related entities (authors, category, publisher) included --- id: ListBuyersQuery version: 1.0.0 name: List Buyers summary: Get buyers in a paged format badges: - content: GET textColor: green backgroundColor: green icon: MagnifyingGlassIcon - content: "Buyer" textColor: yellow backgroundColor: yellow icon: UserGroupIcon - content: "Feature Flag" textColor: blue backgroundColor: blue icon: FlagIcon owners: - nhanxnguyen --- ## Overview This endpoint retrieves a paginated list of buyers from the Ordering bounded context. In our domain model, a Buyer represents a customer who can place orders within the system. ## Implementation Details The List Buyers operation is implemented using the CQRS pattern with a dedicated query handler: ```mermaid sequenceDiagram participant Client participant ListBuyersEndpoint participant ListBuyersValidator participant ListBuyersQuery participant BuyerRepository participant BuyerFilterSpec Client->>ListBuyersEndpoint: GET /api/v1/buyers?pageIndex=1&pageSize=10 ListBuyersEndpoint->>ListBuyersValidator: Validate query parameters ListBuyersValidator-->>ListBuyersEndpoint: Validation result ListBuyersEndpoint->>ListBuyersQuery: Send(query) ListBuyersQuery->>BuyerRepository: ListAsync(BuyerFilterSpec) BuyerRepository->>BuyerFilterSpec: Apply specification BuyerFilterSpec-->>BuyerRepository: Filtered, paginated query BuyerRepository-->>ListBuyersQuery: Buyer entities ListBuyersQuery->>BuyerRepository: CountAsync() BuyerRepository-->>ListBuyersQuery: Total count ListBuyersQuery->>ListBuyersQuery: Map to BuyerDto objects ListBuyersQuery->>ListBuyersQuery: Create PagedResult ListBuyersQuery-->>ListBuyersEndpoint: PagedResult ListBuyersEndpoint-->>Client: 200 OK with paged result ``` ### Key Components 1. **ListBuyersQuery**: Implements `IQuery>` to retrieve a paginated list of buyers 2. **ListBuyersValidator**: Validates the pagination parameters 3. **BuyerFilterSpec**: Specification pattern implementation for filtering and pagination 4. **BuyerDto**: Lightweight projection of the Buyer aggregate for read operations 5. **PagedResult**: Generic container for paginated data with metadata ## Domain Context Within the Ordering bounded context, Buyers are important aggregate roots that encapsulate order history, payment methods, and delivery preferences. The paged query approach respects the query optimization patterns in DDD by: - Implementing a read-focused projection of the Buyer aggregate - Using pagination to maintain performance with potentially large datasets - Exposing only the relevant Buyer attributes needed for listing scenarios ## Technical Implementation This query is implemented using CQRS pattern: - **Command/Query Separation**: This read-only endpoint uses a dedicated query handler - **Materialized View**: Returns a lightweight DTO projection optimized for reads - **Repository Pattern**: Abstracts the underlying data access concerns - **Specification Pattern**: Uses a dedicated specification class for filtering and pagination - **Feature Flag**: Protected by a feature flag for controlled rollout The pagination follows DDD best practices by treating page size and number as domain concepts rather than technical implementation details, allowing business rules to dictate appropriate limits. ## Architecture ## GET `(/api/v1/buyers)` ### Query Parameters - **pageIndex** (query) (optional): The page number to retrieve (default: 1) - **pageSize** (query) (optional): The number of items per page (default: 10) ### Validation Rules - **pageIndex**: Must be greater than 0 - **pageSize**: Must be greater than 0 ### Example Usage ```bash curl -X GET "https://api.bookworm.com/api/v1/buyers?pageIndex=2&pageSize=15" \ -H "Content-Type: application/json" ``` ### Responses #### 200 OK Returns a paginated list of buyers with metadata about the pagination. #### Example Response ```json title="200 OK" { "pageIndex": 2, "pageSize": 15, "totalItems": 45, "totalPages": 3, "hasNextPage": false, "hasPreviousPage": true, "items": [ { "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "name": "John Doe", "address": "123 Main St, New York, NY" }, { "id": "8a1b6a9c-7d3e-4f5a-9b2c-1d3e5f7a9b2c", "name": "Jane Smith", "address": "456 Oak Ave, San Francisco, CA" } ] } ``` #### 400 Bad Request Returned when the request validation fails. ```json title="400 Bad Request" { "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1", "title": "Bad Request", "status": 400, "errors": { "PageIndex": ["The field PageIndex must be greater than 0."], "PageSize": ["The field PageSize must be greater than 0."] } } ``` --- id: ListCategoriesQuery version: 1.0.0 name: List Categories summary: Get all categories badges: - content: GET textColor: blue backgroundColor: blue icon: MagnifyingGlassIcon - content: "Category" textColor: yellow backgroundColor: yellow icon: TagIcon owners: - nhanxnguyen --- ## Overview Get all categories in the system. This query endpoint retrieves the complete collection of categories from the Catalog bounded context without filtering. Categories represent a core domain concept within the catalog taxonomy and are used for organizing book inventory. The endpoint follows the CQRS pattern, implementing a read-only query that returns a denormalized view of category data optimized for client consumption. Categories are aggregates within the catalog domain model and this endpoint preserves their encapsulation by returning only the necessary projection data. This operation is idempotent and cacheable as it performs no state mutations. ## Architecture ## GET `/api/v1/categories` ### Description Retrieves a list of all categories in the system. The response is paginated and includes basic category information. ### Parameters None. This endpoint does not require any parameters. ### Request Body No request body is required for this endpoint. ### Example Usage ```bash curl -X GET 'https://api.bookworm.com/api/v1/categories' ``` ### Responses #### 200 OK Returns a list of categories. ### Example Response ```json title="200 OK" [ { "id": "cat_123", "name": "Fiction" }, { "id": "cat_456", "name": "Science Fiction" } ] ``` --- id: ListChatsQuery version: 1.0.0 name: List Chats summary: Retrieve a list of chat sessions badges: - content: GET textColor: blue backgroundColor: blue icon: MagnifyingGlassIcon - content: "Chat" textColor: yellow backgroundColor: yellow icon: BookOpenIcon owners: - nhanxnguyen deprecated: date: 2025-10-05 message: | This query has been deprecated and removed from the API. It is no longer available for use. --- ## Overview The List Chats query allows users to retrieve a list of chat sessions. This is useful for displaying all active or historical chats in the user interface, enabling users to select a chat session to view its messages. ## Architecture ## Behavior The List Chats query follows these steps: 1. Validates the pagination parameters 2. Queries the chat repository to fetch a paginated list of chat sessions. 3. Maps the chat sessions to a DTO using the configured auto-mapper. 4. Returns the list of chat sessions with a 200 OK status code. # GET `/api/v1/chats` ### Parameters - **Name** (query) (optional) - The name of the chat session to filter by. - **UserId** (query) (optional) - The unique identifier (GUID) of the user to filter chats by. - **IncludeMessages** (query) (optional) - A boolean flag to include messages in the response. Defaults to `false`. ### Request Body This query does not require a request body. ### Response #### 200 OK #### 400 Bad Request --- id: ListFeedbacksQuery version: 1.0.0 name: List Feedbacks summary: Get feedbacks for a book badges: - content: GET textColor: blue backgroundColor: blue icon: MagnifyingGlassIcon - content: "Feedback" textColor: yellow backgroundColor: yellow icon: StarIcon - content: "Paged" textColor: green backgroundColor: green icon: TableCellsIcon owners: - nhanxnguyen --- ## Overview The List Feedbacks endpoint is a core read operation within the Rating bounded context, responsible for fetching book feedback aggregates with pagination support. This endpoint implements a query-side operation following CQRS principles, allowing clients to access feedback data without impacting the command-side of the Rating domain. ## Implementation Details The List Feedbacks operation is implemented using the CQRS pattern with a dedicated query handler: ```mermaid sequenceDiagram participant Client participant ListFeedbacksEndpoint participant ListFeedbacksValidator participant ListFeedbacksQuery participant FeedbackFilterSpec participant FeedbackRepository participant Database Client->>ListFeedbacksEndpoint: GET /api/v1/feedbacks?BookId={bookId} ListFeedbacksEndpoint->>ListFeedbacksValidator: Validate query parameters alt Validation fails ListFeedbacksValidator-->>ListFeedbacksEndpoint: Return validation errors ListFeedbacksEndpoint-->>Client: 400 Bad Request else Validation passes ListFeedbacksEndpoint->>ListFeedbacksQuery: Send(new ListFeedbacksQuery()) ListFeedbacksQuery->>FeedbackFilterSpec: Create filter specification FeedbackFilterSpec->>FeedbackRepository: ListAsync(filterSpec) FeedbackRepository->>Database: Query with filtering, ordering, and paging Database-->>FeedbackRepository: Feedback entities FeedbackRepository-->>ListFeedbacksQuery: Feedback entities ListFeedbacksQuery->>FeedbackFilterSpec: Create count specification FeedbackFilterSpec->>FeedbackRepository: CountAsync(countSpec) FeedbackRepository->>Database: Count query with filtering Database-->>FeedbackRepository: Total count FeedbackRepository-->>ListFeedbacksQuery: Total count ListFeedbacksQuery->>ListFeedbacksQuery: Calculate pagination metadata ListFeedbacksQuery->>ListFeedbacksQuery: Map to FeedbackDto objects ListFeedbacksQuery-->>ListFeedbacksEndpoint: PagedResult ListFeedbacksEndpoint-->>Client: 200 OK with paged result end ``` ### Key Components 1. **ListFeedbacksQuery**: Implements `IQuery>` with parameters for filtering, pagination, and ordering 2. **ListFeedbacksHandler**: Processes the query using repository pattern and specification pattern 3. **FeedbackFilterSpec**: Specification that encapsulates the query logic for filtering, ordering, and paging 4. **ListFeedbacksValidator**: Validates the query parameters using FluentValidation 5. **ListFeedbacksEndpoint**: Maps the HTTP GET request to the query handler ## Domain Context In our domain model, `Feedback` represents a valuable domain concept that captures reader opinions and ratings for books in the catalog. Each feedback belongs to a specific book and contains: - Unique identifier (Id) - Book reference (BookId) - Customer information (FirstName, LastName) - Review content (Comment) - Rating score (numerical evaluation on a scale of 0-5) The List Feedbacks query is a read-only operation that: 1. Filters feedbacks by BookId (required parameter) 2. Applies pagination to limit the result set (default page size is configurable) 3. Supports ordering by feedback properties (default is by Rating) 4. Returns a consistent data structure with pagination metadata ## Query Parameters | Parameter | Type | Required | Default | Description | | ------------ | ------- | -------- | -------- | -------------------------------------------- | | BookId | Guid | Yes | - | The ID of the book to get feedback for | | PageIndex | int | No | 1 | The page number (1-based indexing) | | PageSize | int | No | 10 | Number of items per page | | OrderBy | string | No | "Rating" | Property to order results by | | IsDescending | boolean | No | false | Whether to order results in descending order | ## Technical Implementation The implementation uses several patterns and techniques: 1. **Specification Pattern**: The `FeedbackFilterSpec` encapsulates the query logic, making it reusable and testable 2. **Repository Pattern**: The `IFeedbackRepository` abstracts the data access layer 3. **CQRS**: Separates the read model (query) from the write model (command) 4. **Pagination**: Implements efficient data retrieval with `PagedResult` return type 5. **Minimal API**: Uses .NET's minimal API approach with endpoint mapping The query execution flow: 1. The endpoint receives the HTTP GET request with query parameters 2. Parameters are validated using FluentValidation 3. A filter specification is created with the BookId, ordering, and pagination parameters 4. The repository executes the query against the database using the specification 5. A count query is executed to determine the total number of matching records 6. Pagination metadata is calculated (total items, total pages) 7. Feedback entities are mapped to DTOs 8. A `PagedResult` is returned with both data and pagination metadata ## Integration Points This endpoint serves as an integration point for other bounded contexts within the BookWorm ecosystem: - The Catalog service may consume this data to display aggregated ratings - The UI components use this endpoint to display paginated feedback lists - The Recommendation engine might analyze feedback patterns to suggest books ## Example Usage ```bash curl -X GET "https://api.bookworm.com/api/v1/feedbacks?BookId={bookId}&PageIndex=1&PageSize=10&OrderBy=Rating&IsDescending=true" \ -H "Content-Type: application/json" ``` ## Example Response ```json { "items": [ { "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "firstName": "John", "lastName": "Doe", "comment": "Great book, highly recommended!", "rating": 5, "bookId": "3fa85f64-5717-4562-b3fc-2c963f66afa6" }, { "id": "4fa85f64-5717-4562-b3fc-2c963f66afa7", "firstName": "Jane", "lastName": "Smith", "comment": "Interesting read but a bit slow in the middle.", "rating": 4, "bookId": "3fa85f64-5717-4562-b3fc-2c963f66afa6" } ], "pageIndex": 1, "pageSize": 10, "totalCount": 2, "totalPages": 1, "hasNextPage": false, "hasPreviousPage": false } ``` ## Architecture ## GET `(/api/v1/feedbacks)` ### Parameters - **BookId** (query) (required): The ID of the book to get feedback for - **PageIndex** (query) (optional, default: 1): The page number (1-based indexing) - **PageSize** (query) (optional, default: 10): Number of items per page - **OrderBy** (query) (optional, default: "Rating"): Property to order results by - **IsDescending** (query) (optional, default: false): Whether to order results in descending order ### Validation Rules - PageIndex must be greater than 0 - PageSize must be greater than 0 - BookId must be a valid GUID ### Responses #### 200 OK Returns a paged result of feedback items for the specified book. ```json { "items": [ { "id": "string", "firstName": "string", "lastName": "string", "comment": "string", "rating": 0, "bookId": "string" } ], "pageIndex": 0, "pageSize": 0, "totalCount": 0, "totalPages": 0 } ``` #### 400 Bad Request Returned when validation fails for the provided parameters. ```json { "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1", "title": "Bad Request", "status": 400, "errors": { "BookId": ["The BookId field is required."], "PageIndex": ["PageIndex must be greater than 0."], "PageSize": ["PageSize must be greater than 0."] } } ``` --- id: ListOrdersQuery version: 1.0.0 name: List Orders summary: Get orders in a paged format badges: - content: GET textColor: green backgroundColor: green icon: MagnifyingGlassIcon - content: "Order" textColor: yellow backgroundColor: yellow icon: ListBulletIcon - content: "Role-Based" textColor: blue backgroundColor: blue icon: UserGroupIcon owners: - nhanxnguyen --- ## Overview The `GET /api/v1/orders` endpoint provides access to the Order aggregate roots within the Ordering bounded context. This endpoint implements a paginated collection pattern to efficiently retrieve and present Order entities while maintaining system performance under load. ## Implementation Details The List Orders operation is implemented using the CQRS pattern with a dedicated query handler: ```mermaid sequenceDiagram participant Client participant ListOrdersEndpoint participant ListOrdersValidator participant ListOrdersQuery participant OrderRepository participant OrderFilterSpec Client->>ListOrdersEndpoint: GET /api/v1/orders?pageIndex=1&pageSize=10&status=New ListOrdersEndpoint->>ListOrdersValidator: Validate query parameters ListOrdersValidator-->>ListOrdersEndpoint: Validation result ListOrdersEndpoint->>ListOrdersQuery: Send(query) ListOrdersQuery->>ListOrdersQuery: Check user roles alt User has Admin role ListOrdersQuery->>ListOrdersQuery: Use provided filters else Regular user ListOrdersQuery->>ListOrdersQuery: Force BuyerId filter to user's ID end ListOrdersQuery->>OrderFilterSpec: Create specification with filters ListOrdersQuery->>OrderRepository: ListAsync(OrderFilterSpec) OrderRepository-->>ListOrdersQuery: Order entities ListOrdersQuery->>OrderFilterSpec: Create count specification ListOrdersQuery->>OrderRepository: CountAsync(OrderFilterSpec) OrderRepository-->>ListOrdersQuery: Total count ListOrdersQuery->>ListOrdersQuery: Map to OrderDto objects ListOrdersQuery->>ListOrdersQuery: Create PagedResult ListOrdersQuery-->>ListOrdersEndpoint: PagedResult ListOrdersEndpoint-->>Client: 200 OK with paged result ``` ### Key Components 1. **ListOrdersQuery**: Implements `IQuery>` to retrieve a paginated list of orders 2. **ListOrdersValidator**: Validates the pagination and filter parameters 3. **OrderFilterSpec**: Specification pattern implementation for filtering and pagination 4. **Role-Based Access Control**: Regular users can only see their own orders, while admins can see all orders 5. **OrderDto**: Lightweight projection of the Order aggregate for read operations ## Domain Significance Orders represent completed purchase intentions by customers and serve as the primary aggregate root in the Ordering domain. Each Order encapsulates: - Customer information (BuyerId) - Order items with their quantities and prices - Order status within the fulfillment lifecycle (New, Cancelled, Completed) - Creation date and total price ## Technical Implementation The endpoint uses page-based navigation to handle potentially large datasets according to domain constraints: - **CQRS Pattern**: Separates read and write concerns with dedicated query handlers - **Specification Pattern**: Uses a dedicated specification class for filtering and pagination - **Repository Pattern**: Abstracts the underlying data access concerns - **Role-Based Security**: Automatically filters results based on the user's role - **Sorting**: Results are ordered by creation date in descending order (newest first) ## Authorization Context Access to orders is governed by domain policies that determine which orders a particular user can view: - **Regular Users**: Can only view their own orders (BuyerId is automatically set to the user's ID) - **Administrators**: Can view all orders and optionally filter by BuyerId or Status ## Architecture ## GET `(/api/v1/orders)` ### Query Parameters - **pageIndex** (query) (optional): The page number to retrieve (default: 1) - **pageSize** (query) (optional): The number of items per page (default: 10) - **status** (query) (optional): Filter orders by status (New, Cancelled, Completed) - **buyerId** (query) (optional): Filter orders by buyer ID (admin only) ### Validation Rules - **pageIndex**: Must be greater than 0 - **pageSize**: Must be greater than 0 - **status**: Must be a valid enum value (New, Cancelled, Completed) ### Example Usage #### Regular User ```bash curl -X GET "https://api.bookworm.com/api/v1/orders?pageIndex=1&pageSize=10&status=New" \ -H "Authorization: Bearer " ``` #### Admin User ```bash curl -X GET "https://api.bookworm.com/api/v1/orders?pageIndex=1&pageSize=10&status=New&buyerId={buyerId}" \ -H "Authorization: Bearer " ``` ### Responses #### 200 OK Returns a paginated list of orders with metadata about the pagination. #### Example Response ```json title="200 OK" { "pageIndex": 1, "pageSize": 10, "totalItems": 25, "totalPages": 3, "hasNextPage": true, "hasPreviousPage": false, "items": [ { "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "date": "2025-03-30T15:30:45Z", "total": 59.97, "status": "New" }, { "id": "8a1b6a9c-7d3e-4f5a-9b2c-1d3e5f7a9b2c", "date": "2025-03-29T10:15:22Z", "total": 124.5, "status": "New" } ] } ``` #### 400 Bad Request Returned when the request validation fails. ```json title="400 Bad Request" { "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1", "title": "Bad Request", "status": 400, "errors": { "PageIndex": ["The field PageIndex must be greater than 0."], "PageSize": ["The field PageSize must be greater than 0."], "Status": ["The field Status has a range of values which does not include '5'."] } } ``` #### 401 Unauthorized Returned when the request lacks valid authentication credentials. --- id: OrderStream version: 1.0.0 name: Order Stream summary: Real-time order event sourcing stream for BookWorm badges: - content: WebSocket textColor: orange backgroundColor: orange icon: WifiIcon - content: Order textColor: yellow backgroundColor: yellow icon: BookOpenIcon owners: - nhanxnguyen --- ## Overview The Order Stream service provides a real-time WebSocket connection for users to interact with the BookWorm platform's order management system. It allows users to receive real-time updates on order events, enabling dynamic interactions and monitoring of order statuses. ## Architecture ## WebSocket `wss://api.bookworm.com/api/v1/orders/stream` The Order Stream service uses WebSocket for real-time communication. Users can connect to the WebSocket endpoint to send and receive order-related events. ### Example Usage ```js title="order-stream.js" const connection = new signalR.HubConnectionBuilder() .withUrl("wss://api.bookworm.com/api/v1/orders/stream", { accessTokenFactory: () => "your-access-token", }) .build(); await connection.start(); connection.on("OrderCreated", (order) => { console.log("New order created:", order); }); connection.on("OrderUpdated", (order) => { console.log("Order updated:", order); }); connection.on("OrderDeleted", (orderId) => { console.log("Order deleted:", orderId); }); connection.onclose(() => { console.log("Connection closed"); }); connection.onreconnected(() => { console.log("Reconnected to the order stream"); }); ``` ## Event Types The Order Stream service supports the following event types: - `OrderPlaced`: Triggered when a new order is placed. - `OrderCompleted`: Triggered when an order is completed. - `OrderCancelled`: Triggered when an order is cancelled. --- id: SummarizeFeedbackQuery version: 1.0.0 name: Summarize Feedback summary: AI-powered intelligent summarization of book feedback using semantic analysis badges: - content: GET textColor: blue backgroundColor: blue icon: MagnifyingGlassIcon - content: "AI-Powered" textColor: purple backgroundColor: purple icon: SparklesIcon - content: "Agent Framework" textColor: green backgroundColor: green icon: CpuChipIcon - content: "Real-time" textColor: orange backgroundColor: orange icon: BoltIcon owners: - nhanxnguyen --- ## Overview The **Summarize Feedback** endpoint leverages advanced AI capabilities through Microsoft Agent Framework to provide intelligent, contextual summaries of user feedback and ratings for books. This sophisticated query operation goes beyond simple aggregation by using a specialized RatingAgent that analyzes sentiment, calculates quality metrics, and classifies book performance into distinct categories. As a core read operation within the Rating bounded context, this endpoint implements CQRS principles with AI-enhanced processing, enabling clients to receive comprehensive feedback insights without impacting the command-side operations of the Rating domain. ### Key Features - **AI-Powered Analysis**: Uses Microsoft Agent Framework with Ollama integration for advanced text processing - **Quality Classification**: Automatically categorizes books as "Best Seller", "Good", "Bad", or "No Data" based on sophisticated algorithms - **Real-time Processing**: Provides instant feedback summaries with low-latency response times - **Contextual Intelligence**: Considers rating patterns, review quality, and temporal factors in analysis - **Rate Limited**: Implements per-user rate limiting to ensure fair usage and system stability ## Example Usage ### Basic Request ```bash curl -X GET "https://api.bookworm.com/api/v1/feedbacks/{bookId}/summarize" \ -H "Authorization: Bearer " \ -H "Accept: application/json" ``` ### Response Example ```json { "summary": "**Classification: Best Seller** 📚⭐\n\nThis book demonstrates exceptional quality with an average rating of 4.7/5 across 156 reviews. The feedback analysis reveals:\n\n**Key Insights:**\n- 89% of reviews are 4-5 stars\n- Consistently praised for engaging storytelling and character development\n- Recent reviews maintain high quality scores\n- Strong recommendation patterns from verified readers\n\n**Confidence Level: 95%**\n\nThis title qualifies as a Best Seller based on sustained high ratings (≥4.5) with significant review volume (>50 reviews)." } ``` ## Implementation Details The Summarize Feedback feature implements a sophisticated AI-powered architecture that combines CQRS patterns with Microsoft Agent Framework capabilities for intelligent feedback analysis. ```mermaid flowchart TD HandoffStart["HandoffStart (Start)"] Router["Router"] Language["Language"] Summarize["Summarize"] Sentiment["Sentiment"] Rating["Rating"] HandoffEnd["HandoffEnd"] HandoffStart --> Router Router --> Language Router --> Summarize Router --> Sentiment Router --A2A--> Rating Router --> HandoffEnd Language --A2A--> Rating Language --> HandoffEnd Summarize --A2A--> Rating Summarize --> HandoffEnd Sentiment --A2A--> Rating Sentiment --> HandoffEnd Rating --A2A--> Router Rating --> HandoffEnd ``` ## API Specification ### GET `/api/v1/feedbacks/{id}/summarize` #### Path Parameters | Parameter | Type | Required | Description | | --------- | ------ | -------- | ----------------------------------------------------------- | | `id` | `guid` | ✅ | The unique identifier of the book to summarize feedback for | #### Headers | Header | Type | Required | Description | | --------------- | -------- | -------- | ----------------------------------------------------- | | `Authorization` | `string` | ✅ | Bearer token for authentication | | `Accept` | `string` | ❌ | Content type preference (default: `application/json`) | #### Response Schema **200 OK** ```json { "summary": "string" } ``` **401 Unauthorized** ```json { "type": "https://httpstatuses.com/401", "title": "Unauthorized", "status": 401, "detail": "Authentication required" } ``` **404 Not Found** ```json { "type": "https://httpstatuses.com/404", "title": "Not Found", "status": 404, "detail": "No ratings found for book with ID {bookId}" } ``` **429 Too Many Requests** ```json { "type": "https://httpstatuses.com/429", "title": "Too Many Requests", "status": 429, "detail": "Rate limit exceeded" } ``` ### Performance Metrics - **Average Response Time**: ~200-500ms (depending on feedback volume) - **P95 Response Time**: Less than 1 second - **Throughput**: 100+ requests/second per instance - **AI Processing**: 50-200ms for analysis (varies by complexity) --- id: VisualizeChatWorkflowQuery version: 1.0.0 name: Visualize Workflow summary: Retrieve the workflow visualization for a specific chat session badges: - content: GET textColor: blue backgroundColor: blue icon: MagnifyingGlassIcon - content: "Chat" textColor: yellow backgroundColor: yellow icon: BookOpenIcon owners: - nhanxnguyen deprecated: date: 2025-11-11 message: | This query has been deprecated and removed from the API. It is no longer available for use. Use `DevUI` instead. --- ## Overview The **Visualize Chat Workflow Query** provides administrators with the ability to retrieve a visual representation of the agent orchestration workflow used within the chat service. This query enables technical stakeholders to understand how different AI agents collaborate and communicate during chat sessions, facilitating better system comprehension, debugging, and documentation. ## Business Purpose This query serves as a diagnostic and documentation tool that allows system administrators and technical staff to: - **Understand Agent Interactions**: Visualize how multiple AI agents work together to process chat requests - **Support System Documentation**: Generate up-to-date workflow diagrams for technical documentation and training materials - **Enable Troubleshooting**: Provide visual insights into the agent orchestration flow to identify potential bottlenecks or issues - **Facilitate Onboarding**: Help new team members quickly understand the chat service architecture ## Query Parameters ### Type (Optional) - **Description**: Specifies the output format for the workflow visualization - **Default Value**: Mermaid - **Available Options**: - **Mermaid**: Generates a Mermaid diagram format, ideal for embedding in documentation and rendering in modern documentation platforms - **Dot**: Produces a GraphViz DOT format, suitable for advanced graph visualization tools and custom rendering requirements ## Response The query returns a text-based representation of the workflow in the requested format. This textual representation can be: - Embedded directly into documentation systems that support the chosen format - Rendered into visual diagrams using appropriate visualization tools - Stored for version control and change tracking - Shared with stakeholders for review and analysis ## Authorization & Access Control This query is restricted to administrators only, as it exposes internal system architecture details that should not be accessible to regular users. The rate limiting policy ensures system resources are protected from excessive visualization requests. ## Use Cases ### Documentation Generation Technical writers can use this query to automatically generate current workflow diagrams for inclusion in system documentation, ensuring diagrams remain synchronized with the actual implementation. ### System Monitoring Operations teams can periodically retrieve workflow visualizations to verify that the agent orchestration configuration matches expected patterns and identify any unexpected changes. ### Development & Testing Development teams can use this visualization during development cycles to validate that workflow changes are correctly reflected in the agent orchestration layer. ### Training & Knowledge Transfer New team members and stakeholders can request workflow visualizations to accelerate their understanding of the chat service's internal architecture. ## Integration Points This query integrates with: - **Agent Orchestration Service**: Retrieves the current workflow configuration from the orchestration layer - **Authentication System**: Validates administrator credentials before processing requests - **Rate Limiting Service**: Ensures fair resource usage across administrator requests --- id: VisualizeRatingWorkflowQuery version: 1.0.0 name: Visualize Workflow summary: Retrieve the workflow visualization for a specific rating session badges: - content: GET textColor: blue backgroundColor: blue icon: MagnifyingGlassIcon - content: "Feedback" textColor: yellow backgroundColor: yellow icon: StarIcon owners: - nhanxnguyen deprecated: date: 2025-11-11 message: | This query has been deprecated and removed from the API. It is no longer available for use. Use `DevUI` instead. --- ## Overview The **Visualize Rating Workflow Query** provides administrators with the ability to retrieve a visual representation of the agent orchestration workflow used within the rating service. This query enables technical stakeholders to understand how different AI agents collaborate and communicate during rating sessions, facilitating better system comprehension, debugging, and documentation. ## Business Purpose This query serves as a diagnostic and documentation tool that allows system administrators and technical staff to: - **Understand Agent Interactions**: Visualize how multiple AI agents work together to process rating requests - **Support System Documentation**: Generate up-to-date workflow diagrams for technical documentation and training materials - **Enable Troubleshooting**: Provide visual insights into the agent orchestration flow to identify potential bottlenecks or issues - **Facilitate Onboarding**: Help new team members quickly understand the rating service architecture ## Query Parameters ### Type (Optional) - **Description**: Specifies the output format for the workflow visualization - **Default Value**: Mermaid - **Available Options**: - **Mermaid**: Generates a Mermaid diagram format, ideal for embedding in documentation and rendering in modern documentation platforms - **Dot**: Produces a GraphViz DOT format, suitable for advanced graph visualization tools and custom rendering requirements ## Response The query returns a text-based representation of the workflow in the requested format. This textual representation can be: - Embedded directly into documentation systems that support the chosen format - Rendered into visual diagrams using appropriate visualization tools - Stored for version control and change tracking - Shared with stakeholders for review and analysis ## Authorization & Access Control This query is restricted to administrators only, as it exposes internal system architecture details that should not be accessible to regular users. The rate limiting policy ensures system resources are protected from excessive visualization requests. ## Use Cases ### Documentation Generation Technical writers can use this query to automatically generate current workflow diagrams for inclusion in system documentation, ensuring diagrams remain synchronized with the actual implementation. ### System Monitoring Operations teams can periodically retrieve workflow visualizations to verify that the agent orchestration configuration matches expected patterns and identify any unexpected changes. ### Development & Testing Development teams can use this visualization during development cycles to validate that workflow changes are correctly reflected in the agent orchestration layer. ### Training & Knowledge Transfer New team members and stakeholders can request workflow visualizations to accelerate their understanding of the chat service's internal architecture. ## Integration Points This query integrates with: - **Agent Orchestration Service**: Retrieves the current workflow configuration from the orchestration layer - **Authentication System**: Validates administrator credentials before processing requests - **Rate Limiting Service**: Ensures fair resource usage across administrator requests --- id: BasketService name: Basket Service version: 1.0.0 summary: >- Manages the lifecycle of customer shopping baskets in the BookWorm e-commerce system badges: - content: Event-Driven textColor: purple backgroundColor: purple icon: BoltIcon - content: Redis textColor: red backgroundColor: red icon: CircleStackIcon - content: gRPC textColor: blue backgroundColor: blue icon: ServerIcon sends: - id: BasketDeletedCompletedEvent version: 1.0.0 to: - &ref_0 id: "finance.{env}.events" version: 1.0.0 parameters: env: stg - *ref_0 - id: BasketDeletedFailedEvent version: 1.0.0 to: - id: "finance.{env}.events" version: 1.0.0 parameters: env: stg - id: "finance.{env}.events" version: 1.0.0 parameters: env: stg - id: ListBooksGrpc version: 1.0.0 to: - id: "catalog.{env}.events" version: 1.0.0 parameters: env: stg receives: - id: GetBasketGrpc version: 1.0.0 from: - id: "basket.{env}.events" version: 1.0.0 parameters: env: stg - id: PlaceOrderCommand version: 1.0.0 from: - id: "notification.{env}.events" version: 1.0.0 parameters: env: stg - id: "basket.{env}.events" version: 1.0.0 parameters: env: stg - id: "basket.{env}.events" version: 1.0.0 parameters: env: stg schemaPath: openapi-v1.yml specifications: - type: openapi path: openapi-v1.yml name: OpenAPI V1 - type: asyncapi path: asyncapi-v1.yml name: AsyncAPI V1 owners: - nhanxnguyen repository: language: C# url: "https://github.com/foxminchan/BookWorm" writesTo: - id: BasketDatabase version: 1.0.0 readsFrom: - id: BasketDatabase version: 1.0.0 --- ## Overview The Basket Service is a domain-centric microservice within our bounded context that implements a gRPC-based API architecture to manage the transient state of customer shopping carts. This service encapsulates the complete lifecycle of a basket aggregate, from creation through modification to eventual checkout processing. As a core part of our Domain-Driven Design implementation, the Basket Service maintains its own dedicated Redis persistence store, ensuring proper encapsulation of the basket domain model. It serves as the system of record for the shopping intent of customers before this intent is transformed into an order through domain events. ### Key Responsibilities - **Basket Lifecycle Management**: Creation, modification, and deletion of shopping baskets - **Item Management**: Adding, updating, and removing items with quantity validation - **Checkout Orchestration**: Coordinating the transition from basket to order - **State Persistence**: Maintaining basket state in Redis with configurable TTL - **Event Publishing**: Emitting domain events for basket state changes ## Component Diagram ```mermaid C4Component title Component diagram for Basket Service Container_Boundary(basket, "Basket Service") { Container_Boundary(application, "Application") { Component(basketEndpoint, "Basket Endpoints", ".NET", "Manages basket operations") Component(basketFacade, "Basket Facade", ".NET", "Core business logic for basket management") Component(eventPublisher, "Event Handler", ".NET", "Publishes basket events") } Container_Boundary(infrastructure, "Infrastructure") { ComponentDb(basketDb, "Basket DB", "Redis", "Stores basket data") ComponentQueue(eventBus, "Event Bus", "RabbitMQ", "Handles async communication") } } Rel(basketEndpoint, basketFacade, "Uses", "Internal") BiRel(basketFacade, eventPublisher, "Uses", "Internal") Rel(basketFacade, basketDb, "Reads/Writes", "Redis Protocol") BiRel(eventPublisher, eventBus, "Publishes/Subscribes", "Async") UpdateLayoutConfig($c4ShapeInRow="3", $c4BoundaryInRow="1") ``` ## Core Features | Feature | Description | Event Published | | --------------------------- | --------------------------------------------------------------------------------------------------------- | -------------------------------- | | **Get Basket** | Retrieves the current basket for an authenticated user, including all items and their quantities. | - | | **Add Item to Basket** | Allows customers to add items to their basket, with validation ensuring quantities are greater than zero. | `BasketItemAddedEvent` | | **Remove Item from Basket** | Enables customers to remove items from their basket, maintaining data consistency. | `BasketItemRemovedEvent` | | **Update Item Quantity** | Supports customers in updating the quantity of items in their basket, with built-in validation. | `BasketItemQuantityUpdatedEvent` | | **Clear Basket** | Removes all items from the basket at once. | `BasketClearedEvent` | | **Delete Basket** | Permanently removes the basket from the system. | `BasketDeletedCompletedEvent` | ## Architecture diagram ## Key Concepts The basket is the core domain entity representing a customer's shopping cart. It is identified by a unique ID and contains a collection of basket items. The basket is stored in Redis for fast access and persistence. Basket items are the individual products added to the basket by the customer. ## Infrastructure The Basket Service is deployed as a containerized application within the BookWorm ecosystem, leveraging cloud-native technologies for scalability and resilience. ```mermaid architecture-beta group api(logos:microsoft-azure)[API Layer] group data(logos:microsoft-azure)[Data Layer] group messaging(logos:microsoft-azure)[Messaging Layer] service server(logos:docker-icon)[Application] in api service db(logos:redis)[Redis Primary] in data service bus(logos:rabbitmq)[RabbitMQ] in messaging server:L --> R:db server:R --> L:bus ``` ## Performance Optimizations - **Connection Pooling**: Redis connection multiplexing - **Caching Strategy**: L1 in-memory cache with L2 Redis cache - **Batch Operations**: Bulk item updates in single transactions - **Async/Await**: Non-blocking I/O throughout the stack ## Security Considerations - **Authentication**: JWT bearer tokens validated on each request - **Authorization**: Customer can only access their own basket - **Input Validation**: All inputs sanitized and validated - **Rate Limiting**: Per-customer request throttling - **Data Encryption**: TLS for transit, optional Redis encryption at rest ## Raw Schema:openapi-v1.yml --- openapi: 3.1.1 info: title: Basket Service API description: | Manages the lifecycle of customer shopping baskets in the BookWorm e-commerce system contact: name: Nhan Nguyen url: https://github.com/foxminchan email: nguyenxuannhan407@gmail.com license: name: MIT url: https://opensource.org/licenses/MIT version: "1.0" externalDocs: description: Documentation url: https://github.com/foxminchan/BookWorm servers: - url: "{protocol}://{environment}.basket.bookworm.com" description: Non-production environment variables: protocol: enum: - https - http - grpc default: https environment: enum: - dev - stg - qa default: dev - url: "{protocol}://basket.bookworm.com" description: Production environment variables: protocol: enum: - https - grpc default: https security: - OAuth: - basket_read - basket_write paths: /api/v1/baskets: put: tags: - Basket summary: Update Basket description: Update a basket by its unique identifier x-eventcatalog-message-type: command operationId: UpdateBasketEndpoint security: - OAuth: - basket_read - basket_write requestBody: description: The command to update the basket with new items content: application/json: schema: "$ref": "#/components/schemas/UpdateBasketCommand" examples: single-item: summary: Update basket with a single item value: items: - id: "019622a3-a3ae-7705-a11b-a08f2a2ca621" quantity: 2 multiple-items: summary: Update basket with multiple items value: items: - id: "019622a3-a3ae-7705-a11b-a08f2a2ca621" quantity: 2 - id: "019622a4-a3ae-7705-a11b-a08f2a2ca622" quantity: 1 required: true responses: "204": description: No Content "400": description: Bad Request content: application/problem+json: schema: "$ref": "#/components/schemas/HttpValidationProblemDetails" examples: invalid-quantity: summary: Invalid quantity provided value: type: "https://tools.ietf.org/html/rfc9110#section-15.5.1" title: "Validation failed" status: 400 detail: "One or more validation errors has occurred" errors: - propertyName: "'Quantity'" errorMessage: "'Quantity' must be greater than 0." attemptedValue: null customState: null severity: 0 errorCode: null formattedMessagePlaceholderValues: null empty-item-id: summary: Invalid item ID format value: type: "https://tools.ietf.org/html/rfc9110#section-15.5.1" title: "Validation failed" status: 400 detail: "One or more validation errors has occurred" errors: - propertyName: "'Id'" errorMessage: "'Id' must not be empty." attemptedValue: null customState: null severity: 0 errorCode: null formattedMessagePlaceholderValues: null "401": description: Unauthorized - Access token is missing or invalid. get: tags: - Basket summary: Get Basket description: Get a basket by user x-eventcatalog-message-type: query operationId: GetBasketEndpoint security: - OAuth: - basket_read - basket_write responses: "200": description: OK content: application/json: schema: "$ref": "#/components/schemas/CustomerBasketDto" examples: empty-basket: summary: Empty basket response value: id: "019622af-19dd-73dd-9420-9e9d401ad85b" items: [] basket-with-items: summary: Basket with multiple items value: id: "019622af-19dd-73dd-9420-9e9d401ad85b" items: - id: "019622a3-a3ae-7705-a11b-a08f2a2ca621" quantity: 2 name: "Designing Data-Intensive Applications" price: 46.67 priceSale: 41.89 - id: "019622a4-a3ae-7705-a11b-a08f2a2ca622" quantity: 1 name: "Clean Code" price: 39.99 priceSale: 35.99 "401": description: Unauthorized - Access token is missing or invalid. "404": description: Not Found content: application/problem+json: schema: "$ref": "#/components/schemas/ProblemDetails" example: basket-not-found: summary: Basket not found value: type: "https://tools.ietf.org/html/rfc9110#section-15.5.5" title: "Not Found" status: 404 detail: "Basket with id 01962e92-02ed-7b23-8af6-95b267a97e44 not found." delete: tags: - Basket summary: Delete Basket description: Delete a basket by its unique identifier x-eventcatalog-message-type: command operationId: DeleteBasketEndpoint security: - OAuth: - basket_read - basket_write responses: "204": description: No Content "401": description: Unauthorized - Access token is missing or invalid. "404": description: Not Found content: application/problem+json: schema: "$ref": "#/components/schemas/ProblemDetails" example: basket-not-found: summary: Basket not found value: type: "https://tools.ietf.org/html/rfc9110#section-15.5.5" title: "Not Found" status: 404 detail: "Basket with id 01962e92-02ed-7b23-8af6-95b267a97e44 not found." post: tags: - Basket summary: Create Basket description: Create a new basket for a user x-eventcatalog-message-type: command operationId: CreateBasketEndpoint security: - OAuth: - basket_read - basket_write requestBody: description: A command to create a basket with the specified items content: application/json: schema: "$ref": "#/components/schemas/CreateBasketCommand" examples: single-item: summary: Create basket with a single item value: items: - id: "019622a3-a3ae-7705-a11b-a08f2a2ca621" quantity: 2 multiple-items: summary: Create basket with multiple items value: items: - id: "019622a3-a3ae-7705-a11b-a08f2a2ca621" quantity: 2 - id: "019622a4-a3ae-7705-a11b-a08f2a2ca622" quantity: 1 required: true responses: "201": description: Created content: application/json: schema: type: string description: The unique identifier of the basket example: "019622af-19dd-73dd-9420-9e9d401ad85b" "400": description: Bad Request content: application/problem+json: schema: "$ref": "#/components/schemas/HttpValidationProblemDetails" examples: invalid-quantity: summary: Invalid quantity provided value: type: "https://tools.ietf.org/html/rfc9110#section-15.5.1" title: "Validation failed" status: 400 detail: "One or more validation errors has occurred" errors: - propertyName: "'Quantity'" errorMessage: "'Quantity' must be greater than 0." attemptedValue: null customState: null severity: 0 errorCode: null formattedMessagePlaceholderValues: null empty-item-id: summary: Invalid item ID format value: type: "https://tools.ietf.org/html/rfc9110#section-15.5.1" title: "Validation failed" status: 400 detail: "One or more validation errors has occurred" errors: - propertyName: "'Id'" errorMessage: "'Id' must not be empty." attemptedValue: null customState: null severity: 0 errorCode: null formattedMessagePlaceholderValues: null "401": description: Unauthorized - Access token is missing or invalid. /grpc/v1/baskets: get: tags: - Basket summary: Get Basket (gRPC) description: Retrieve a basket using gRPC x-eventcatalog-message-type: query x-grpc-service: BasketGrpcService x-grpc-method: GetBasket security: - OAuth: - basket_read - basket_write operationId: GetBasketGrpcEndpoint responses: "200": description: OK content: application/grpc: schema: "$ref": "#/components/schemas/BasketResponse" examples: empty-basket: summary: Empty basket response value: id: "019622af-19dd-73dd-9420-9e9d401ad85b" items: [] basket-with-items: summary: Basket with multiple items value: id: "019622af-19dd-73dd-9420-9e9d401ad85b" items: - id: "019622a3-a3ae-7705-a11b-a08f2a2ca621" quantity: 2 - id: "019622a4-a3ae-7705-a11b-a08f2a2ca622" quantity: 1 "401": description: Unauthorized - Access token is missing or invalid. "404": description: Not Found content: application/problem+json: schema: "$ref": "#/components/schemas/ProblemDetails" example: basket-not-found: summary: Basket not found value: type: "https://tools.ietf.org/html/rfc9110#section-15.5.5" title: "Not Found" status: 404 detail: "Basket with id 01962e92-02ed-7b23-8af6-95b267a97e44 not found." components: schemas: BasketItemDto: type: object readOnly: true description: The basket item details properties: id: type: - string description: The unique identifier of the basket item example: "019622a3-a3ae-7705-a11b-a08f2a2ca621" quantity: type: integer format: int32 description: The quantity of the basket item example: 16 name: type: string description: The name of the basket item example: "Designing Data-Intensive Applications" price: type: number format: double description: The price of the basket item example: 46.67 priceSale: type: number format: double description: The sale price of the basket item example: 41.89 BasketItemRequest: required: - id - quantity type: object writeOnly: true description: The basket item request properties: id: type: string description: The unique identifier of the basket item example: "019622a3-a3ae-7705-a11b-a08f2a2ca621" quantity: type: integer format: int32 description: The quantity of the basket item minimum: 1 example: 16 CreateBasketCommand: required: - items type: object writeOnly: true description: The command to create a basket properties: items: description: An array of basket items to be included in the basket example: - id: "019622a3-a3ae-7705-a11b-a08f2a2ca621" quantity: 2 - id: "019622a4-a3ae-7705-a11b-a08f2a2ca622" quantity: 1 type: array items: "$ref": "#/components/schemas/BasketItemRequest" UpdateBasketCommand: required: - items type: object writeOnly: true description: The command to update a basket properties: items: description: An array of basket items to update the basket with example: - id: "019622a3-a3ae-7705-a11b-a08f2a2ca621" quantity: 3 - id: "019622a4-a3ae-7705-a11b-a08f2a2ca622" quantity: 1 type: array items: "$ref": "#/components/schemas/BasketItemRequest" CustomerBasketDto: type: object readOnly: true description: The customer basket details example: id: "019622a3-a3ae-7705-a11b-a08f2a2ca621" items: - id: "019622a3-a3ae-7705-a11b-a08f2a2ca621" quantity: 2 name: "Designing Data-Intensive Applications" price: 46.67 priceSale: 41.89 - id: "019622a4-a3ae-7705-a11b-a08f2a2ca622" quantity: 1 name: "Clean Code" price: 39.99 priceSale: 35.99 properties: id: type: - string description: The unique identifier of the basket example: "019622a3-a3ae-7705-a11b-a08f2a2ca621" items: type: array items: "$ref": "#/components/schemas/BasketItemDto" HttpValidationProblemDetails: type: object readOnly: true x-scalar-ignore: true description: RFC 9110 (https://tools.ietf.org/html/rfc9110#section-15.5.1) properties: type: type: - "null" - string description: The type of the problem example: "https://tools.ietf.org/html/rfc9110#section-15.5.1" title: type: - "null" - string description: The title of the problem example: "Validation failed" status: pattern: ^-?(?:0|[1-9]\d*)$ type: - "null" - integer - string format: int32 description: The status code of the problem example: 400 detail: type: - "null" - string description: The detail of the problem example: "One or more validation errors has occurred" instance: type: - "null" - string format: uri description: The instance of the problem example: "/api/v1/baskets" errors: type: object additionalProperties: type: array items: type: string description: The validation errors example: propertyName: "'Quantity'" errorMessage: "'Quantity' must be greater than 0." attemptedValue: null customState: null severity: 0 errorCode: null formattedMessagePlaceholderValues: null ProblemDetails: type: object readOnly: true x-scalar-ignore: true description: RFC 9110 (https://tools.ietf.org/html/rfc9110) properties: type: type: - "null" - string description: The type of the problem example: "https://tools.ietf.org/html/rfc9110#section-15.5.5" title: type: - "null" - string description: The title of the problem example: "Not Found" status: pattern: ^-?(?:0|[1-9]\d*)$ type: - "null" - integer - string format: int32 description: The status code of the problem example: 404 detail: type: - "null" - string description: The detail of the problem example: "Basket with id 01962e92-02ed-7b23-8af6-95b267a97e44 not found." instance: type: - "null" - string format: uri description: The instance of the problem example: "/api/v1/baskets/01962e92-02ed-7b23-8af6-95b267a97e44" BasketResponse: type: object readOnly: true description: | gRPC BasketResponse model (from basket.proto) This aligns with REST CustomerBasketDto but contains only core fields properties: id: type: string description: The unique identifier of the basket example: "019622a3-a3ae-7705-a11b-a08f2a2ca621" items: type: array description: Array of basket items items: "$ref": "#/components/schemas/Item" Item: type: object readOnly: true description: | gRPC Item model (from basket.proto) This aligns with REST BasketItemDto but contains only core fields properties: id: type: string description: The unique identifier of the item example: "019622a3-a3ae-7705-a11b-a08f2a2ca621" quantity: type: integer format: int32 description: The quantity of the item example: 2 securitySchemes: OAuth: type: oauth2 description: OAuth2 security scheme for the BookWorm API flows: authorizationCode: authorizationUrl: "https://auth.bookworm.com/realms/bookworm/protocol/openid-connect/auth" tokenUrl: "https://auth.bookworm.com/realms/bookworm/protocol/openid-connect/token" scopes: basket_read: Read for Basket API basket_write: Write for Basket API x-usePkce: "SHA-256" x-scalar-client-id: "basket" x-scalar-security-body: audience: "account" tags: - name: Basket description: Manages shopping baskets for customers, allowing them to add, update, and remove items before checkout ## Raw Schema:asyncapi-v1.yml --- asyncapi: 3.0.0 info: title: Basket Service API version: 1.0.0 description: |- This service handles all shopping basket operations for the BookWorm e-commerce platform, including creating, updating, and processing customer baskets. contact: name: Nhan Nguyen url: "https://github.com/foxminchan" license: name: MIT url: "https://opensource.org/licenses/MIT" defaultContentType: application/vnd.masstransit+json servers: development: host: dev.eventbus:5672 protocol: amqp protocolVersion: 0.9.1 summary: RabbitMQ server for development environment description: RabbitMQ server for development environment security: - type: userPassword description: An authentication method for the server tags: - name: env:development description: >- Development environment configuration for local testing and debugging staging: host: stg.eventbus:5672 protocol: amqp protocolVersion: 0.9.1 summary: RabbitMQ server for staging environment description: RabbitMQ server for staging environment security: - type: userPassword description: An authentication method for the server tags: - name: env:staging description: >- Staging environment configuration for testing and debugging qa: host: qa.eventbus:5672 protocol: amqp protocolVersion: 0.9.1 summary: RabbitMQ server for QA environment description: RabbitMQ server for QA environment security: - type: userPassword description: An authentication method for the server tags: - name: env:qa description: >- QA environment configuration for testing and debugging production: host: prod.eventbus:5672 protocol: amqp protocolVersion: 0.9.1 summary: RabbitMQ server for production environment description: RabbitMQ server for production environment security: - type: userPassword description: An authentication method for the server tags: - name: env:production description: >- Production environment configuration for production channels: basket-place-order: address: basket-place-order description: Triggered when a customer submits their basket for order processing messages: PlaceOrderCommand.message: $ref: "#/components/messages/placeOrderCommand" basket-checkout-complete: address: basket-checkout-complete description: Published when a basket has been successfully processed and the order is ready for fulfillment messages: PublishCompletedEvent.message: $ref: "#/components/messages/basketDeletedFailedIntegrationEvent" basket-checkout-failed: address: basket-checkout-failed description: Published when there was an error processing the basket checkout messages: PublishFailedEvent.message: $ref: "#/components/messages/basketDeletedCompleteIntegrationEvent" operations: PlaceOrderCommand: title: Place Order summary: Process a new order description: Handles the submission of a customer's basket for order processing action: receive channel: $ref: "#/channels/basket-place-order" messages: - $ref: "#/channels/basket-place-order/messages/PlaceOrderCommand.message" PublishCompletedEvent: title: Publish Completed Event summary: Notify successful checkout description: Signals that the basket has been successfully processed and the order is ready for fulfillment action: send channel: $ref: "#/channels/basket-checkout-complete" messages: - $ref: >- #/channels/basket-checkout-complete/messages/PublishCompletedEvent.message PublishFailedEvent: title: Publish Failed Event summary: Notify checkout failure description: Signals that there was an error processing the basket checkout action: send channel: $ref: "#/channels/basket-checkout-failed" messages: - $ref: "#/channels/basket-checkout-failed/messages/PublishFailedEvent.message" components: schemas: placeOrderCommand: id: placeOrderCommand description: Command message that initiates the order placement process, containing all necessary information to process a customer's basket allOf: - $ref: "#/components/schemas/integrationEvent" - type: object additionalProperties: false properties: basketId: type: string format: guid description: The unique identifier of the basket example: "019622a3-a3ae-7705-a11b-a08f2a2ca621" email: type: - "null" - string description: The email address of the customer pattern: "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$" example: "john.doe@example.com" orderId: type: string format: guid description: The unique identifier of the order example: "019622a3-a3ae-7705-a11b-a08f2a2ca621" totalMoney: type: number format: decimal description: The total amount of money in the basket minimum: 0 example: 100.00 integrationEvent: id: integrationEvent type: object description: Base event structure containing common metadata for all integration events in the system additionalProperties: false properties: id: type: string format: guid description: The unique identifier of the event example: "019622a3-a3ae-7705-a11b-a08f2a2ca621" creationDate: type: string format: date-time description: The date and time the event was created example: "2021-01-01T00:00:00Z" basketDeletedFailedIntegrationEvent: id: basketDeletedFailedIntegrationEvent description: Event published when the system fails to process a basket checkout, containing details about the failed transaction allOf: - $ref: "#/components/schemas/integrationEvent" - type: object additionalProperties: false properties: orderId: type: string format: guid description: The unique identifier of the order example: "019622a3-a3ae-7705-a11b-a08f2a2ca621" basketId: type: string format: guid description: The unique identifier of the basket example: "019622a3-a3ae-7705-a11b-a08f2a2ca621" email: type: - "null" - string description: The email address of the customer pattern: "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$" example: "john.doe@example.com" totalMoney: type: number format: decimal description: The total amount of money in the basket minimum: 0 example: 100.00 basketDeletedCompleteIntegrationEvent: id: basketDeletedCompleteIntegrationEvent description: Event published when a basket is successfully processed and the order is ready for fulfillment allOf: - $ref: "#/components/schemas/integrationEvent" - type: object additionalProperties: false properties: orderId: type: string format: guid description: The unique identifier of the order example: "019622a3-a3ae-7705-a11b-a08f2a2ca621" basketId: type: string format: guid description: The unique identifier of the basket example: "019622a3-a3ae-7705-a11b-a08f2a2ca621" totalMoney: type: number format: decimal description: The total amount of money in the basket minimum: 0 example: 100.00 messages: placeOrderCommand: payload: $ref: "#/components/schemas/placeOrderCommand" name: placeOrderCommand basketDeletedFailedIntegrationEvent: payload: $ref: "#/components/schemas/basketDeletedFailedIntegrationEvent" name: basketDeletedFailedIntegrationEvent basketDeletedCompleteIntegrationEvent: payload: $ref: "#/components/schemas/basketDeletedCompleteIntegrationEvent" name: basketDeletedCompleteIntegrationEvent --- id: ChatService name: Chat Service version: 1.0.0 summary: Manages chat interactions and conversations for the BookWorm platform badges: - content: Event-Driven textColor: purple icon: BoltIcon backgroundColor: purple - content: LLM textColor: green icon: ChatBubbleBottomCenterTextIcon backgroundColor: green - content: Real-time textColor: blue icon: ArrowPathIcon backgroundColor: blue - content: MCP textColor: orange icon: CogIcon backgroundColor: orange sends: - id: GetMcpComponentsQuery version: 1.0.0 receives: - id: ChatStream version: 1.0.0 from: - id: "chat.{env}.events" version: 1.0.0 parameters: env: stg schemaPath: openapi-v1.yml specifications: - type: openapi path: openapi-v1.yml name: OpenAPI V1 owners: - nhanxnguyen repository: language: C# url: "https://github.com/foxminchan/BookWorm" writesTo: - id: ChatDatabase version: 1.0.0 readsFrom: - id: ChatDatabase version: 1.0.0 attachments: - url: >- https://foxminchan.github.io/BookWorm/docs/architecture-decisions/adr-006-signalr-realtime title: ADR-006 - SignalR for Real-time Communication description: Learn more about the real-time communication strategy using SignalR. type: architecture-decisions icon: FileTextIcon - url: >- https://foxminchan.github.io/BookWorm/docs/architecture-decisions/adr-009-ai-integration title: ADR-009 - AI Integration Strategy description: >- Learn more about the AI integration strategy within the BookWorm ecosystem. type: architecture-decisions icon: FileTextIcon - url: >- https://foxminchan.github.io/BookWorm/docs/architecture-decisions/adr-020-mcp-tools-standardize title: ADR-020 - MCP Tools Standardization description: Learn more about the standardization of tools within the MCP ecosystem. type: architecture-decisions icon: FileTextIcon - url: >- https://foxminchan.github.io/BookWorm/docs/architecture-decisions/adr-021-agent-to-agent-via-a2a title: ADR-021 - Agent-to-Agent Communication via A2A description: Learn more about the agent-to-agent communication strategy using A2A. type: architecture-decisions icon: FileTextIcon --- ## Overview The Chat Service is a specialized bounded context within the BookWorm ecosystem that implements natural language processing capabilities to provide interactive conversations with users, following our [AI Integration Strategy](https://foxminchan.github.io/BookWorm/architecture-decisions/adr-009-ai-integration). This service is responsible for: - Processing user chat prompts and generating contextually relevant responses - Maintaining chat session state and history - Providing book recommendations and information through conversational interfaces - Leveraging large language models (LLMs) to understand and respond to user queries - Integrating with the catalog service to access book metadata and information The domain model follows strategic DDD patterns with clearly defined aggregates, entities, and value objects. The Chat domain enforces business rules such as prompt validation, session management, and response generation. ### Key Capabilities - **Conversational AI**: Natural language understanding and generation using state-of-the-art LLMs - **Context Management**: Maintains conversation history and context for coherent multi-turn dialogues - **Tool Integration**: Leverages Model Context Protocol (MCP) for dynamic tool usage - **Streaming Responses**: Real-time streaming of AI responses using [SignalR](https://foxminchan.github.io/BookWorm/architecture-decisions/adr-006-signalr-realtime) for better user experience ## Component Diagram ```mermaid C4Component title Component diagram for Chat Service Container_Boundary(chat, "Chat Service") { Container_Boundary(application, "Application") { Component(chatEndpoint, "Chat Endpoints", ".NET", "Manages chat operations") Component(chatFacade, "Chat Facade", ".NET", "Core business logic for chat management") Component(llmService, "LLM Service", ".NET", "Handles AI-powered conversations") } Container_Boundary(infrastructure, "Infrastructure") { ComponentDb(chatDb, "Chat Database", "PostgreSQL", "Stores chat sessions and history") Container_Boundary(ai, "AI") { Component(ollama, "Ollama", "Ollama", "Get and run models") Component(mcp, "MCP Server", "MCP C# SDK", "Get and run tools") Container_Boundary(model, "Model") { Component(chatModel, "Chat Model", "Gemma 3", "Handles chat interactions") } } } } Rel(chatEndpoint, chatFacade, "Uses", "Internal") Rel(chatFacade, llmService, "Uses", "Internal") Rel(ollama, chatModel, "Uses", "Internal") Rel(mcp, llmService, "Uses", "MCP") Rel(llmService, ollama, "Uses", "Internal") Rel(llmService, chatDb, "Reads/Writes", "Internal") UpdateLayoutConfig($c4ShapeInRow="3", $c4BoundaryInRow="1") ``` ## Core Features | Feature | Description | | --------------------- | ---------------------------------------------------------------------------------------------- | | **Create Chat** | Initialize a new chat session with a prompt and receive a unique identifier. | | **Cancel Chat** | Terminate an existing chat session. | | **Conversational AI** | Engage with an AI assistant that can answer questions about books and provide recommendations. | | **Context Awareness** | Maintain conversation context across multiple interactions within a session. | ## Architecture diagram ## Infrastructure The Chat Service is deployed on Microsoft Azure and leverages various Azure services for scalability, reliability, and performance. The infrastructure components include: ```mermaid architecture-beta group api(logos:microsoft-azure)[API] service server(logos:docker-icon)[Server] in api service llm(server)[LLM] in api server:L -- R:llm llm:L -- R:server ``` ## Security Considerations - **Authentication**: JWT-based authentication with user context isolation - **Rate Limiting**: Per-user request throttling to prevent abuse - **Content Filtering**: Automated moderation of inappropriate content - **Data Privacy**: Conversation history encrypted at rest - **Prompt Injection Protection**: Input sanitization and validation - **API Key Management**: Secure storage of LLM API credentials in Azure Key Vault ## Monitoring and Observability ### Key Metrics - **Response Latency**: Time to first token and total generation time - **Token Usage**: Track token consumption per user and model - **Conversation Quality**: User satisfaction and engagement metrics - **Error Rates**: Failed generations and timeout occurrences - **Tool Usage**: Frequency and success rate of MCP tool calls ## Raw Schema:openapi-v1.yml --- openapi: 3.1.1 info: title: Chat Service API description: Manages the chat functionality for the BookWorm platform, providing conversational interactions about books and reading recommendations contact: name: Nhan Nguyen url: https://github.com/foxminchan email: nguyenxuannhan407@gmail.com license: name: MIT url: https://opensource.org/licenses/MIT version: "1.0" externalDocs: description: Documentation url: https://github.com/foxminchan/BookWorm servers: - url: "{protocol}://{environment}.chat.bookworm.com" description: Non-production environment variables: protocol: enum: - https - http - wss default: https environment: enum: - dev - stg - qa default: dev - url: "{protocol}://chat.bookworm.com" description: Production variables: protocol: enum: - https - wss default: https security: - OAuth: - chatting_read - chatting_write paths: "/api/v1/chats/visualize": get: tags: - Chat summary: Visualizer Workflow deprecated: true description: Get the workflow for the visualizer x-eventcatalog-message-type: query operationId: VisualizeWorkflowEndpoint security: - OAuth: - chatting_read - chatting_write parameters: - name: Type in: query description: The type of visualization to generate schema: $ref: "#/components/schemas/Visualizations" responses: "200": description: OK content: text/plain: schema: type: string description: The workflow in the specified visualization format example: | flowchart TD HandoffStart["HandoffStart (Start)"]; language["language"]; summarize["summarize"]; HandoffEnd["HandoffEnd"]; rating["rating"]; HandoffStart --> language; language --> summarize; language --> HandoffEnd; summarize --> rating; summarize --> HandoffEnd; rating --> language; rating --> HandoffEnd; "400": description: Bad Request content: application/problem+json: schema: "$ref": "#/components/schemas/HttpValidationProblemDetails" examples: invalid-type: summary: "Invalid visualization type" value: type: "https://tools.ietf.org/html/rfc9110#section-15.5.1" title: "Validation failed" status: 400 detail: "One or more validation errors has occurred" errors: - propertyName: "'Type'" errorMessage: "'Type' has a range of values which does not include '4'." attemptedValue: null customState: null severity: 0 errorCode: null formattedMessagePlaceholderValues: null "401": description: Unauthorized - Access token is missing or invalid. "403": description: Forbidden - Access token does not have the required permissions. "/api/v1/chats": post: tags: - Chat summary: Create Chat deprecated: true description: Create a new chat in the catalog system x-eventcatalog-message-type: command operationId: CreateChatEndpoint security: - OAuth: - chatting_read - chatting_write requestBody: description: The command to create a chat content: application/json: schema: "$ref": "#/components/schemas/CreateChatCommand" example: name: "Book Recommendations Chat" required: true responses: "201": description: Created content: application/json: schema: type: string format: uuid description: The unique identifier of the created chat example: "01961f13-64ef-7dbb-84a1-25114e2e0ca4" headers: Location: description: The URI of the created chat schema: type: string format: uri example: "/api/v1/chats/01961f13-64ef-7dbb-84a1-25114e2e0ca4" "400": description: Bad Request content: application/problem+json: schema: "$ref": "#/components/schemas/HttpValidationProblemDetails" examples: empty-name: summary: "Chat name is empty" value: type: "https://tools.ietf.org/html/rfc9110#section-15.5.1" title: "Validation failed" status: 400 detail: "One or more validation errors has occurred" errors: - propertyName: "'Name'" errorMessage: "'Name' must not be empty." attemptedValue: null customState: null severity: 0 errorCode: null formattedMessagePlaceholderValues: null "401": description: Unauthorized - Access token is missing or invalid. get: tags: - Chat deprecated: true summary: List Chats description: List all chats with optional filtering x-eventcatalog-message-type: query operationId: ListChatEndpoint security: - OAuth: - chatting_read - chatting_write parameters: - name: name in: query description: The name of the conversation to filter by required: false schema: type: string example: "Book Recommendations" - name: userId in: query description: The user ID to filter conversations by (admin only) required: false schema: type: string format: uuid example: "01961f13-64ef-7dbb-84a1-25114e2e0ca4" - name: includeMessages in: query description: Flag to include messages in the result required: false schema: type: boolean default: false example: false responses: "200": description: OK content: application/json: schema: type: array items: "$ref": "#/components/schemas/ConversationDto" examples: chat-empty: summary: "Empty chat list" value: [] chat-with-messages: summary: "Chat list with messages" value: - id: "01961f13-64ef-7dbb-84a1-25114e2e0ca4" name: "Book Recommendations" userId: "01961f13-64ef-7dbb-84a1-25114e2e0ca4" messages: - id: "01961f13-64ef-7dbb-84a1-25114e2e0ca4" text: "What are the top science fiction books available?" role: "user" parentMessageId: "01961f13-64ef-7dbb-84a1-25114e2e0ca4" createdAt: "2023-10-01T12:00:00Z" - id: "01961f13-64ef-7dbb-84a1-25114e2e0ca4" text: "Here are some recommendations: Dune, Neuromancer, Foundation." role: "assistant" parentMessageId: "01961f13-64ef-7dbb-84a1-25114e2e0ca4" createdAt: "2023-10-01T12:05:00Z" chat-without-messages: summary: "Chat list without messages" value: - id: "01961f13-64ef-7dbb-84a1-25114e2e0ca4" name: "Book Recommendations" userId: "01961f13-64ef-7dbb-84a1-25114e2e0ca4" messages: [] "401": description: Unauthorized - Access token is missing or invalid. put: tags: - Chat deprecated: true summary: Update Chat description: Update an existing chat in the catalog system x-eventcatalog-message-type: command operationId: UpdateChatEndpoint security: - OAuth: - chatting_read - chatting_write requestBody: description: The command to update a chat content: application/json: schema: "$ref": "#/components/schemas/UpdateChatCommand" example: id: "01961f13-64ef-7dbb-84a1-25114e2e0ca4" prompt: text: "What are the top science fiction books available?" required: true responses: "204": description: No Content "400": description: Bad Request content: application/problem+json: schema: "$ref": "#/components/schemas/HttpValidationProblemDetails" examples: empty-id: summary: "Chat ID is empty" value: type: "https://tools.ietf.org/html/rfc9110#section-15.5.1" title: "Validation failed" status: 400 detail: "One or more validation errors has occurred" errors: - propertyName: "'Id'" errorMessage: "'Id' must not be empty." attemptedValue: null customState: null severity: 0 errorCode: null formattedMessagePlaceholderValues: null empty-prompt: summary: "Chat prompt is empty" value: type: "https://tools.ietf.org/html/rfc9110#section-15.5.1" title: "Validation failed" status: 400 detail: "One or more validation errors has occurred" errors: - propertyName: "'Prompt'" errorMessage: "'Prompt' must not be empty." attemptedValue: null customState: null severity: 0 errorCode: null formattedMessagePlaceholderValues: null "401": description: Unauthorized - Access token is missing or invalid. "404": description: Not Found content: application/json: schema: "$ref": "#/components/schemas/ProblemDetails" "/api/v1/chats/{id}": get: tags: - Chat deprecated: true summary: Get Chat description: Get a chat by its ID x-eventcatalog-message-type: query operationId: GetChatEndpoint security: - OAuth: - chatting_read - chatting_write parameters: - name: id in: path description: The unique identifier of the chat required: true schema: type: string format: uuid example: "01961f13-64ef-7dbb-84a1-25114e2e0ca4" responses: "200": description: OK content: application/json: schema: "$ref": "#/components/schemas/ConversationDto" example: id: "01961f13-64ef-7dbb-84a1-25114e2e0ca4" name: "Book Recommendations" userId: "01961f13-64ef-7dbb-84a1-25114e2e0ca4" messages: - id: "01961f13-64ef-7dbb-84a1-25114e2e0ca4" text: "What are the top science fiction books available?" role: "user" parentMessageId: "01961f13-64ef-7dbb-84a1-25114e2e0ca4" createdAt: "2023-10-01T12:00:00Z" "404": description: Not Found content: application/problem+json: schema: "$ref": "#/components/schemas/ProblemDetails" example: type: "https://tools.ietf.org/html/rfc9110#section-15.5.5" title: "Not Found" status: 404 detail: "Chat with id 01961f13-64ef-7dbb-84a1-25114e2e0ca4 not found." "401": description: Unauthorized - Access token is missing or invalid. delete: tags: - Chat summary: Delete Chat deprecated: true description: Delete a chat by its ID x-eventcatalog-message-type: command operationId: DeleteChatEndpoint security: - OAuth: - chatting_read - chatting_write parameters: - name: id in: path description: The unique identifier of the chat to be deleted required: true schema: type: string format: uuid example: "01961f13-64ef-7dbb-84a1-25114e2e0ca4" responses: "204": description: No Content "404": description: Not Found content: application/problem+json: schema: "$ref": "#/components/schemas/ProblemDetails" example: type: "https://tools.ietf.org/html/rfc9110#section-15.5.5" title: "Not Found" status: 404 detail: "Chat with id 01961f13-64ef-7dbb-84a1-25114e2e0ca4 not found." "401": description: Unauthorized - Access token is missing or invalid. "/api/v1/chats/{id}/cancel": delete: tags: - Chat deprecated: true summary: Cancel Chat description: Cancel a chat if it exists x-eventcatalog-message-type: command operationId: CancelChatEndpoint security: - OAuth: - chatting_read - chatting_write parameters: - name: id in: path description: The unique identifier of the chat to be cancelled required: true schema: type: string format: uuid example: "01961f13-64ef-7dbb-84a1-25114e2e0ca4" responses: "204": description: No Content "401": description: Unauthorized - Access token is missing or invalid. "/api/v1/chats/stream": get: tags: - Chat summary: Chat Stream deprecated: true x-eventcatalog-message-type: command description: SignalR hub endpoint for real-time chat streaming operationId: ChatStreamEndpoint security: - OAuth: - chatting_read - chatting_write responses: "101": description: Switching Protocols - WebSocket connection established "200": description: OK content: application/json: schema: type: string example: "Chat stream endpoint is available." "401": description: Unauthorized - Access token is missing or invalid. components: schemas: Prompt: required: - text type: object writeOnly: true description: The prompt for creating a chat in the catalog system properties: text: type: string description: The text of the prompt example: "What is the best book in the world?" ProblemDetails: type: object readOnly: true x-scalar-ignore: true description: RFC 9110 (https://tools.ietf.org/html/rfc9110) properties: type: type: - "null" - string description: The type of the problem example: "https://tools.ietf.org/html/rfc9110#section-15.5.5" title: type: - "null" - string description: The title of the problem example: "Not Found" status: pattern: ^-?(?:0|[1-9]\d*)$ type: - "null" - integer - string format: int32 description: The status code of the problem example: 404 detail: type: - "null" - string description: The detail of the problem example: "Chat with id 01961f13-64ef-7dbb-84a1-25114e2e0ca4 not found." instance: type: - "null" - string format: uri description: The instance of the problem example: "/api/v1/chats/01961f13-64ef-7dbb-84a1-25114e2e0ca4" HttpValidationProblemDetails: type: object readOnly: true x-scalar-ignore: true description: RFC 9110 (https://tools.ietf.org/html/rfc9110) properties: type: type: - "null" - string description: The type of the problem example: "https://tools.ietf.org/html/rfc9110#section-15.5.1" title: type: - "null" - string description: The title of the problem example: "Validation failed" status: pattern: ^-?(?:0|[1-9]\d*)$ type: - "null" - integer - string format: int32 description: The status code of the problem example: 400 detail: type: - "null" - string description: The detail of the problem example: "One or more validation errors has occurred" instance: type: - "null" - string format: uri description: The instance of the problem example: "/api/v1/chats" errors: type: object additionalProperties: type: array items: type: string description: The validation errors example: propertyName: "Type" errorMessage: "'Type' has a range of values which does not include '4'." attemptedValue: null customState: null severity: 0 errorCode: null formattedMessagePlaceholderValues: null ConversationDto: type: object readOnly: true description: A conversation in the catalog system properties: id: type: string format: uuid description: The unique identifier of the conversation example: "01961f13-64ef-7dbb-84a1-25114e2e0ca4" name: type: string description: The name of the conversation example: "Book Recommendations" userId: type: string format: uuid description: The ID of the user who created the conversation example: "01961f13-64ef-7dbb-84a1-25114e2e0ca4" messages: type: array items: "$ref": "#/components/schemas/MessageDto" CreateChatCommand: type: object required: - name description: Command to create a chat in the catalog system properties: name: type: string description: The name of the chat example: "Book Recommendations Chat" UpdateChatCommand: type: object required: - id - prompt description: Command to update a chat in the catalog system properties: id: type: string format: uuid description: The unique identifier of the chat to be updated example: "01961f13-64ef-7dbb-84a1-25114e2e0ca4" prompt: "$ref": "#/components/schemas/Prompt" MessageDto: type: object readOnly: true description: A message in a conversation properties: id: type: string format: uuid description: The unique identifier of the message example: "01961f13-64ef-7dbb-84a1-25114e2e0ca4" text: type: string description: The text of the message example: "What are the top science fiction books available?" role: type: string description: The role of the message sender (e.g., user, assistant) example: "user" parentMessageId: type: - "string" - "null" format: uuid description: The ID of the parent message, if any example: "01961f13-64ef-7dbb-84a1-25114e2e0ca4" createdAt: type: string format: date-time description: The timestamp when the message was created example: "2023-10-01T12:00:00Z" Visualizations: enum: - Mermaid - Dot default: Mermaid securitySchemes: OAuth: type: oauth2 description: OAuth2 security scheme for the BookWorm API flows: authorizationCode: authorizationUrl: "https://auth.bookworm.com/realms/bookworm/protocol/openid-connect/auth" tokenUrl: "https://auth.bookworm.com/realms/bookworm/protocol/openid-connect/token" scopes: chatting_read: Read for Chatting API chatting_write: Write for Chatting API x-usePkce: "SHA-256" x-scalar-client-id: "chatting" x-scalar-security-body: audience: "account" tags: - name: Chat description: Endpoints for handling book-related discussions and user interactions in chat format --- id: FinanceService name: Finance Service version: 1.0.0 summary: >- Orchestrates order processing and financial transactions for BookWorm using the Saga pattern badges: - content: Saga Orchestrator textColor: blue backgroundColor: blue icon: CubeTransparentIcon - content: Event-Driven textColor: purple backgroundColor: purple icon: BoltIcon - content: Mission Critical textColor: red backgroundColor: red icon: ShieldCheckIcon sends: - id: CancelOrderCommand version: 1.0.0 to: - id: "notification.{env}.events" version: 1.0.0 parameters: env: stg - id: "notification.{env}.events" version: 1.0.0 parameters: env: stg - id: CompleteOrderCommand version: 1.0.0 to: - id: "notification.{env}.events" version: 1.0.0 parameters: env: stg - id: "notification.{env}.events" version: 1.0.0 parameters: env: stg - id: DeleteBasketCompleteCommand version: 1.0.0 to: - id: "ordering.{env}.events" version: 1.0.0 parameters: env: stg - id: "ordering.{env}.events" version: 1.0.0 parameters: env: stg - id: DeleteBasketFailedCommand version: 1.0.0 to: - id: "ordering.{env}.events" version: 1.0.0 parameters: env: stg - id: "ordering.{env}.events" version: 1.0.0 parameters: env: stg - id: PlaceOrderCommand version: 1.0.0 to: - id: "notification.{env}.events" version: 1.0.0 parameters: env: stg - id: "basket.{env}.events" version: 1.0.0 parameters: env: stg - id: "basket.{env}.events" version: 1.0.0 parameters: env: stg receives: - id: BasketDeletedCompletedEvent version: 1.0.0 from: - &ref_0 id: "finance.{env}.events" version: 1.0.0 parameters: env: stg - *ref_0 - id: BasketDeletedFailedEvent version: 1.0.0 from: - id: "finance.{env}.events" version: 1.0.0 parameters: env: stg - id: "finance.{env}.events" version: 1.0.0 parameters: env: stg - id: OrderStatusChangedToCancelEvent version: 1.0.0 from: - id: "finance.{env}.events" version: 1.0.0 parameters: env: stg - id: "finance.{env}.events" version: 1.0.0 parameters: env: stg - id: OrderStatusChangedToCompleteEvent version: 1.0.0 from: - id: "finance.{env}.events" version: 1.0.0 parameters: env: stg - id: "finance.{env}.events" version: 1.0.0 parameters: env: stg - id: UserCheckedOutEvent version: 1.0.0 from: - id: "finance.{env}.events" version: 1.0.0 parameters: env: stg - id: "finance.{env}.events" version: 1.0.0 parameters: env: stg schemaPath: openapi-v1.yml specifications: - type: openapi path: openapi-v1.yml name: OpenAPI V1 - type: asyncapi path: asyncapi-v1.yml name: AsyncAPI V1 owners: - nhanxnguyen repository: language: C# url: "https://github.com/foxminchan/BookWorm" writesTo: - id: FinanceDatabase version: 1.0.0 readsFrom: - id: FinanceDatabase version: 1.0.0 --- ## Overview The Finance Service is the orchestration engine for BookWorm's order processing workflow. It implements the **Saga pattern** to manage distributed transactions across multiple services, ensuring data consistency and handling complex failure scenarios gracefully. ### Key Responsibilities 1. **Payment Processing**: Integrate with payment gateways 2. **Saga Orchestration**: Coordinate multi-service transactions 3. **State Management**: Track order lifecycle states 4. **Compensation Logic**: Handle rollback scenarios 5. **Financial Reporting**: Generate transaction records ## 🏗️ Architecture ### Component Diagram ```mermaid C4Component title Component diagram for Finance Service Container_Boundary(finance, "Finance Service") { Container_Boundary(application, "Application") { Component(financeEndpoint, "Finance Endpoints", ".NET", "Get state machine") Component(sagaOrchestrator, "Saga Orchestrator", ".NET", "Coordinates distributed transactions") } Container_Boundary(infrastructure, "Infrastructure") { ComponentQueue(eventBus, "Event Bus", "RabbitMQ", "Handles async communication") ComponentDb(financeDb, "Finance DB", "PostgreSQL", "Stores financial records and transactions") } } Rel(financeEndpoint, sagaOrchestrator, "Uses", "Internal") Rel(sagaOrchestrator, financeDb, "Reads/Writes", "SQL") BiRel(sagaOrchestrator, eventBus, "Publishes/Subscribes", "Async") UpdateLayoutConfig($c4ShapeInRow="3", $c4BoundaryInRow="1") ``` ### State Machine Workflow The Finance Service implements a state machine to manage the order lifecycle: ```mermaid stateDiagram-v2 [*] --> OrderCreated: User Checkout OrderCreated --> BasketClearing: Process Basket BasketClearing --> BasketCleared: Success BasketClearing --> BasketClearFailed: Failure BasketCleared --> OrderCompleted: Complete Order OrderCompleted --> [*]: End BasketClearFailed --> OrderFailed: Handle Failure OrderFailed --> [*]: End OrderCreated --> OrderCancelled: Cancel Order OrderCancelled --> [*]: End ``` ## 📊 Core Features | Feature | Description | SLA | | ---------------------- | -------------------------------------------------------------- | ------------------- | | **Payment Processing** | Integration with multiple payment providers (Stripe, PayPal) | 99.99% uptime | | **Saga Orchestration** | Distributed transaction management with automatic compensation | < 5s completion | | **Idempotency** | Duplicate request handling for financial operations | 100% accuracy | | **Audit Trail** | Complete transaction history with event sourcing | Permanent retention | | **Multi-Currency** | Support for 50+ currencies with real-time conversion | 99.9% accuracy | | **Fraud Detection** | ML-based transaction risk assessment | < 100ms response | ## 🔒 Security & Compliance ### Security Measures 1. **PCI DSS Level 1** compliance for payment data 2. **End-to-end encryption** for sensitive information 3. **Token vault** for payment method storage 4. **Rate limiting** on payment endpoints 5. **Fraud detection** algorithms ### Compliance Features - **GDPR**: Data privacy and right to be forgotten - **SOC 2**: Security and availability controls - **ISO 27001**: Information security management - **Audit logging**: Immutable transaction records ## 📈 Performance & Scalability ### Performance Metrics | Metric | Target | Current | | --------------- | -------- | ------- | | **Throughput** | 1000 TPS | 850 TPS | | **P50 Latency** | < 50ms | 42ms | | **P95 Latency** | < 200ms | 185ms | | **P99 Latency** | < 500ms | 450ms | | **Error Rate** | < 0.1% | 0.08% | ### Scaling Strategy - **Horizontal scaling**: Kubernetes HPA based on CPU/memory - **Database sharding**: By customer region - **Cache strategy**: Redis cluster with automatic failover - **Queue partitioning**: Service Bus topic partitions ## Architecture diagram ## 🏢 Infrastructure The Finance service is deployed on Microsoft Azure, leveraging Azure Database for PostgreSQL as the primary data store. ```mermaid architecture-beta group api(logos:microsoft-azure)[API] service db(logos:postgresql)[Database] in api service server(logos:docker-icon)[Server] in api db:L -- R:server ``` ## 🔍 Monitoring & Observability ### Dashboards 1. **Business Metrics** - Transaction volume and value - Success/failure rates - Payment method distribution - Geographic distribution 2. **Technical Metrics** - Service health and uptime - Response time percentiles - Error rates by type - Resource utilization 3. **Saga Metrics** - Active saga instances - State transition times - Compensation rates - Timeout occurrences ### Alerts ```yaml - name: HighPaymentFailureRate condition: failure_rate > 5% severity: critical - name: SagaTimeout condition: saga_duration > 60s severity: warning - name: PaymentGatewayDown condition: gateway_health == unhealthy severity: critical ``` ## Raw Schema:openapi-v1.yml --- openapi: 3.1.1 info: title: Finance Service API description: Orchestrates order processing and financial transactions for BookWorm contact: name: Nhan Nguyen url: https://github.com/foxminchan email: nguyenxuannhan407@gmail.com license: name: MIT url: https://opensource.org/licenses/MIT version: "1.0" externalDocs: description: Documentation url: https://github.com/foxminchan/BookWorm servers: - url: "{protocol}://{environment}.finance.bookworm.com" description: Non-production environment variables: protocol: enum: - https - http default: https environment: enum: - dev - stg - qa default: dev - url: "{protocol}://finance.bookworm.com" description: Production variables: protocol: enum: - https default: https security: - OAuth: - finance paths: "/api/v1/order-state-machine": get: deprecated: true tags: - OrderState summary: Get Order State Machine description: Get the order state machine definition x-eventcatalog-message-type: query operationId: GetOrderStateEndpoint security: - OAuth: - finance responses: "200": description: OK content: application/json: schema: type: string description: The order state machine definition example: |- digraph { graph [layout=dot rankdir=LR] node [shape=circle] Initial [shape=diamond] Placed [shape=circle] Completed [shape=circle] Cancelled [shape=circle] Failed [shape=circle] Initial -> Placed [label="OrderPlaced"] Placed -> Failed [label="BasketDeletedFailed"] Placed -> Completed [label="OrderCompleted"] Placed -> Cancelled [label="OrderCancelled"] } "401": description: Unauthorized - Access token is missing or invalid. "403": description: Forbidden - The permission requirements are not met. components: securitySchemes: OAuth: type: oauth2 description: OAuth2 security scheme for the BookWorm API flows: authorizationCode: authorizationUrl: "https://auth.bookworm.com/realms/bookworm/protocol/openid-connect/auth" tokenUrl: "https://auth.bookworm.com/realms/bookworm/protocol/openid-connect/token" scopes: finance: "Finance API" x-usePkce: "SHA-256" x-scalar-client-id: "finance" x-scalar-security-body: audience: "account" tags: - name: OrderState description: Operations related to order state management ## Raw Schema:asyncapi-v1.yml --- asyncapi: 3.0.0 info: title: Finance Service API version: 1.0.0 description: |- Orchestrates order processing and financial transactions for BookWorm contact: name: Nhan Nguyen url: "https://github.com/foxminchan" license: name: MIT url: "https://opensource.org/licenses/MIT" defaultContentType: application/vnd.masstransit+json servers: development: host: dev.eventbus:5672 protocol: amqp protocolVersion: 0.9.1 summary: RabbitMQ server for development environment description: RabbitMQ server for development environment security: - type: userPassword description: An authentication method for the server tags: - name: env:development description: >- Development environment configuration for local testing and debugging staging: host: stg.eventbus:5672 protocol: amqp protocolVersion: 0.9.1 summary: RabbitMQ server for staging environment description: RabbitMQ server for staging environment security: - type: userPassword description: An authentication method for the server tags: - name: env:staging description: >- Staging environment configuration for testing and debugging qa: host: qa.eventbus:5672 protocol: amqp protocolVersion: 0.9.1 summary: RabbitMQ server for QA environment description: RabbitMQ server for QA environment security: - type: userPassword description: An authentication method for the server tags: - name: env:qa description: >- QA environment configuration for testing and debugging production: host: prod.eventbus:5672 protocol: amqp protocolVersion: 0.9.1 summary: RabbitMQ server for production environment description: RabbitMQ server for production environment security: - type: userPassword description: An authentication method for the server tags: - name: env:production description: >- Production environment configuration for production channels: notification-cancel-order: address: notification-cancel-order description: Event emitted when a user cancels an order messages: CancelOrderCommand.message: $ref: "#/components/messages/CancelOrderCommand" notification-complete-order: address: notification-complete-order description: Event emitted when a user completes an order messages: CompleteOrderCommand.message: $ref: "#/components/messages/CompleteOrderCommand" basket-checkout-complete: address: basket-checkout-complete description: Event emitted when a user completes a basket checkout messages: DeleteBasketCompleteCommand.message: $ref: "#/components/messages/DeleteBasketCompleteCommand" basket-checkout-failed: address: basket-checkout-failed description: Event emitted when a user fails to complete a basket checkout messages: DeleteBasketFailedCommand.message: $ref: "#/components/messages/DeleteBasketFailedCommand" basket-place-order: address: basket-place-order description: Event emitted when a user places an order messages: PlaceOrderCommand.message: $ref: "#/components/messages/PlaceOrderCommand" "BookWorm.Contracts:BasketDeletedCompleteIntegrationEvent": address: "BookWorm.Contracts:BasketDeletedCompleteIntegrationEvent" description: Event emitted when a basket is deleted and the order is complete messages: BasketDeletedCompleteIntegrationEvent.message: $ref: "#/components/messages/BasketDeletedCompleteIntegrationEvent" "BookWorm.Contracts:BasketDeletedFailedIntegrationEvent": address: "BookWorm.Contracts:BasketDeletedFailedIntegrationEvent" description: Event emitted when a basket is deleted and the order fails messages: BasketDeletedFailedIntegrationEvent.message: $ref: "#/components/messages/BasketDeletedFailedIntegrationEvent" "BookWorm.Contracts:OrderStatusChangedToCancelIntegrationEvent": address: "BookWorm.Contracts:OrderStatusChangedToCancelIntegrationEvent" description: Event emitted when the order status is changed to cancel messages: OrderStatusChangedToCancelIntegrationEvent.message: $ref: "#/components/messages/OrderStatusChangedToCancelIntegrationEvent" "BookWorm.Contracts:OrderStatusChangedToCompleteIntegrationEvent": address: "BookWorm.Contracts:OrderStatusChangedToCompleteIntegrationEvent" description: Event emitted when the order status is changed to complete messages: OrderStatusChangedToCompleteIntegrationEvent.message: $ref: "#/components/messages/OrderStatusChangedToCompleteIntegrationEvent" "BookWorm.Contracts:UserCheckedOutIntegrationEvent": address: "BookWorm.Contracts:UserCheckedOutIntegrationEvent" description: Event emitted when a user checks out messages: UserCheckedOutIntegrationEvent.message: $ref: "#/components/messages/UserCheckedOutIntegrationEvent" operations: CancelOrderCommand: title: Cancel Order summary: Cancel order notification description: Represents a successful integration event when cancelling an order in the system action: send channel: $ref: "#/channels/notification-cancel-order" CompleteOrderCommand: title: Complete Order summary: Complete order notification description: Represents a successful integration event when completing an order in the system action: send channel: $ref: "#/channels/notification-complete-order" DeleteBasketCompleteCommand: title: Delete Basket Complete summary: Delete basket complete description: Represents a successful integration event when deleting a basket in the system action: send channel: $ref: "#/channels/basket-checkout-complete" DeleteBasketFailedCommand: title: Delete Basket Failed summary: Delete basket failed description: Represents a failed integration event when deleting a basket in the system action: send channel: $ref: "#/channels/basket-checkout-failed" PlaceOrderCommand: title: Place Order summary: Place order notification description: Represents a successful integration event when placing an order in the system action: send channel: $ref: "#/channels/basket-place-order" BasketDeletedCompleteIntegrationEvent: title: Basket Deleted Complete summary: Basket deleted complete description: Represents a successful integration event when deleting a basket in the system action: receive channel: $ref: "#/channels/BookWorm.Contracts:BasketDeletedCompleteIntegrationEvent" BasketDeletedFailedIntegrationEvent: title: Basket Deleted Failed summary: Basket deleted failed description: Represents a failed integration event when deleting a basket in the system action: receive channel: $ref: "#/channels/BookWorm.Contracts:BasketDeletedFailedIntegrationEvent" OrderStatusChangedToCancelIntegrationEvent: title: Order Status Changed To Cancel summary: Order status changed to cancel description: Represents a successful integration event when the order status is changed to cancel in the system action: receive channel: $ref: >- #/channels/BookWorm.Contracts:OrderStatusChangedToCompleteIntegrationEvent UserCheckedOutIntegrationEvent: title: User Checked Out summary: User checked out description: Represents a successful integration event when a user checks out in the system action: receive channel: $ref: "#/channels/BookWorm.Contracts:UserCheckedOutIntegrationEvent" components: schemas: integrationEvent: id: integrationEvent type: object description: Base event structure containing common metadata for all integration events in the system additionalProperties: false properties: id: type: string format: guid description: The unique identifier of the integration event example: "01961eb4-668d-7e7e-ae25-0fab379614f7" creationDate: type: string format: date-time description: The creation date of the integration event example: "2021-01-01T00:00:00Z" CancelOrderCommand: id: CancelOrderCommand description: Event published when a user cancels an order, containing details about the order allOf: - $ref: "#/components/schemas/integrationEvent" - type: object additionalProperties: false properties: orderId: type: string format: guid description: The unique identifier of the order example: "01961eb4-668d-7e7e-ae25-0fab379614f7" basketId: type: string format: guid description: The unique identifier of the basket example: "01961eb4-668d-7e7e-ae25-0fab379614f7" email: type: string description: The email of the user pattern: "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$" example: "test@test.com" totalMoney: type: number format: double description: The total amount of money in the basket minimum: 0 example: 100.00 CompleteOrderCommand: id: CompleteOrderCommand description: Event published when a user completes an order, containing details about the order allOf: - $ref: "#/components/schemas/integrationEvent" - type: object additionalProperties: false properties: orderId: type: string format: guid description: The unique identifier of the order example: "01961eb4-668d-7e7e-ae25-0fab379614f7" email: type: string description: The email of the user pattern: "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$" example: "test@test.com" totalMoney: type: number format: double description: The total amount of money in the order example: 100.00 minimum: 0 DeleteBasketCompleteCommand: id: DeleteBasketCompleteCommand description: Event published when a basket is deleted and the order is complete, containing details about the order allOf: - $ref: "#/components/schemas/integrationEvent" - type: object additionalProperties: false properties: orderId: type: string format: guid description: The unique identifier of the order example: "01961eb4-668d-7e7e-ae25-0fab379614f7" totalMoney: type: number format: double description: The total amount of money in the order example: 100.00 minimum: 0 DeleteBasketFailedCommand: id: DeleteBasketFailedCommand description: Event published when a basket is deleted and the order fails, containing details about the order allOf: - $ref: "#/components/schemas/integrationEvent" - type: object additionalProperties: false properties: basketId: type: string format: guid description: The unique identifier of the basket example: "01961eb4-668d-7e7e-ae25-0fab379614f7" email: type: string description: The email of the user pattern: "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$" example: "test@test.com" orderId: type: string format: guid description: The unique identifier of the order example: "01961eb4-668d-7e7e-ae25-0fab379614f7" totalMoney: type: number format: double description: The total amount of money in the order example: 100.00 minimum: 0 PlaceOrderCommand: id: PlaceOrderCommand description: Event published when a user places an order, containing details about the order allOf: - $ref: "#/components/schemas/integrationEvent" - type: object additionalProperties: false properties: basketId: type: string format: guid description: The unique identifier of the basket example: "01961eb4-668d-7e7e-ae25-0fab379614f7" email: type: string description: The email of the user example: "test@test.com" pattern: "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$" orderId: type: string format: guid description: The unique identifier of the order example: "01961eb4-668d-7e7e-ae25-0fab379614f7" totalMoney: type: number format: double description: The total amount of money in the order example: 100.00 minimum: 0 BasketDeletedCompleteIntegrationEvent: id: BasketDeletedCompleteIntegrationEvent description: Event consumed when a basket is deleted and the order is complete, containing details about the order allOf: - $ref: "#/components/schemas/integrationEvent" - type: object additionalProperties: false properties: orderId: type: string format: guid description: The unique identifier of the order example: "01961eb4-668d-7e7e-ae25-0fab379614f7" basketId: type: string format: guid description: The unique identifier of the basket example: "01961eb4-668d-7e7e-ae25-0fab379614f7" totalMoney: type: number format: double description: The total amount of money in the order example: 100.00 minimum: 0 BasketDeletedFailedIntegrationEvent: id: BasketDeletedFailedIntegrationEvent description: Event consumed when a basket is deleted and the order fails, containing details about the order allOf: - $ref: "#/components/schemas/integrationEvent" - type: object additionalProperties: false properties: basketId: type: string format: guid description: The unique identifier of the basket example: "01961eb4-668d-7e7e-ae25-0fab379614f7" email: type: string description: The email of the user example: "test@test.com" pattern: "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$" orderId: type: string format: guid description: The unique identifier of the order example: "01961eb4-668d-7e7e-ae25-0fab379614f7" totalMoney: type: number format: double description: The total amount of money in the order example: 100.00 minimum: 0 OrderStatusChangedToCancelIntegrationEvent: id: OrderStatusChangedToCancelIntegrationEvent description: Event consumed when the order status is changed to cancel, containing details about the order allOf: - $ref: "#/components/schemas/integrationEvent" - type: object additionalProperties: false properties: basketId: type: string format: guid description: The unique identifier of the basket example: "01961eb4-668d-7e7e-ae25-0fab379614f7" email: type: string description: The email of the user pattern: "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$" example: "test@test.com" orderId: type: string format: guid description: The unique identifier of the order example: "01961eb4-668d-7e7e-ae25-0fab379614f7" totalMoney: type: number format: double description: The total amount of money in the order example: 100.00 minimum: 0 OrderStatusChangedToCompleteIntegrationEvent: id: OrderStatusChangedToCompleteIntegrationEvent description: Event consumed when the order status is changed to complete, containing details about the order allOf: - $ref: "#/components/schemas/integrationEvent" - type: object additionalProperties: false properties: basketId: type: string format: guid description: The unique identifier of the basket example: "01961eb4-668d-7e7e-ae25-0fab379614f7" email: type: string description: The email of the user example: "test@test.com" pattern: "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$" orderId: type: string format: guid description: The unique identifier of the order example: "01961eb4-668d-7e7e-ae25-0fab379614f7" totalMoney: type: number format: double description: The total amount of money in the order example: 100.00 minimum: 0 UserCheckedOutIntegrationEvent: id: UserCheckedOutIntegrationEvent description: Event consumed when a user checks out, containing details about the order allOf: - $ref: "#/components/schemas/integrationEvent" - type: object additionalProperties: false properties: basketId: type: string format: guid description: The unique identifier of the basket example: "01961eb4-668d-7e7e-ae25-0fab379614f7" email: type: string description: The email of the user example: "test@test.com" pattern: "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$" orderId: type: string format: guid description: The unique identifier of the order example: "01961eb4-668d-7e7e-ae25-0fab379614f7" totalMoney: type: number format: double description: The total amount of money in the order example: 100.00 minimum: 0 messages: CancelOrderCommand: payload: $ref: "#/components/schemas/CancelOrderCommand" name: CancelOrderCommand CompleteOrderCommand: payload: $ref: "#/components/schemas/CompleteOrderCommand" name: CompleteOrderCommand DeleteBasketCompleteCommand: payload: $ref: "#/components/schemas/DeleteBasketCompleteCommand" name: DeleteBasketCompleteCommand DeleteBasketFailedCommand: payload: $ref: "#/components/schemas/DeleteBasketFailedCommand" name: DeleteBasketFailedCommand PlaceOrderCommand: payload: $ref: "#/components/schemas/PlaceOrderCommand" name: PlaceOrderCommand BasketDeletedCompleteIntegrationEvent: payload: $ref: "#/components/schemas/BasketDeletedCompleteIntegrationEvent" name: BasketDeletedCompleteIntegrationEvent BasketDeletedFailedIntegrationEvent: payload: $ref: "#/components/schemas/BasketDeletedFailedIntegrationEvent" name: BasketDeletedFailedIntegrationEvent OrderStatusChangedToCancelIntegrationEvent: payload: $ref: "#/components/schemas/OrderStatusChangedToCancelIntegrationEvent" name: OrderStatusChangedToCancelIntegrationEvent OrderStatusChangedToCompleteIntegrationEvent: payload: $ref: "#/components/schemas/OrderStatusChangedToCompleteIntegrationEvent" name: OrderStatusChangedToCompleteIntegrationEvent UserCheckedOutIntegrationEvent: payload: $ref: "#/components/schemas/UserCheckedOutIntegrationEvent" name: UserCheckedOutIntegrationEvent --- id: MCPTools name: MCP Tools version: 1.0.0 summary: Model Context Protocol (MCP) server providing AI-powered tools and components for managing BookWorm catalog data and operations badges: - content: MCP Server textColor: purple backgroundColor: purple icon: WrenchScrewdriverIcon - content: JSON-RPC textColor: blue backgroundColor: blue icon: CodeBracketIcon - content: Server-Sent Events textColor: orange backgroundColor: orange icon: RadioIcon - content: AI-Powered textColor: green backgroundColor: green icon: CpuChipIcon sends: - id: ListBooksQuery version: 1.0.0 receives: - id: GetMcpComponentsQuery version: 1.0.0 schemaPath: openapi-v1.yml specifications: - type: openapi path: openapi-v1.yml name: OpenAPI V1 owners: - nhanxnguyen repository: language: C# url: https://github.com/foxminchan/BookWorm attachments: - url: https://foxminchan.github.io/BookWorm/docs/architecture-decisions/adr-020-mcp-tools-standardize title: ADR-020 - MCP Tools Standardization description: Learn more about the standardization of tools within the MCP ecosystem. type: "architecture-decisions" icon: FileTextIcon --- ## Overview The MCP Tools Service is a specialized integration service within the BookWorm ecosystem that implements the Model Context Protocol (MCP) server specification. This service serves as a bridge between AI-powered applications and the BookWorm catalog system, providing structured access to book data and catalog operations through a standardized protocol. Built following our [microservices architecture](https://foxminchan.github.io/BookWorm/docs/architecture-decisions/adr-001-microservices-architecture) principles, this service encapsulates the complexities of AI tool integration while maintaining clean boundaries between the Integration subdomain and other bounded contexts. ### Key Responsibilities - **MCP Protocol Implementation**: HTTP-based MCP server using ModelContextProtocol.AspNetCore - **Tool Registry Management**: Auto-discovery of MCP tools from assembly attributes - **Catalog Integration**: HTTP API integration with BookWorm Catalog Service - **AI Assistant Support**: Pre-defined prompts for customer service interactions - **OpenAPI Documentation**: Self-documenting API with MCP-specific extensions ### MCP Protocol Features The service implements the Model Context Protocol with the following capabilities: - **Tools**: Exposes BookWorm catalog search operations as MCP tools - **Prompts**: Pre-defined prompts for AI customer service interactions - **HTTP Transport**: Stateless HTTP transport for MCP communication - **OpenAPI Integration**: Self-documenting API with MCP extensions ### Domain Model The MCP Tools service is structured around the following key domain models: - **Book**: Core entity with properties like Id, Name, Description, Price, Category, Publisher, Authors, and ratings - **Author**: Entity representing book authors - **Category**: Entity for book categorization - **Publisher**: Entity for book publishers - **MCP Tool**: Attribute-decorated methods exposed as MCP tools - **MCP Prompt**: Pre-defined AI conversation templates ### Available Tools The service exposes the following tools for BookWorm integration: | Tool Name | Description | Capabilities | | --------------- | ---------------------------------------- | --------------------------------- | | `SearchCatalog` | Search for books in the BookWorm catalog | Hybrid search by book description | ### Available Prompts The service provides pre-defined prompts for AI interactions: | Prompt Name | Description | Purpose | | -------------- | ------------------------------------------ | ------------------------------------------ | | `SystemPrompt` | BookWorm customer service assistant prompt | Provides AI assistant context and behavior | ## Core Features | Feature | Description | | ------------------------ | ------------------------------------------------------------------ | | **Tool Discovery** | Auto-discovery of MCP tools via assembly attributes and reflection | | **Catalog Search** | Full-text search of BookWorm catalog by book description | | **AI Assistant Prompts** | Pre-defined system prompts for customer service AI interactions | | **HTTP Transport** | Stateless HTTP transport following MCP protocol specification | | **OpenAPI Integration** | Self-documenting API with MCP-specific extensions | | **Model Serialization** | Efficient JSON serialization with source generators | ## Architecture Diagram ### External Dependencies The MCP Tools Service integrates with: - **Catalog Service**: Primary data source for book information via HTTP/REST API - **AI Clients**: External applications that consume MCP tools (Claude, OpenAI assistants, etc.) - **BookWorm Service Defaults**: Shared infrastructure components and configurations - **OpenTelemetry**: For distributed tracing and metrics collection ### Technology Stack - **.NET 9**: Core runtime and web framework - **ModelContextProtocol.AspNetCore**: MCP server implementation library - **Refit**: Type-safe HTTP client for Catalog service integration - **Source Generators**: Efficient JSON serialization - **OpenAPI**: API documentation and specification with MCP extensions - **Docker**: Containerization and deployment ## Integration Patterns The service follows several key integration patterns: - **Attribute-Based Discovery**: Uses .NET attributes to automatically register MCP tools and prompts - **HTTP Client Pattern**: Leverages Refit for type-safe HTTP communication with Catalog service - **Stateless Design**: HTTP transport is configured as stateless for better scalability - **Source Generation**: Uses JSON source generators for efficient serialization - **Health Checks**: Integrates with ASP.NET Core health check system - **Observability**: Built-in OpenTelemetry integration for metrics and tracing ## Raw Schema:openapi-v1.yml --- openapi: 3.1.1 info: title: MCP Tools Server 1.0 description: Provides tools for managing and interacting with BookWorm catalog data, including book search and catalog operations contact: name: Nhan Nguyen url: https://github.com/foxminchan email: nguyenxuannhan407@gmail.com license: name: MIT url: https://opensource.org/licenses/MIT version: "1.0" externalDocs: description: Documentation url: https://github.com/foxminchan/BookWorm servers: - url: "{protocol}://{environment}.mcp.bookworm.com" description: Non-production environment variables: protocol: enum: - https - http default: https environment: enum: - dev - stg - qa default: dev - url: "{protocol}://mcp.bookworm.com" description: Production environment variables: protocol: enum: - https default: https paths: /mcp: post: tags: - MCP summary: Get MCP Components description: | Invoke MCP methods via JSON-RPC over HTTP. Responses are streamed using **Server-Sent Events** (`text/event-stream`) following the MCP streamable protocol (`mcp-streamable-1.0`). operationId: InvokeMCP requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/JsonRpcRequest" example: jsonrpc: "2.0" method: "tools/list" params: name: "SearchCatalog" arguments: description: "science fiction" _meta: progressToken: 0 id: 1 "text/event-stream": schema: $ref: "#/components/schemas/JsonRpcRequest" example: jsonrpc: "2.0" method: "tools/list" params: name: "SearchCatalog" arguments: description: "science fiction" _meta: progressToken: 0 id: 1 responses: "200": description: Successful stream response content: text/event-stream: schema: $ref: "#/components/schemas/JsonRpcResponse" example: jsonrpc: "2.0" result: content: content: type: "text" value: '{"books":[{"title":"Dune","author":"Frank Herbert"},{"title":"Neuromancer","author":"William Gibson"}]}' id: 1 "400": description: Bad Request content: application/json: schema: $ref: "#/components/schemas/JsonRpcError" examples: invalid-json: summary: Invalid JSON value: error: code: 400 message: "Bad Request: The POST body did not contain a valid JSON-RPC message." id: 1 jsonrpc: "2.0" missing-method: summary: Session-Id not supported value: error: code: 400 message: "Bad Request: The Mcp-Session-Id header is not supported in stateless mode" id: 1 jsonrpc: "2.0" "406": description: Not Acceptable content: application/json: schema: $ref: "#/components/schemas/JsonRpcError" examples: not-acceptable: summary: Not Acceptable value: error: code: 406 message: "Not Acceptable: Client must accept both application/json and text/event-stream" id: 1 jsonrpc: "2.0" "403": description: Forbidden content: application/json: schema: $ref: "#/components/schemas/JsonRpcError" examples: forbidden: summary: Forbidden value: error: code: 403 message: "Forbidden: The currently authenticated user does not match the user who initiated the session." id: 1 jsonrpc: "2.0" x-ms-agentic-protocol: mcp-streamable-1.0 components: schemas: JsonRpcError: readOnly: true x-scalar-ignore: true description: Error object (https://www.jsonrpc.org/specification#error_object) required: - error type: object properties: error: required: - code - message type: object properties: code: pattern: ^-?(?:0|[1-9]\d*)$ type: - integer - string format: int32 description: Error code indicating the type of error example: 400 message: type: string description: Human-readable error message example: "Bad Request: The POST body did not contain a valid JSON-RPC message." data: type: - object - "null" description: Optional additional error data id: type: - integer - string - "null" description: Each ID is expected to be unique within the context of a given session. example: fc54a698-b469-4b42-bc3e-f45bcb13d707 jsonrpc: type: string description: JSON-RPC version (e.g. "2.0") example: "2.0" JsonRpcRequest: required: - method properties: method: type: string description: Name of the MCP method to invoke example: "tools/list" params: type: object description: Method parameters example: name: "SearchCatalog" arguments: description: "science fiction" _meta: progressToken: 0 id: type: - integer - string - "null" description: Each ID is expected to be unique within the context of a given session. example: fc54a698-b469-4b42-bc3e-f45bcb13d707 jsonrpc: type: string description: JSON-RPC version (e.g. "2.0") example: "2.0" JsonRpcResponse: required: - result type: object properties: result: type: object description: Method result example: content: content: type: "text" value: '{"books":[{"title":"Dune","author":"Frank Herbert"},{"title":"Neuromancer","author":"William Gibson"}]}' id: type: - integer - string - "null" description: Each ID is expected to be unique within the context of a given session. example: fc54a698-b469-4b42-bc3e-f45bcb13d707 jsonrpc: type: string description: JSON-RPC version (e.g. "2.0") example: "2.0" tags: - name: MCP description: MCP Tools Server API --- id: NotificationService name: Notification Service version: 1.0.0 summary: Transactional Email Service for BookWorm badges: - content: Event Consumer textColor: blue backgroundColor: blue icon: ArrowPathIcon - content: Email Delivery textColor: green backgroundColor: green icon: EnvelopeIcon - content: Resilient textColor: orange backgroundColor: orange icon: WrenchScrewdriverIcon sends: [] receives: - id: CancelOrderCommand version: 1.0.0 from: - id: "notification.{env}.events" version: 1.0.0 parameters: env: stg - id: "notification.{env}.events" version: 1.0.0 parameters: env: stg - id: CompleteOrderCommand version: 1.0.0 from: - id: "notification.{env}.events" version: 1.0.0 parameters: env: stg - id: "notification.{env}.events" version: 1.0.0 parameters: env: stg - id: PlaceOrderCommand version: 1.0.0 from: - id: "notification.{env}.events" version: 1.0.0 parameters: env: stg - id: "basket.{env}.events" version: 1.0.0 parameters: env: stg - id: "basket.{env}.events" version: 1.0.0 parameters: env: stg - id: CleanUpSentEmailIntegrationEvent version: 1.0.0 from: - &ref_0 id: "notification.{env}.events" version: 1.0.0 parameters: env: stg - *ref_0 - id: ResendErrorEmailIntegrationEvent version: 1.0.0 from: - &ref_1 id: "notification.{env}.events" version: 1.0.0 parameters: env: stg - *ref_1 schemaPath: asyncapi-v1.yml specifications: - type: asyncapi path: asyncapi-v1.yml name: AsyncAPI V1 owners: - nhanxnguyen repository: language: C# url: "https://github.com/foxminchan/BookWorm" writesTo: - id: NotificationDatabase version: 1.0.0 readsFrom: - id: NotificationDatabase version: 1.0.0 attachments: - url: >- https://foxminchan.github.io/BookWorm/docs/architecture-decisions/adr-011-rabbitmq-message title: ADR-011 - RabbitMQ for Message Brokering description: Learn more about the RabbitMQ choice for message brokering in BookWorm. type: architecture-decisions icon: FileTextIcon - url: >- https://foxminchan.github.io/BookWorm/docs/architecture-decisions/adr-014-sendgrid-email-provider title: ADR-014 - SendGrid Email Provider description: >- Learn more about the SendGrid email provider choice for the Notification Service. type: architecture-decisions icon: FileTextIcon --- ## Overview The Notification Service is a supporting service within BookWorm's [microservices architecture](https://foxminchan.github.io/BookWorm/architecture-decisions/adr-001-microservices-architecture), responsible for sending transactional emails to customers at critical points in the order lifecycle. Built using our [event-driven approach](https://foxminchan.github.io/BookWorm/architecture-decisions/adr-002-event-driven-cqrs), this service listens for integration events from other domains and transforms them into appropriate customer communications. ### Key Responsibilities - **Event Processing**: Consumes order-related events from the message bus - **Email Composition**: Transforms events into well-formatted email messages - **Template Management**: Maintains and renders email templates with dynamic content - **Delivery Orchestration**: Manages email delivery through multiple providers - **Retry & Resilience**: Implements robust retry mechanisms for failed deliveries - **Audit Trail**: Maintains comprehensive logs of all notification attempts ## Component Diagram ```mermaid C4Component title Component diagram for Notification Service Container_Boundary(notification, "Notification Service") { Container_Boundary(application, "Application") { Component(smtpClient, "SMTP Client", ".NET", "Email delivery abstraction") Component(emailService, "Email Service", ".NET", "Handles email composition and delivery") Component(eventHandler, "Event Handler", ".NET", "Processes incoming events") Component(templateEngine, "Template Engine", ".NET", "Manages email templates") } Container_Boundary(infrastructure, "Infrastructure") { ComponentDb(notificationDb, "Notification DB", "Azure Table Storage", "Stores notification records") ComponentQueue(eventBus, "Event Bus", "RabbitMQ", "Handles async communication") } } System_Ext(sendGrid, "SendGrid", "Production email provider") Rel(emailService, templateEngine, "Uses", "Internal") Rel(emailService, smtpClient, "Uses", "Internal") Rel(emailService, notificationDb, "Reads/Writes", "TCP") Rel(eventBus, eventHandler, "Events", "Async") Rel(eventHandler, emailService, "Uses", "Internal") Rel(emailService, templateEngine, "Uses", "Internal") Rel(emailService, smtpClient, "Uses", "Internal") Rel(smtpClient, sendGrid, "Sends emails", "SMTP/API") UpdateLayoutConfig($c4ShapeInRow="3", $c4BoundaryInRow="1") ``` ## Architecture diagram ## Message Flow The Notification Service processes the following commands: | Command | Channel | Purpose | SLA | | ---------------------------------- | -------------------------------- | ------------------------------------ | -------- | | `PlaceOrderCommand` | notification-place-order | Sends order confirmation emails | < 5 min | | `CompleteOrderCommand` | notification-complete-order | Sends order completion notifications | < 10 min | | `CancelOrderCommand` | notification-cancel-order | Delivers order cancellation notices | < 5 min | | `CleanUpSentEmailIntegrationEvent` | notification-clean-up-sent-email | Cleans up sent email records | < 15 min | | `ResendErrorEmailIntegrationEvent` | notification-resend-error-email | Resends failed email deliveries | < 10 min | ### Event Processing Pipeline ```mermaid stateDiagram-v2 [*] --> EventReceived: Order Event EventReceived --> ValidateEvent: Parse & Validate ValidateEvent --> LoadTemplate: Valid Event ValidateEvent --> ErrorHandling: Invalid Event LoadTemplate --> ComposeEmail: Template Found LoadTemplate --> ErrorHandling: Template Missing ComposeEmail --> SendEmail: Email Ready SendEmail --> DeliverySuccess: Sent SendEmail --> RetryQueue: Failed RetryQueue --> SendEmail: Retry Attempt RetryQueue --> DeadLetter: Max Retries DeliverySuccess --> OutBox: Log Success DeadLetter --> OutBox: Log Failure OutBox --> [*] ErrorHandling --> OutBox: Log Error ``` ## Email Templates Each notification type follows a standardized template structure: - **Order Confirmation**: "Your order has been placed successfully." - **Order Completion**: "Your order has been completed successfully." - **Order Cancellation**: "Your order has been cancelled." ## Infrastructure The Notification Service is deployed on Microsoft Azure, leveraging Azure Service Bus for message consumption and Azure Monitor for observability. For `Development` environment, the service uses MailPit for email delivery. For `Production` environment, the service uses SendGrid for email delivery. ```mermaid architecture-beta group prod[Production Environment] group dev[Development Environment] service app(logos:docker)[Notification Service] in prod service sg(logos:sendgrid)[SendGrid API] in prod service bus(logos:rabbitmq)[RabbitMQ] in prod service storage(logos:microsoft-azure)[Table Storage] in prod service monitor(logos:microsoft-azure)[Azure Monitor] in prod service appDev(logos:docker)[Notification Service] in dev service mail(logos:docker)[MailPit SMTP] in dev service busDev(logos:rabbitmq)[RabbitMQ] in dev app:R --> L:sg bus:R --> L:app app:B --> T:storage app:L --> R:monitor busDev:R --> L:appDev appDev:R --> L:mail ``` ## Security Considerations - **PII Protection**: Customer data is encrypted at rest and in transit - **API Key Management**: SendGrid API keys stored in Azure Key Vault - **Rate Limiting**: Protection against notification spam - **Content Sanitization**: HTML content is sanitized to prevent XSS - **Audit Compliance**: GDPR-compliant data retention policies ## Performance Optimizations - **Async Processing**: All operations are fully asynchronous - **Batch Processing**: Groups multiple notifications when possible - **Template Caching**: Compiled templates cached in memory - **Connection Pooling**: Reuses SMTP/HTTP connections - **Parallel Delivery**: Concurrent email sending with throttling ## Raw Schema:asyncapi-v1.yml --- asyncapi: 3.0.0 info: title: Notification Service API version: 1.0.0 description: Transactional Email Service for BookWorm contact: name: Nhan Nguyen url: "https://github.com/foxminchan" license: name: MIT url: "https://opensource.org/licenses/MIT" defaultContentType: application/vnd.masstransit+json servers: development: host: dev.eventbus:5672 protocol: amqp protocolVersion: 0.9.1 summary: RabbitMQ server for development environment description: RabbitMQ server for development environment security: - type: userPassword description: An authentication method for the server tags: - name: env:development description: >- Development environment configuration for local testing and debugging staging: host: stg.eventbus:5672 protocol: amqp protocolVersion: 0.9.1 summary: RabbitMQ server for staging environment description: RabbitMQ server for staging environment security: - type: userPassword description: An authentication method for the server tags: - name: env:staging description: >- Staging environment configuration for testing and debugging qa: host: qa.eventbus:5672 protocol: amqp protocolVersion: 0.9.1 summary: RabbitMQ server for QA environment description: RabbitMQ server for QA environment security: - type: userPassword description: An authentication method for the server tags: - name: env:qa description: >- QA environment configuration for testing and debugging production: host: prod.eventbus:5672 protocol: amqp protocolVersion: 0.9.1 summary: RabbitMQ server for production environment description: RabbitMQ server for production environment security: - type: userPassword description: An authentication method for the server tags: - name: env:production description: >- Production environment configuration for production channels: notification-cancel-order: address: notification-cancel-order description: Event emitted when a user cancels an order messages: CancelOrderCommand.message: $ref: "#/components/messages/cancelOrderCommand" notification-complete-order: address: notification-complete-order description: Event emitted when a user completes an order messages: CompleteOrderCommand.message: $ref: "#/components/messages/completeOrderCommand" notification-place-order: address: notification-place-order description: Event emitted when a user places an order messages: PlaceOrderCommand.message: $ref: "#/components/messages/placeOrderCommand" notification-clean-up-sent-email: address: notification-clean-up-sent-email description: Event emitted to trigger cleanup of sent emails for system maintenance messages: CleanUpSentEmailIntegrationEvent.message: $ref: "#/components/messages/cleanUpSentEmailIntegrationEvent" notification-resend-error-email: address: notification-resend-error-email description: Event emitted to trigger resending of failed emails messages: ResendErrorEmailIntegrationEvent.message: $ref: "#/components/messages/resendErrorEmailIntegrationEvent" operations: CancelOrderCommand: title: Cancel order notification summary: Cancel order notification description: Represents a successful integration event when a user cancels an order action: receive channel: $ref: "#/channels/notification-cancel-order" messages: - $ref: >- #/channels/notification-cancel-order/messages/CancelOrderCommand.message CompleteOrderCommand: title: Complete order notification summary: Complete order notification description: Represents a successful integration event when a user completes an order action: receive channel: $ref: "#/channels/notification-complete-order" messages: - $ref: >- #/channels/notification-complete-order/messages/CompleteOrderCommand.message PlaceOrderCommand: title: Place order notification summary: Place order notification description: Represents a successful integration event when a user places an order action: receive channel: $ref: "#/channels/notification-place-order" messages: - $ref: "#/channels/notification-place-order/messages/PlaceOrderCommand.message" CleanUpSentEmailIntegrationEvent: title: Clean up sent email notification summary: Clean up sent email notification description: Represents an integration event to trigger cleanup of sent emails for system maintenance action: receive channel: $ref: "#/channels/notification-clean-up-sent-email" messages: - $ref: "#/channels/notification-clean-up-sent-email/messages/CleanUpSentEmailIntegrationEvent.message" ResendErrorEmailIntegrationEvent: title: Resend error email notification summary: Resend error email notification description: Represents an integration event to trigger resending of failed emails action: receive channel: $ref: "#/channels/notification-resend-error-email" messages: - $ref: "#/channels/notification-resend-error-email/messages/ResendErrorEmailIntegrationEvent.message" components: schemas: cancelOrderCommand: id: cancelOrderCommand description: Represents a successful integration event when a user cancels an order allOf: - $ref: "#/components/schemas/integrationEvent" - type: object additionalProperties: false properties: orderId: type: string format: guid description: The unique identifier of the order example: "01961eb4-668d-7e7e-ae25-0fab379614f7" email: type: - "null" - string pattern: "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$" description: The email of the user example: "test@test.com" totalMoney: type: number format: decimal description: The total amount of money in the order minimum: 0 example: 100.00 integrationEvent: id: integrationEvent type: object description: Base event structure containing common metadata for all integration events in the system additionalProperties: false properties: id: type: string format: guid description: The unique identifier of the integration event example: "01961eb4-668d-7e7e-ae25-0fab379614f7" creationDate: type: string format: date-time description: The creation date of the integration event example: "2021-01-01T00:00:00Z" completeOrderCommand: id: completeOrderCommand description: Represents a successful integration event when a user completes an order allOf: - $ref: "#/components/schemas/integrationEvent" - type: object additionalProperties: false properties: orderId: type: string format: guid description: The unique identifier of the order example: "01961eb4-668d-7e7e-ae25-0fab379614f7" email: type: - "null" - string pattern: "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$" description: The email of the user example: "test@test.com" totalMoney: type: number format: decimal description: The total amount of money in the order minimum: 0 example: 100.00 placeOrderCommand: id: placeOrderCommand description: Represents a successful integration event when a user places an order allOf: - $ref: "#/components/schemas/integrationEvent" - type: object additionalProperties: false properties: basketId: type: string format: guid description: The unique identifier of the basket example: "01961eb4-668d-7e7e-ae25-0fab379614f7" email: type: - "null" - string pattern: "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$" description: The email of the user example: "test@test.com" orderId: type: string format: guid description: The unique identifier of the order example: "01961eb4-668d-7e7e-ae25-0fab379614f7" totalMoney: type: number format: decimal description: The total amount of money in the basket minimum: 0 example: 100.00 cleanUpSentEmailIntegrationEvent: id: cleanUpSentEmailIntegrationEvent description: Represents an integration event to trigger cleanup of sent emails for system maintenance allOf: - $ref: "#/components/schemas/integrationEvent" - type: object additionalProperties: false resendErrorEmailIntegrationEvent: id: resendErrorEmailIntegrationEvent description: Represents an integration event to trigger resending of failed emails allOf: - $ref: "#/components/schemas/integrationEvent" - type: object additionalProperties: false messages: cancelOrderCommand: payload: $ref: "#/components/schemas/cancelOrderCommand" name: cancelOrderCommand completeOrderCommand: payload: $ref: "#/components/schemas/completeOrderCommand" name: completeOrderCommand placeOrderCommand: payload: $ref: "#/components/schemas/placeOrderCommand" name: placeOrderCommand cleanUpSentEmailIntegrationEvent: payload: $ref: "#/components/schemas/cleanUpSentEmailIntegrationEvent" name: cleanUpSentEmailIntegrationEvent resendErrorEmailIntegrationEvent: payload: $ref: "#/components/schemas/resendErrorEmailIntegrationEvent" name: resendErrorEmailIntegrationEvent --- id: OrderingService name: Ordering Service version: 1.0.0 summary: >- Orchestrates the entire order lifecycle, from creation to completion or cancellation, for the BookWorm platform badges: - content: Event-Driven textColor: purple backgroundColor: purple icon: BoltIcon - content: Event Store textColor: green backgroundColor: green icon: CubeTransparentIcon - content: CQRS textColor: blue backgroundColor: blue icon: CommandLineIcon - content: Domain-Driven textColor: orange backgroundColor: orange icon: CubeIcon sends: - id: UserCheckedOutEvent version: 1.0.0 to: - id: "finance.{env}.events" version: 1.0.0 parameters: env: stg - id: "finance.{env}.events" version: 1.0.0 parameters: env: stg - id: OrderStatusChangedToCompleteEvent version: 1.0.0 to: - id: "finance.{env}.events" version: 1.0.0 parameters: env: stg - id: "finance.{env}.events" version: 1.0.0 parameters: env: stg - id: OrderStatusChangedToCancelEvent version: 1.0.0 to: - id: "finance.{env}.events" version: 1.0.0 parameters: env: stg - id: "finance.{env}.events" version: 1.0.0 parameters: env: stg - id: ListBooksGrpc version: 1.0.0 to: - id: "catalog.{env}.events" version: 1.0.0 parameters: env: stg - id: GetBasketGrpc version: 1.0.0 to: - id: "basket.{env}.events" version: 1.0.0 parameters: env: stg receives: - id: CompleteOrderCommand version: 1.0.0 from: - id: "notification.{env}.events" version: 1.0.0 parameters: env: stg - id: "notification.{env}.events" version: 1.0.0 parameters: env: stg - id: CancelOrderCommand version: 1.0.0 from: - id: "notification.{env}.events" version: 1.0.0 parameters: env: stg - id: "notification.{env}.events" version: 1.0.0 parameters: env: stg - id: OrderStream version: 1.0.0 from: - id: "ordering.{env}.events" version: 1.0.0 parameters: env: stg - id: DeleteBasketCompleteCommand version: 1.0.0 from: - id: "ordering.{env}.events" version: 1.0.0 parameters: env: stg - id: "ordering.{env}.events" version: 1.0.0 parameters: env: stg - id: DeleteBasketFailedCommand version: 1.0.0 from: - id: "ordering.{env}.events" version: 1.0.0 parameters: env: stg - id: "ordering.{env}.events" version: 1.0.0 parameters: env: stg schemaPath: openapi-v1.yml specifications: - type: openapi path: openapi-v1.yml name: OpenAPI V1 - type: asyncapi path: asyncapi-v1.yml name: AsyncAPI V1 owners: - nhanxnguyen repository: language: C# url: "https://github.com/foxminchan/BookWorm" writesTo: - id: OrderingDatabase version: 1.0.0 - id: OrderingCache version: 1.0.0 readsFrom: - id: OrderingDatabase version: 1.0.0 - id: OrderingCache version: 1.0.0 attachments: - url: >- https://foxminchan.github.io/BookWorm/docs/architecture-decisions/adr-004-postgresql-database title: ADR-004 - PostgreSQL Database description: Learn more about the PostgreSQL database choice for the BookWorm platform. type: architecture-decisions icon: FileTextIcon - url: >- https://foxminchan.github.io/BookWorm/docs/architecture-decisions/adr-013-ordering-eventsoucring title: ADR-013 - Event Sourcing for Ordering Service description: >- Learn more about the Event Sourcing pattern implemented in the Ordering Service. type: architecture-decisions icon: FileTextIcon --- ## Overview The Ordering Service is a core bounded context in the BookWorm ecosystem responsible for managing the complete order lifecycle. Implemented using Domain-Driven Design principles, this service encapsulates all business logic related to order processing, from creation to fulfillment or cancellation. ### Key Responsibilities - **Order Lifecycle Management**: Complete order processing from placement to completion - **Buyer Management**: Customer profile and address management - **Status Orchestration**: State machine implementation for order transitions - **Event Sourcing**: Maintains complete audit trail of all order changes ## Component Diagram ```mermaid C4Component title Component diagram for Order Service Container_Boundary(order, "Order Service") { Container_Boundary(application, "Application") { Component(orderEndpoint, "Order Endpoints", ".NET", "Manages order operations") Component(orderFacade, "Order Facade", ".NET", "Core business logic for order management") Component(eventPublisher, "Event Publisher", ".NET", "Publishes order events") } Container_Boundary(infrastructure, "Infrastructure") { ComponentDb(orderDb, "Order DB", "PostgreSQL", "Stores order information and history") ComponentQueue(eventBus, "Event Bus", "RabbitMQ", "Handles async communication") } } Rel(orderEndpoint, orderFacade, "Uses", "Internal") BiRel(orderFacade, eventPublisher, "Uses", "Internal") Rel(orderFacade, orderDb, "Reads/Writes", "SQL") BiRel(eventPublisher, eventBus, "Publishes/Subscribes", "Async") UpdateLayoutConfig($c4ShapeInRow="3", $c4BoundaryInRow="1") ``` ### Domain Model The service is structured around the Order aggregate root, which enforces invariants and business rules across the entire ordering process. Key components include: - **Order Aggregate**: Central entity that maintains order consistency and contains: - Order Items (entities) - Order Status (enumeration value object) - **Buyer Aggregate**: Represents customer information with: - Personal Details (entity) - Default Address (value object) ### Patterns & Implementation - **CQRS Pattern**: Separates command and query responsibilities - **Repository Pattern**: Provides persistence abstraction for aggregates - **Specification Pattern**: Encapsulates business rules for order validation - **State Pattern**: Manages transitions between order statuses ### External Dependencies The Ordering Service interacts with: - **Basket Service**: Consumes basket data for order creation - **Catalog Service**: Validates product availability - **Notification Service**: Triggers customer notifications on status changes ## Core Features | Feature | Description | | ---------------------- | --------------------------------------------------------------------------- | | **Create Order** | Allows customers to create new orders with multiple items | | **Update Order** | Supports order status updates, including completion or cancellation | | **View Order** | Provides detailed order information, including status and items | | **Manage Buyer** | Enables customer profile management, including address and personal details | | **Delete Buyer** | Allows customers to remove their account and associated orders | | **View Buyer Profile** | Displays customer information and order history | ## Architecture diagram ## Infrastructure The Ordering Service is deployed as a set of microservices within the BookWorm ecosystem. It leverages cloud-native technologies and follows best practices for scalability, reliability, and performance. ```mermaid architecture-beta group api(logos:microsoft-azure)[API] service db(logos:postgresql)[Database] in api service server(logos:docker-icon)[Server] in api service cache(logos:redis)[Idempotency Store] in api db:L -- R:server server:L -- R:cache ``` ## Raw Schema:openapi-v1.yml --- openapi: 3.1.1 info: title: Ordering Service API description: Orchestrates the entire order lifecycle, from creation to completion or cancellation, for the BookWorm platform contact: name: Nhan Nguyen url: https://github.com/foxminchan email: nguyenxuannhan407@gmail.com license: name: MIT url: https://opensource.org/licenses/MIT version: "1.0" externalDocs: description: Documentation url: https://github.com/foxminchan/BookWorm servers: - url: "{protocol}://{environment}.ordering.bookworm.com" description: Non-production environment variables: protocol: enum: - https - http default: https environment: enum: - dev - stg - qa default: dev - url: "{protocol}://ordering.bookworm.com" description: Production variables: protocol: enum: - https default: https security: - OAuth: - ordering_read - ordering_write paths: "/api/v1/orders/{id}/summary": get: tags: - Order summary: Get Order Summary description: Get an order summary if it exists x-eventcatalog-message-type: query operationId: SummaryOrderEndpoint security: - OAuth: - ordering_read - ordering_write parameters: - name: id in: path description: The unique identifier of the order to be retrieved required: true schema: type: string format: uuid description: The unique identifier of the order to be retrieved example: "019622cf-6bd5-7499-bc18-31f71acbad44" responses: "200": description: OK content: application/json: schema: "$ref": "#/components/schemas/OrderSummaryInfo" example: id: "019622cf-6bd5-7499-bc18-31f71acbad44" status: "New" totalPrice: 100.00 "401": description: Unauthorized - Access token is missing or invalid. "/api/v1/orders": get: tags: - Order summary: List Orders description: List orders with pagination and filtering x-eventcatalog-message-type: query operationId: ListOrdersEndpoint security: - OAuth: - ordering_read - ordering_write parameters: - name: PageIndex in: query description: Number of items to return in a single page of results schema: type: integer format: int32 default: 1 description: The page index to return minimum: 1 example: 1 - name: PageSize in: query description: Number of items to return in a single page of results schema: type: integer format: int32 default: 20 description: The page size to return minimum: 1 example: 20 - name: Status in: query description: Status to filter results by schema: "$ref": "#/components/schemas/NullableOfStatus" - name: BuyerId in: query description: Buyer ID to filter results by schema: type: - string - "null" format: uuid default: null description: The unique identifier of the buyer to be retrieved example: "019622a3-a3ae-7705-a11b-a08f2a2ca621" responses: "200": description: OK content: application/json: schema: "$ref": "#/components/schemas/PagedResultOfOrderDto" examples: orders-empty: summary: No orders found value: items: [] pageIndex: 1 pageSize: 20 totalItems: 0 totalPages: 0 hasPreviousPage: false hasNextPage: false orders-single: summary: Single order value: items: - id: "019622cf-6bd5-7499-bc18-31f71acbad44" date: "2021-01-01T00:00:00Z" status: "New" total: 100.00 pageIndex: 1 pageSize: 20 totalItems: 1 totalPages: 1 hasPreviousPage: false hasNextPage: false orders-multiple: summary: Multiple orders value: items: - id: "019622cf-6bd5-7499-bc18-31f71acbad44" date: "2021-01-01T00:00:00Z" status: "New" total: 100.00 - id: "019622d1-3613-737e-8d58-af7db6317153" date: "2021-01-02T00:00:00Z" status: "Completed" total: 200.00 pageIndex: 1 pageSize: 20 totalItems: 2 totalPages: 1 hasPreviousPage: false hasNextPage: false "400": description: Bad Request content: application/problem+json: schema: "$ref": "#/components/schemas/HttpValidationProblemDetails" examples: invalid-page-index: summary: Invalid page index value: type: "https://tools.ietf.org/html/rfc9110#section-15.5.1" title: "Validation failed" status: 400 detail: "One or more validation errors has occurred" errors: - propertyName: "'PageIndex'" errorMessage: "'PageIndex' must be greater than 0." attemptedValue: null customState: null severity: 0 errorCode: null formattedMessagePlaceholderValues: null invalid-page-size: summary: Invalid page size value: type: "https://tools.ietf.org/html/rfc9110#section-15.5.1" title: "Validation failed" status: 400 detail: "One or more validation errors has occurred" errors: - propertyName: "'PageSize'" errorMessage: "'PageSize' must be greater than 0." attemptedValue: null customState: null severity: 0 errorCode: null formattedMessagePlaceholderValues: null "401": description: Unauthorized - Access token is missing or invalid. post: tags: - Order summary: Create Order description: Create a new order x-eventcatalog-message-type: command operationId: CreateOrderEndpoint security: - OAuth: - ordering_read - ordering_write parameters: - name: X-Request-Id in: header description: The idempotency key of the order to be created required: true schema: type: string description: The idempotency key of the order to be created example: "019622d1-014e-7bb7-b279-42a43ab74347" responses: "201": description: Created content: application/json: schema: type: string format: uuid description: The unique identifier of the order to be created example: "019622d1-3613-737e-8d58-af7db6317153" "401": description: Unauthorized - Access token is missing or invalid. "/api/v1/orders/{id}": get: tags: - Order summary: Get Order description: Get an order if it exists x-eventcatalog-message-type: query operationId: GetOrderEndpoint security: - OAuth: - ordering_read - ordering_write parameters: - name: id in: path description: The unique identifier of the order to be retrieved required: true schema: type: string format: uuid description: The unique identifier of the order to be retrieved example: "019622d1-89ba-791c-a11c-f1821326441b" responses: "200": description: OK content: application/json: schema: "$ref": "#/components/schemas/OrderDetailDto" example: id: "019622d1-89ba-791c-a11c-f1821326441b" date: "2021-01-01T00:00:00Z" status: "New" total: 100.00 items: - id: "019622a3-a3ae-7705-a11b-a08f2a2ca621" name: "AI Engineering" quantity: 1 price: 100.00 - id: "029622a4-a3ae-7705-a11b-a08f2a2ca622" name: "Software Architecture: The Hard Parts" quantity: 1 price: 56.00 - id: "039622a5-a3ae-7705-a11b-a08f2a2ca623" name: "The Pragmatic Programmer: From Journeyman to Master" quantity: 1 price: 45.00 "401": description: Unauthorized - Access token is missing or invalid. "404": description: Not Found content: application/problem+json: schema: "$ref": "#/components/schemas/ProblemDetails" examples: order-not-found: summary: Order not found value: type: "https://tools.ietf.org/html/rfc9110#section-15.5.1" title: "Not Found" status: 404 detail: "Order with id 019622d1-89ba-791c-a11c-f1821326441b not found." delete: tags: - Order summary: Delete Order description: Delete an order if it exists operationId: DeleteOrderEndpoint security: - OAuth: - ordering_read - ordering_write parameters: - name: id in: path description: The unique identifier of the order to be deleted required: true schema: type: string format: uuid description: The unique identifier of the order to be deleted example: "019622d1-c7df-7d66-82fe-8124d493cb1d" responses: "204": description: No Content "401": description: Unauthorized - Access token is missing or invalid. "404": description: Not Found content: application/problem+json: schema: "$ref": "#/components/schemas/ProblemDetails" examples: order-not-found: summary: Order not found value: type: "https://tools.ietf.org/html/rfc9110#section-15.5.1" title: "Not Found" status: 404 detail: "Order with id 019622d1-c7df-7d66-82fe-8124d493cb1d not found." "/api/v1/orders/{orderId}/complete": patch: tags: - Order summary: Complete Order description: Complete an order x-eventcatalog-message-type: command operationId: CompleteOrderEndpoint security: - OAuth: - ordering_read - ordering_write parameters: - name: X-Request-Id in: header description: The idempotency key of the order to be completed required: true schema: type: string description: The idempotency key of the order to be completed example: "019622d1-014e-7bb7-b279-42a43ab74347" - name: orderId in: path description: The unique identifier of the order to be completed required: true schema: type: string format: uuid description: The unique identifier of the order to be completed example: "019622d1-89ba-791c-a11c-f1821326441b" responses: "200": description: OK content: application/json: schema: "$ref": "#/components/schemas/OrderDetailDto" examples: order-detail: summary: Order detail value: id: "019622d1-89ba-791c-a11c-f1821326441b" date: "2021-01-01T00:00:00Z" status: "Completed" total: 100.00 items: - id: "019622a3-a3ae-7705-a11b-a08f2a2ca621" name: "AI Engineering" quantity: 1 price: 100.00 - id: "029622a4-a3ae-7705-a11b-a08f2a2ca622" name: "Software Architecture: The Hard Parts" quantity: 1 price: 56.00 - id: "039622a5-a3ae-7705-a11b-a08f2a2ca623" name: "The Pragmatic Programmer: From Journeyman to Master" quantity: 1 "401": description: Unauthorized - Access token is missing or invalid. "404": description: Not Found content: application/problem+json: schema: "$ref": "#/components/schemas/ProblemDetails" examples: order-not-found: summary: Order not found value: type: "https://tools.ietf.org/html/rfc9110#section-15.5.1" title: "Not Found" status: 404 detail: "Order with id 019622d1-89ba-791c-a11c-f1821326441b not found." "/api/v1/orders/{orderId}/cancel": patch: tags: - Order summary: Cancel Order description: Cancel an order x-eventcatalog-message-type: command operationId: CancelOrderEndpoint security: - OAuth: - ordering_read - ordering_write parameters: - name: X-Request-Id in: header description: The idempotency key of the order to be cancelled required: true schema: type: string description: The idempotency key of the order to be cancelled example: "019622d1-014e-7bb7-b279-42a43ab74347" - name: orderId in: path description: The unique identifier of the order to be cancelled required: true schema: type: string format: uuid description: The unique identifier of the order to be cancelled example: "019622d1-89ba-791c-a11c-f1821326441b" responses: "200": description: OK content: application/json: schema: "$ref": "#/components/schemas/OrderDetailDto" examples: order-detail: summary: Order detail value: id: "019622d1-89ba-791c-a11c-f1821326441b" date: "2021-01-01T00:00:00Z" status: "Cancelled" total: 100.00 items: - id: "019622a3-a3ae-7705-a11b-a08f2a2ca621" name: "AI Engineering" quantity: 1 price: 100.00 - id: "029622a4-a3ae-7705-a11b-a08f2a2ca622" name: "Software Architecture: The Hard Parts" quantity: 1 price: 56.00 - id: "039622a5-a3ae-7705-a11b-a08f2a2ca623" name: "The Pragmatic Programmer: From Journeyman to Master" quantity: 1 price: 45.00 "401": description: Unauthorized - Access token is missing or invalid. "404": description: Not Found content: application/problem+json: schema: "$ref": "#/components/schemas/ProblemDetails" examples: order-not-found: summary: Order not found value: type: "https://tools.ietf.org/html/rfc9110#section-15.5.1" title: "Not Found" status: 404 detail: "Order with id 019622d1-89ba-791c-a11c-f1821326441b not found." "/api/v1/buyers/address": patch: tags: - Buyer summary: Update Buyer Address description: Update the current buyer's address x-eventcatalog-message-type: command operationId: UpdateAddressEndpoint security: - OAuth: - ordering_read - ordering_write requestBody: description: The command to update the current buyer's address content: application/json: schema: "$ref": "#/components/schemas/UpdateAddressCommand" example: street: "123 Main St" city: "Anytown" province: "CA" required: true responses: "200": description: OK content: application/json: schema: "$ref": "#/components/schemas/BuyerDto" examples: buyer-detail: summary: Buyer detail value: id: "019622d7-e45c-7ccb-868f-509b69559883" name: "John Doe" address: "123 Main St, Anytown, USA" "400": description: Bad Request content: application/problem+json: schema: "$ref": "#/components/schemas/HttpValidationProblemDetails" examples: empty-street: summary: Empty street value: type: "https://tools.ietf.org/html/rfc9110#section-15.5.1" title: "Validation failed" status: 400 detail: "One or more validation errors has occurred" errors: - propertyName: "'Street'" errorMessage: "'Street' must not be empty." attemptedValue: null customState: null severity: 0 errorCode: null formattedMessagePlaceholderValues: null empty-city: summary: Empty city value: type: "https://tools.ietf.org/html/rfc9110#section-15.5.1" title: "Validation failed" status: 400 detail: "One or more validation errors has occurred" errors: - propertyName: "'City'" errorMessage: "'City' must not be empty." attemptedValue: null customState: null severity: 0 errorCode: null formattedMessagePlaceholderValues: null empty-province: summary: Empty province value: type: "https://tools.ietf.org/html/rfc9110#section-15.5.1" title: "Validation failed" status: 400 detail: "One or more validation errors has occurred" errors: - propertyName: "'Province'" errorMessage: "'Province' must not be empty." attemptedValue: null customState: null severity: 0 errorCode: null formattedMessagePlaceholderValues: null "401": description: Unauthorized - Access token is missing or invalid. "404": description: Not Found content: application/problem+json: schema: "$ref": "#/components/schemas/ProblemDetails" example: type: "https://tools.ietf.org/html/rfc9110#section-15.5.1" title: "Not Found" status: 404 detail: "Buyer with id 019622d7-e45c-7ccb-868f-509b69559883 not found." "/api/v1/buyers": get: tags: - Buyer summary: List Buyers description: List all buyers with pagination options x-eventcatalog-message-type: query operationId: ListBuyersEndpoint security: - OAuth: - ordering_read - ordering_write parameters: - name: PageIndex in: query description: Number of items to return in a single page of results schema: type: integer format: int32 default: 1 description: The page index to return minimum: 1 example: 1 - name: PageSize in: query description: Number of items to return in a single page of results schema: type: integer format: int32 default: 20 description: The page size to return minimum: 1 example: 20 responses: "200": description: OK content: application/json: schema: "$ref": "#/components/schemas/PagedResultOfBuyerDto" examples: buyers-empty: summary: No buyers found value: items: [] pageIndex: 1 pageSize: 20 totalItems: 0 totalPages: 0 hasPreviousPage: false hasNextPage: false buyers-single: summary: Single buyer value: items: - id: "019622d7-e45c-7ccb-868f-509b69559883" name: "John Doe" address: "123 Main St, Anytown, USA" pageIndex: 1 pageSize: 20 totalItems: 1 totalPages: 1 hasPreviousPage: false hasNextPage: false buyers-multiple: summary: Multiple buyers value: items: - id: "019622d7-e45c-7ccb-868f-509b69559883" name: "John Doe" address: "123 Main St, Anytown, USA" - id: "029622d8-e45c-7ccb-868f-509b69559884" name: "Jane Smith" address: "456 Maple Ave, Anytown, USA" pageIndex: 1 pageSize: 20 totalItems: 2 totalPages: 1 hasPreviousPage: false hasNextPage: false "401": description: Unauthorized - Access token is missing or invalid. "403": description: Forbidden - The permission requirements are not met. post: tags: - Buyer summary: Create Buyer description: Create a new buyer in the system x-eventcatalog-message-type: command operationId: CreateBuyerEndpoint security: - OAuth: - ordering_read - ordering_write requestBody: description: The command to create a new buyer content: application/json: schema: "$ref": "#/components/schemas/CreateBuyerCommand" example: street: "123 Main St" city: "Anytown" province: "CA" required: true responses: "201": description: Created content: application/json: schema: type: string format: uuid description: The unique identifier of the buyer to be created example: "019622d6-d8ae-7512-9b28-3f5591426db8" "400": description: Bad Request content: application/problem+json: schema: "$ref": "#/components/schemas/HttpValidationProblemDetails" examples: empty-street: summary: Empty street value: type: "https://tools.ietf.org/html/rfc9110#section-15.5.1" title: "Validation failed" status: 400 detail: "One or more validation errors has occurred" errors: - propertyName: "'Street'" errorMessage: "'Street' must not be empty." attemptedValue: null customState: null severity: 0 errorCode: null formattedMessagePlaceholderValues: null empty-city: summary: Empty city value: type: "https://tools.ietf.org/html/rfc9110#section-15.5.1" title: "Validation failed" status: 400 detail: "One or more validation errors has occurred" errors: - propertyName: "'City'" errorMessage: "'City' must not be empty." attemptedValue: null customState: null severity: 0 errorCode: null formattedMessagePlaceholderValues: null empty-province: summary: Empty province value: type: "https://tools.ietf.org/html/rfc9110#section-15.5.1" title: "Validation failed" status: 400 detail: "One or more validation errors has occurred" errors: - propertyName: "'Province'" errorMessage: "'Province' must not be empty." attemptedValue: null customState: null severity: 0 errorCode: null formattedMessagePlaceholderValues: null "/api/v1/buyers/me": get: tags: - Buyer summary: Get Buyer description: Get the current buyer's information operationId: GetBuyerEndpoint security: - OAuth: - ordering_read - ordering_write parameters: - name: Id in: query description: Only 'ADMIN' role can retrieve other users' data required: true schema: type: string format: uuid description: The unique identifier of the buyer to be retrieved example: "019622d7-313a-777a-9077-4323b9f59fb9" responses: "200": description: OK content: application/json: schema: "$ref": "#/components/schemas/BuyerDto" examples: buyer-detail: summary: Buyer detail value: id: "019622d7-e45c-7ccb-868f-509b69559883" name: "John Doe" address: "123 Main St, Anytown, USA" "401": description: Unauthorized - Access token is missing or invalid. "404": description: Not Found content: application/problem+json: schema: "$ref": "#/components/schemas/ProblemDetails" example: type: "https://tools.ietf.org/html/rfc9110#section-15.5.1" title: "Not Found" status: 404 detail: "Buyer with id 019622d7-e45c-7ccb-868f-509b69559883 not found." "/api/v1/buyers/{id}": delete: tags: - Buyer summary: Delete Buyer description: Delete a buyer by ID if it exists operationId: DeleteBuyerEndpoint security: - OAuth: - ordering_read - ordering_write parameters: - name: id in: path description: The unique identifier of the buyer to be deleted required: true schema: type: string format: uuid description: The unique identifier of the buyer to be deleted example: "019622d7-6ba8-7edc-9340-8c93524c3819" responses: "204": description: No Content "401": description: Unauthorized - Access token is missing or invalid. "403": description: Forbidden - The permission requirements are not met. "404": description: Not Found content: application/problem+json: schema: "$ref": "#/components/schemas/ProblemDetails" example: type: "https://tools.ietf.org/html/rfc9110#section-15.5.1" title: "Not Found" status: 404 detail: "Buyer with id 019622d7-e45c-7ccb-868f-509b69559883 not found." components: schemas: BuyerDto: type: object readOnly: true description: The buyer details properties: id: type: string format: uuid description: The unique identifier of the buyer example: "019622d7-e45c-7ccb-868f-509b69559883" name: type: - "string" - "null" description: The name of the buyer example: "John Doe" address: type: - "string" - "null" description: The address of the buyer example: "123 Main St, Anytown, USA" CreateBuyerCommand: required: - street - city - province type: object writeOnly: true description: The command to create a buyer properties: street: type: string description: The street of the buyer maxLength: 50 example: "123 Main St" city: type: string description: The city of the buyer maxLength: 50 example: "Anytown" province: type: string description: The province of the buyer maxLength: 50 example: "CA" HttpValidationProblemDetails: type: object readOnly: true x-scalar-ignore: true description: RFC 9110 (https://tools.ietf.org/html/rfc9110#section-15.5.1) properties: type: type: - "null" - string description: The type of the problem example: "https://tools.ietf.org/html/rfc9110#section-15.5.1" title: type: - "null" - string description: The title of the problem example: "Validation failed" status: pattern: ^-?(?:0|[1-9]\d*)$ type: - "null" - integer - string format: int32 description: The status code of the problem example: 400 detail: type: - "null" - string description: The detail of the problem example: "One or more validation errors has occurred" instance: type: - "null" - string format: uri description: The instance of the problem example: "/api/v1/orders" errors: type: object additionalProperties: type: array items: type: string description: The validation errors example: propertyName: "'PageIndex'" errorMessage: "'PageIndex' must be greater than 0." attemptedValue: null customState: null severity: 0 errorCode: null formattedMessagePlaceholderValues: null NullableOfStatus: description: The status of the order readOnly: true enum: - New - Cancelled - Completed - default: OrderDetailDto: type: object readOnly: true description: The order details properties: id: type: string format: uuid description: The unique identifier of the order example: "019622e7-87ac-7bd1-aa9c-35803d8a1e50" date: type: string format: date-time description: The date of the order example: "2021-01-01T00:00:00Z" total: type: number format: double description: The total price of the order example: 100.00 status: "$ref": "#/components/schemas/Status" items: type: array description: The items of the order items: "$ref": "#/components/schemas/OrderItemDto" OrderDto: type: object readOnly: true description: The order summary properties: id: type: string format: uuid description: The unique identifier of the order example: "019622e7-87ac-7bd1-aa9c-35803d8a1e50" date: type: string format: date-time description: The date of the order example: "2021-01-01T00:00:00Z" total: type: number format: double description: The total price of the order example: 100.00 status: "$ref": "#/components/schemas/Status" OrderItemDto: type: object readOnly: true description: The order item details properties: id: type: string format: uuid description: The unique identifier of the order item example: "019622e7-87ac-7bd1-aa9c-35803d8a1e50" quantity: type: integer format: int32 description: The quantity of the order item example: 1 price: type: number format: double description: The price of the order item example: 100.00 name: type: string description: The name of the order item example: "Book" OrderSummaryInfo: type: object readOnly: true description: The order summary information properties: id: type: string format: uuid description: The unique identifier of the order example: "019622e7-87ac-7bd1-aa9c-35803d8a1e50" status: "$ref": "#/components/schemas/Status" totalPrice: type: number format: double description: The total price of the order example: 100.00 PagedResultOfBuyerDto: type: object readOnly: true description: The paged result of the buyer properties: items: type: array items: "$ref": "#/components/schemas/BuyerDto" pageIndex: type: integer format: int32 description: The page index example: 1 pageSize: type: integer format: int32 description: The page size example: 10 totalItems: type: integer format: int64 description: The total items example: 100 totalPages: type: number format: double description: The total pages example: 10 hasPreviousPage: type: boolean description: The has previous page example: true hasNextPage: type: boolean description: The has next page example: true PagedResultOfOrderDto: type: object readOnly: true description: The paged result of the order properties: items: type: array items: "$ref": "#/components/schemas/OrderDto" pageIndex: type: integer format: int32 description: The page index example: 1 pageSize: type: integer format: int32 description: The page size example: 10 totalItems: type: integer format: int64 description: The total items example: 100 totalPages: type: number format: double description: The total pages example: 10 hasPreviousPage: type: boolean description: The has previous page example: true hasNextPage: type: boolean description: The has next page example: true ProblemDetails: type: object readOnly: true x-scalar-ignore: true description: RFC 9110 (https://tools.ietf.org/html/rfc9110) properties: type: type: - "null" - string description: The type of the problem example: "https://tools.ietf.org/html/rfc9110#section-15.5.1" title: type: - "null" - string description: The title of the problem example: "Not Found" status: pattern: ^-?(?:0|[1-9]\d*)$ type: - "null" - integer - string format: int32 description: The status code of the problem example: 404 detail: type: - "null" - string description: The detail of the problem example: "Buyer with id 019622d7-e45c-7ccb-868f-509b69559883 not found." instance: type: - "null" - string format: uri description: The instance of the problem example: "/api/v1/buyers/019622d7-e45c-7ccb-868f-509b69559883" Status: description: The status of the order enum: - New - Cancelled - Completed UpdateAddressCommand: required: - street - city - province type: object writeOnly: true description: The command to update the address of the buyer properties: street: type: string description: The street of the buyer maxLength: 50 example: "123 Main St" city: type: string description: The city of the buyer maxLength: 50 example: "Anytown" province: type: string description: The province of the buyer maxLength: 50 example: "CA" securitySchemes: OAuth: type: oauth2 description: OAuth2 security scheme for the BookWorm API flows: authorizationCode: authorizationUrl: "https://auth.bookworm.com/realms/bookworm/protocol/openid-connect/auth" tokenUrl: "https://auth.bookworm.com/realms/bookworm/protocol/openid-connect/token" scopes: ordering_read: Read for Ordering API ordering_write: Write for Ordering API x-usePkce: "SHA-256" x-scalar-client-id: "ordering" x-scalar-security-body: audience: "account" tags: - name: Buyer description: Endpoints for managing buyer information, including profile details, shipping addresses, and purchase history for customers of the BookWorm platform - name: Order description: Endpoints for managing orders, including creation, retrieval, updating status, and cancellation of orders in the BookWorm e-commerce platform ## Raw Schema:asyncapi-v1.yml --- asyncapi: 3.0.0 info: title: Ordering Service API version: 1.0.0 description: Orchestrates the entire order lifecycle, from creation to completion or cancellation, for the BookWorm platform contact: name: Nhan Nguyen url: "https://github.com/foxminchan" license: name: MIT url: "https://opensource.org/licenses/MIT" defaultContentType: application/vnd.masstransit+json servers: development: host: dev.eventbus:5672 protocol: amqp protocolVersion: 0.9.1 summary: RabbitMQ server for development environment description: RabbitMQ server for development environment security: - type: userPassword description: An authentication method for the server tags: - name: env:development description: >- Development environment configuration for local testing and debugging staging: host: stg.eventbus:5672 protocol: amqp protocolVersion: 0.9.1 summary: RabbitMQ server for staging environment description: RabbitMQ server for staging environment security: - type: userPassword description: An authentication method for the server tags: - name: env:staging description: >- Staging environment configuration for testing and debugging qa: host: qa.eventbus:5672 protocol: amqp protocolVersion: 0.9.1 summary: RabbitMQ server for QA environment description: RabbitMQ server for QA environment security: - type: userPassword description: An authentication method for the server tags: - name: env:qa description: >- QA environment configuration for testing and debugging production: host: prod.eventbus:5672 protocol: amqp protocolVersion: 0.9.1 summary: RabbitMQ server for production environment description: RabbitMQ server for production environment security: - type: userPassword description: An authentication method for the server tags: - name: env:production description: >- Production environment configuration for production channels: basket-checkout-complete: address: basket-checkout-complete description: Event emitted when a user completes a basket checkout messages: DeleteBasketCompleteCommand.message: $ref: "#/components/messages/deleteBasketCompleteCommand" basket-checkout-failed: address: basket-checkout-failed description: Event emitted when a user fails to complete a basket checkout messages: DeleteBasketFailedCommand.message: $ref: "#/components/messages/deleteBasketFailedCommand" "BookWorm.Contracts:UserCheckedOutIntegrationEvent": address: "BookWorm.Contracts:UserCheckedOutIntegrationEvent" description: Event emitted when a user checks out messages: UserCheckedOutIntegrationEvent.message: $ref: "#/components/messages/userCheckedOutIntegrationEvent" "BookWorm.Contracts:OrderStatusChangedToCompleteIntegrationEvent": address: "BookWorm.Contracts:OrderStatusChangedToCompleteIntegrationEvent" description: Event emitted when an order is completed messages: OrderStatusChangedToCompleteIntegrationEvent.message: $ref: "#/components/messages/orderStatusChangedToCompleteIntegrationEvent" "BookWorm.Contracts:OrderStatusChangedToCancelIntegrationEvent": address: "BookWorm.Contracts:OrderStatusChangedToCancelIntegrationEvent" description: Event emitted when an order is cancelled messages: OrderStatusChangedToCancelIntegrationEvent.message: $ref: "#/components/messages/orderStatusChangedToCancelIntegrationEvent" operations: DeleteBasketCompleteCommand: title: Delete basket complete summary: Delete basket complete description: Represents a successful integration event when deleting a basket in the system action: receive channel: $ref: "#/channels/basket-checkout-complete" messages: - $ref: >- #/channels/basket-checkout-complete/messages/DeleteBasketCompleteCommand.message DeleteBasketFailedCommand: title: Delete basket failed summary: Delete basket failed description: Represents a failed integration event when deleting a basket in the system action: receive channel: $ref: "#/channels/basket-checkout-failed" messages: - $ref: >- #/channels/basket-checkout-failed/messages/DeleteBasketFailedCommand.message UserCheckedOutIntegrationEvent: title: User checked out summary: User checked out description: Represents a successful integration event when a user checks out action: send channel: $ref: "#/channels/BookWorm.Contracts:UserCheckedOutIntegrationEvent" messages: - $ref: >- #/channels/BookWorm.Contracts:UserCheckedOutIntegrationEvent/messages/UserCheckedOutIntegrationEvent.message OrderStatusChangedToCompleteIntegrationEvent: title: Order status changed to complete summary: Order status changed to complete description: Represents a successful integration event when an order is completed action: send channel: $ref: >- #/channels/BookWorm.Contracts:OrderStatusChangedToCompleteIntegrationEvent messages: - $ref: >- #/channels/BookWorm.Contracts:OrderStatusChangedToCompleteIntegrationEvent/messages/OrderStatusChangedToCompleteIntegrationEvent.message OrderStatusChangedToCancelIntegrationEvent: title: Order status changed to cancel summary: Order status changed to cancel description: Represents a successful integration event when an order is cancelled action: send channel: $ref: "#/channels/BookWorm.Contracts:OrderStatusChangedToCancelIntegrationEvent" messages: - $ref: >- #/channels/BookWorm.Contracts:OrderStatusChangedToCancelIntegrationEvent/messages/OrderStatusChangedToCancelIntegrationEvent.message components: schemas: deleteBasketCompleteCommand: id: deleteBasketCompleteCommand description: Represents a successful integration event when deleting a basket in the system allOf: - $ref: "#/components/schemas/integrationEvent" - type: object additionalProperties: false properties: orderId: type: string format: guid description: The unique identifier of the order example: "019622e7-87ac-7bd1-aa9c-35803d8a1e50" totalMoney: type: number format: decimal description: The total amount of money in the basket minimum: 0 example: 100.00 integrationEvent: id: integrationEvent type: object description: Base event structure containing common metadata for all integration events in the system additionalProperties: false properties: id: type: string format: guid description: The unique identifier of the integration event example: "01961eb4-668d-7e7e-ae25-0fab379614f7" creationDate: type: string format: date-time description: The creation date of the integration event example: "2021-01-01T00:00:00Z" deleteBasketFailedCommand: id: deleteBasketFailedCommand description: Represents a failed integration event when deleting a basket in the system allOf: - $ref: "#/components/schemas/integrationEvent" - type: object additionalProperties: false properties: basketId: type: string format: guid description: The unique identifier of the basket example: "01961eb4-668d-7e7e-ae25-0fab379614f7" email: type: - "null" - string description: The email of the user pattern: "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$" example: "test@test.com" orderId: type: string format: guid description: The unique identifier of the order example: "01961eb4-668d-7e7e-ae25-0fab379614f7" userCheckedOutIntegrationEvent: id: userCheckedOutIntegrationEvent description: Represents a successful integration event when a user checks out allOf: - $ref: "#/components/schemas/integrationEvent" - type: object additionalProperties: false properties: orderId: type: string format: guid description: The unique identifier of the order example: "01961eb4-668d-7e7e-ae25-0fab379614f7" basketId: type: string format: guid description: The unique identifier of the basket example: "01961eb4-668d-7e7e-ae25-0fab379614f7" email: type: - "null" - string description: The email of the user pattern: "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$" example: "test@test.com" totalMoney: type: number format: decimal description: The total amount of money in the basket minimum: 0 example: 100.00 orderStatusChangedToCompleteIntegrationEvent: id: orderStatusChangedToCompleteIntegrationEvent description: Represents a successful integration event when an order is completed allOf: - $ref: "#/components/schemas/integrationEvent" - type: object additionalProperties: false properties: orderId: type: string format: guid description: The unique identifier of the order example: "01961eb4-668d-7e7e-ae25-0fab379614f7" basketId: type: string format: guid description: The unique identifier of the basket example: "01961eb4-668d-7e7e-ae25-0fab379614f7" email: type: - "null" - string description: The email of the user pattern: "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$" example: "test@test.com" totalMoney: type: number format: decimal description: The total amount of money in the basket minimum: 0 example: 100.00 orderStatusChangedToCancelIntegrationEvent: id: orderStatusChangedToCancelIntegrationEvent description: Represents a successful integration event when an order is cancelled allOf: - $ref: "#/components/schemas/integrationEvent" - type: object additionalProperties: false properties: orderId: type: string format: guid description: The unique identifier of the order example: "01961eb4-668d-7e7e-ae25-0fab379614f7" basketId: type: string format: guid description: The unique identifier of the basket example: "01961eb4-668d-7e7e-ae25-0fab379614f7" email: type: - "null" - string description: The email of the user pattern: "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$" example: "test@test.com" totalMoney: type: number format: decimal description: The total amount of money in the basket minimum: 0 example: 100.00 messages: deleteBasketCompleteCommand: payload: $ref: "#/components/schemas/deleteBasketCompleteCommand" name: deleteBasketCompleteCommand deleteBasketFailedCommand: payload: $ref: "#/components/schemas/deleteBasketFailedCommand" name: deleteBasketFailedCommand userCheckedOutIntegrationEvent: payload: $ref: "#/components/schemas/userCheckedOutIntegrationEvent" name: userCheckedOutIntegrationEvent orderStatusChangedToCompleteIntegrationEvent: payload: $ref: "#/components/schemas/orderStatusChangedToCompleteIntegrationEvent" name: orderStatusChangedToCompleteIntegrationEvent orderStatusChangedToCancelIntegrationEvent: payload: $ref: "#/components/schemas/orderStatusChangedToCancelIntegrationEvent" name: orderStatusChangedToCancelIntegrationEvent --- id: ProductService name: Product Service version: 1.0.0 summary: >- Manages the catalog of products for the BookWorm platform, including books, authors, and categories badges: - content: Event-Driven textColor: purple icon: BoltIcon backgroundColor: purple - content: PostgreSQL textColor: blue icon: CircleStackIcon backgroundColor: blue - content: Azure Blob Storage textColor: teal icon: PhotoIcon backgroundColor: teal sends: - id: BookUpdatedRatingFailedEvent version: 1.0.0 to: - &ref_0 id: "rating.{env}.events" version: 1.0.0 parameters: env: stg - *ref_0 receives: - id: GetBookGrpc version: 1.0.0 from: - id: "catalog.{env}.events" version: 1.0.0 parameters: env: stg - id: ListBooksGrpc version: 1.0.0 from: - id: "catalog.{env}.events" version: 1.0.0 parameters: env: stg - id: FeedbackCreatedEvent version: 1.0.0 from: - &ref_1 id: "catalog.{env}.events" version: 1.0.0 parameters: env: stg - *ref_1 - id: FeedbackDeletedEvent version: 1.0.0 from: - &ref_2 id: "catalog.{env}.events" version: 1.0.0 parameters: env: stg - *ref_2 schemaPath: openapi-v1.yml specifications: - type: asyncapi path: asyncapi-v1.yml name: AsyncAPI V1 - type: openapi path: openapi-v1.yml name: OpenAPI V1 owners: - nhanxnguyen repository: language: C# url: "https://github.com/foxminchan/BookWorm" writesTo: - id: CatalogDatabase version: 1.0.0 - id: CatalogBlob version: 1.0.0 - id: CatalogVectorDB version: 1.0.0 - id: CatalogCache version: 1.0.0 readsFrom: - id: CatalogDatabase version: 1.0.0 - id: CatalogBlob version: 1.0.0 - id: CatalogVectorDB version: 1.0.0 - id: CatalogCache version: 1.0.0 attachments: - url: >- https://foxminchan.github.io/BookWorm/docs/architecture-decisions/adr-004-postgresql-database title: ADR-004 - PostgreSQL Database description: Learn more about the PostgreSQL database choice for the BookWorm platform. type: architecture-decisions icon: FileTextIcon - url: >- https://foxminchan.github.io/BookWorm/docs/architecture-decisions/adr-016-ddd-catalog title: ADR-016 - DDD for Catalog Management description: >- Learn more about the Domain-Driven Design approach for catalog management in BookWorm. type: architecture-decisions icon: FileTextIcon - url: >- https://foxminchan.github.io/BookWorm/docs/architecture-decisions/adr-017-grpc title: ADR-017 - gRPC for Internal Service Communication description: >- Learn more about the gRPC communication strategy for internal services in BookWorm. type: architecture-decisions icon: FileTextIcon --- ## Overview The Catalog Service is a core bounded context within the BookWorm ecosystem that implements Domain-Driven Design principles to manage the book inventory and metadata, built on our [microservices architecture](https://foxminchan.github.io/BookWorm/architecture-decisions/adr-001-microservices-architecture). This service is responsible for: - Managing the book catalog, including details like title, ISBN, publication date, and price - Maintaining author profiles and their associated works - Organizing books by categories and genres - Handling publisher information and their published titles - Processing book metadata updates and synchronization - Broadcasting domain events when significant catalog changes occur The domain model follows strategic DDD patterns with clearly defined aggregates, entities, and value objects. The Catalog domain enforces business rules such as ISBN validation, category relationships, and author attribution. The service uses [PostgreSQL as the primary database](https://foxminchan.github.io/BookWorm/architecture-decisions/adr-004-postgresql-database) for transactional data and publishes domain events via message broker integration following our [event-driven CQRS pattern](https://foxminchan.github.io/BookWorm/architecture-decisions/adr-002-event-driven-cqrs) to notify other bounded contexts about catalog changes, maintaining eventual consistency across the system. ## Component Diagram ```mermaid C4Component title Component diagram for Catalog Service Container_Boundary(catalog, "Catalog Service") { Container_Boundary(application, "Application") { Component(catalogEndpoint, "Catalog Endpoints", ".NET", "Manages book catalog operations") Component(catalogFacade, "Catalog Facade", ".NET", "Core business logic for catalog management") Component(vectorService, "Vector Service", ".NET", "Manages vector embeddings for semantic search") Component(storageService, "Storage Service", ".NET", "Manages book files and metadata") Component(llmService, "LLM Service", ".NET", "Handles AI-powered recommendations") Component(eventHandler, "Event Handler", ".NET", "Handles event publishing/subscribing") } Container_Boundary(infrastructure, "Infrastructure") { ComponentDb(storageSystem, "Storage System", "Azure Blob Storage", "Stores book files and resources") ComponentQueue(eventBus, "Event Bus", "RabbitMQ", "Handles async communication") Container_Boundary(ai, "AI") { Component(ollama, "Model Manager", "Ollama", "Get and run models") Container_Boundary(model, "Model") { Component(chatModel, "Chat Model", "Gemma 3", "Handles chat interactions") Component(embeddingModel, "Embedding Model", "Nomic Embed Text", "Handles embedding operations") } } Container_Boundary(database, "Database") { ComponentDb(catalogDb, "Catalog DB", "PostgreSQL", "Stores book metadata and catalog information") ComponentDb(vectorDb, "Vector DB", "Qdrant", "Stores vector embeddings") } } } Rel(catalogEndpoint, catalogFacade, "Uses", "Internal") Rel(catalogFacade, storageService, "Uses", "Internal") Rel(catalogFacade, vectorService, "Uses", "Internal") Rel(catalogFacade, llmService, "Uses", "Internal") Rel(ollama, chatModel, "Uses", "Internal") Rel(ollama, embeddingModel, "Uses", "Internal") Rel(catalogFacade, catalogDb, "Reads/Writes", "SQL") Rel(vectorService, vectorDb, "Reads/Writes", "API") Rel(llmService, vectorDb, "Reads/Writes", "API") Rel(llmService, ollama, "Uses", "Internal") Rel(storageService, storageSystem, "Reads/Writes", "File I/O") BiRel(eventHandler, eventBus, "Publishes/Subscribes", "Async") BiRel(catalogFacade, eventHandler, "Uses", "Internal") UpdateLayoutConfig($c4ShapeInRow="3", $c4BoundaryInRow="1") ``` ## Core Features | Feature | Description | | --------------------- | ---------------------------------------------------------------------------------------------------- | | **Manage Books** | Create, update, and delete book records, including title, ISBN, author, category, and price details. | | **Manage Authors** | Maintain author profiles with biographical information and associated works. | | **Manage Publishers** | Store publisher details and their published titles, including ISBN, publication date, and price. | | **Manage Categories** | Organize books by categories and genres, ensuring proper classification and searchability. | | **View Book Details** | Retrieve detailed information about a specific book, including author, category, and publisher data. | | **Search and Filter** | Search for books by title, author, category, or ISBN, with advanced filtering options. | ## Architecture diagram ## Infrastructure The Catalog Service is deployed on Microsoft Azure and leverages various Azure services for scalability, reliability, and performance. The infrastructure components include: ```mermaid architecture-beta group api(logos:microsoft-azure)[API] service db(logos:postgresql)[Database] in api service server(logos:docker-icon)[Server] in api service disk(disk)[Storage] in api service llm(server)[LLM] in api service vector_db(database)[Vector Database] in api db:L -- R:server server:L -- R:disk server:L -- R:llm server:L -- R:vector_db ``` The service uses Azure Database for PostgreSQL as the primary data store, ensuring ACID compliance and data durability. Azure Blob Storage provides scalable file storage for book cover images and other media assets. Redis Cache is used for performance optimization and session management. Ollama (LLM) and Vector Database are used for GenAI integration. ## Security The Catalog Service implements comprehensive security measures to protect data and ensure secure access: ### Authentication & Authorization - JWT-based authentication with Azure AD integration - Role-based access control (RBAC) for different user types - API key authentication for service-to-service communication ### Rate Limiting & DDoS Protection - Azure DDoS Protection for network-level security - Rate limiting per API key and user - Request throttling based on service tier ## Performance & Scalability The service is designed for high performance and scalability: ### Performance Targets - API Response Time: < 100ms for 95th percentile - Cache Hit Ratio: > 90% - Database Query Time: < 50ms for 95th percentile ### Scalability Features - Horizontal scaling with Azure Kubernetes Service - Auto-scaling based on CPU and memory metrics - Read replicas for database scaling - CDN integration for static content delivery ### Monitoring & Alerts - Real-time performance monitoring - Automated alerts for performance degradation - Custom dashboards for key metrics ## Monitoring & Observability The Catalog Service implements comprehensive monitoring and observability features to ensure system health and performance: ### Metrics & Telemetry The service exposes key metrics through Azure Monitor: #### Business Metrics - Total number of books in catalog - Number of active authors - Number of publishers - Book search success rate - Average book rating #### Technical Metrics - API response times - Error rates by endpoint - Cache hit/miss ratios - Database query performance - Memory and CPU usage - Network latency ### Distributed Tracing The service implements distributed tracing using OpenTelemetry: - Request/response flow tracking - Dependency tracking (database, cache, external services) - Performance profiling - Error tracking with stack traces ### Alerting Automated alerts are configured for: - Error rate thresholds - Response time degradation - Resource utilization - Business metric anomalies - Security incidents ### Dashboard Azure Monitor dashboards provide real-time visibility into: - Service health status - Performance metrics - Error rates and types - Resource utilization - Business metrics trends The base URL for the Catalog Service API is `https://api.bookworm.com/catalog/api/v1`. ```javascript import axios from 'axios'; const catalogService = axios.create({ baseURL: 'https://api.bookworm.com/catalog/api/v1', timeout: 5000, headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer YOUR_JWT_TOKEN' }, }); // Add request interceptor for error handling catalogService.interceptors.request.use( config => { // Add retry logic for failed requests config.retry = 3; config.retryDelay = 1000; return config; }, error => Promise.reject(error) ); // Add response interceptor for error handling catalogService.interceptors.response.use( response => response, async error => { const { config } = error; // Handle rate limiting if (error.response?.status === 429) { const retryAfter = error.response.headers['retry-after']; await new Promise(resolve => setTimeout(resolve, retryAfter * 1000)); return catalogService(config); } // Handle authentication errors if (error.response?.status === 401) { // Implement token refresh logic here return Promise.reject(error); } return Promise.reject(error); } ); ``` ```javascript // Example with error handling and pagination async function getBooks(page = 1, pageSize = 10) { try { const response = await catalogService.get('/books', { params: { page, pageSize, sortBy: 'title', sortOrder: 'asc' } }); return { books: response.data.items, totalCount: response.data.totalCount, currentPage: response.data.currentPage, totalPages: response.data.totalPages }; } catch (error) { if (error.response) { // Handle specific error cases switch (error.response.status) { case 400: console.error('Invalid request parameters:', error.response.data); break; case 404: console.error('Resource not found'); break; case 429: console.error('Rate limit exceeded'); break; default: console.error('An unexpected error occurred'); } } throw error; } } // Example usage getBooks() .then(result => { console.log('Books:', result.books); console.log('Total count:', result.totalCount); console.log('Current page:', result.currentPage); }) .catch(error => { console.error('Failed to fetch books:', error); }); ``` ## Error Handling The Catalog Service uses standard HTTP status codes and provides detailed error responses: ### Error Response Format ```json { "type": "RFC URI identifying the problem type", "title": "A short, human-readable summary of the problem", "status": "The HTTP status code", "detail": "A human-readable explanation of the error" } ``` ### Best Practices for Error Handling 1. Always check the response status code 2. Implement exponential backoff for retries 3. Handle rate limiting with proper backoff 4. Log errors with appropriate context 5. Implement circuit breakers for failing endpoints 6. Cache successful responses to reduce API calls ## Raw Schema:asyncapi-v1.yml --- asyncapi: 3.0.0 info: title: Catalog Service API version: 1.0.0 description: |- Manages the catalog of products for the BookWorm platform, including books, authors, and categories contact: name: Nhan Nguyen url: "https://github.com/foxminchan" license: name: MIT url: "https://opensource.org/licenses/MIT" defaultContentType: application/vnd.masstransit+json servers: development: host: dev.eventbus:5672 protocol: amqp protocolVersion: 0.9.1 summary: RabbitMQ server for development environment description: RabbitMQ server for development environment security: - type: userPassword description: An authentication method for the server tags: - name: env:development description: >- Development environment configuration for local testing and debugging staging: host: stg.eventbus:5672 protocol: amqp protocolVersion: 0.9.1 summary: RabbitMQ server for staging environment description: RabbitMQ server for staging environment security: - type: userPassword description: An authentication method for the server tags: - name: env:staging description: >- Staging environment configuration for testing and debugging qa: host: qa.eventbus:5672 protocol: amqp protocolVersion: 0.9.1 summary: RabbitMQ server for QA environment description: RabbitMQ server for QA environment security: - type: userPassword description: An authentication method for the server tags: - name: env:qa description: >- QA environment configuration for testing and debugging production: host: prod.eventbus:5672 protocol: amqp protocolVersion: 0.9.1 summary: RabbitMQ server for production environment description: RabbitMQ server for production environment security: - type: userPassword description: An authentication method for the server tags: - name: env:production description: >- Production environment configuration for production channels: catalog-feedback-created: address: catalog-feedback-created description: Event emitted when a new feedback is successfully created messages: FeedbackCreatedIntegrationEvent.message: $ref: "#/components/messages/feedbackCreatedIntegrationEvent" rating-book-updated-rating-failed: address: rating-book-updated-rating-failed description: Event emitted when updating a book's rating fails messages: BookUpdatedRatingFailedIntegrationEvent.message: $ref: "#/components/messages/bookUpdatedRatingFailedIntegrationEvent" catalog-feedback-deleted: address: catalog-feedback-deleted description: Event emitted when a feedback is successfully deleted messages: FeedbackDeletedIntegrationEvent.message: $ref: "#/components/messages/feedbackDeletedIntegrationEvent" operations: FeedbackCreatedIntegrationEvent: title: Feedback Created summary: Update book rating description: Represents a successful integration event when creating a feedback in the system action: receive channel: $ref: "#/channels/catalog-feedback-created" messages: - $ref: >- #/channels/catalog-feedback-created/messages/FeedbackCreatedIntegrationEvent.message BookUpdatedRatingFailedIntegrationEvent: action: send channel: $ref: "#/channels/rating-book-updated-rating-failed" summary: "" messages: - $ref: >- #/channels/rating-book-updated-rating-failed/messages/BookUpdatedRatingFailedIntegrationEvent.message FeedbackDeletedIntegrationEvent: action: receive channel: $ref: "#/channels/catalog-feedback-deleted" summary: Update book rating messages: - $ref: >- #/channels/catalog-feedback-deleted/messages/FeedbackDeletedIntegrationEvent.message components: schemas: feedbackCreatedIntegrationEvent: id: feedbackCreatedIntegrationEvent description: Event consumed when a feedback is successfully created, containing details about the feedback allOf: - $ref: "#/components/schemas/integrationEvent" - type: object additionalProperties: false properties: bookId: type: string format: guid description: The ID of the book example: "01961eb4-668d-7e7e-ae25-0fab379614f7" rating: type: integer format: int32 description: The rating of the book example: 4 minimum: 1 maximum: 5 feedbackId: type: string format: guid description: The ID of the feedback example: "01961eb4-668d-7e7e-ae25-0fab379614f7" integrationEvent: id: integrationEvent type: object description: Base event structure containing common metadata for all integration events in the system additionalProperties: false properties: id: type: string format: guid description: The ID of the integration event example: "01961eb4-668d-7e7e-ae25-0fab379614f7" creationDate: type: string format: date-time description: The creation date of the integration event example: "2021-01-01T00:00:00Z" bookUpdatedRatingFailedIntegrationEvent: id: bookUpdatedRatingFailedIntegrationEvent description: Event published when updating a book's rating fails, containing details about the failed transaction allOf: - $ref: "#/components/schemas/integrationEvent" - type: object additionalProperties: false properties: feedbackId: type: string format: guid description: The ID of the feedback example: "01961eb4-668d-7e7e-ae25-0fab379614f7" feedbackDeletedIntegrationEvent: id: feedbackDeletedIntegrationEvent description: Event published when a feedback is successfully deleted, containing details about the deleted feedback allOf: - $ref: "#/components/schemas/integrationEvent" - type: object additionalProperties: false properties: bookId: type: string format: guid description: The ID of the book example: "01961eb4-668d-7e7e-ae25-0fab379614f7" rating: type: integer format: int32 description: The rating of the book example: 4 minimum: 1 maximum: 5 feedbackId: type: string format: guid description: The ID of the feedback example: "01961eb4-668d-7e7e-ae25-0fab379614f7" messages: feedbackCreatedIntegrationEvent: payload: $ref: "#/components/schemas/feedbackCreatedIntegrationEvent" name: feedbackCreatedIntegrationEvent bookUpdatedRatingFailedIntegrationEvent: payload: $ref: "#/components/schemas/bookUpdatedRatingFailedIntegrationEvent" name: bookUpdatedRatingFailedIntegrationEvent feedbackDeletedIntegrationEvent: payload: $ref: "#/components/schemas/feedbackDeletedIntegrationEvent" name: feedbackDeletedIntegrationEvent ## Raw Schema:openapi-v1.yml --- openapi: 3.1.1 info: title: Catalog Service API description: Manages the catalog of products for the BookWorm platform, including books, authors, and categories contact: name: Nhan Nguyen url: https://github.com/foxminchan email: nguyenxuannhan407@gmail.com license: name: MIT url: https://opensource.org/licenses/MIT version: "1.0" externalDocs: description: Documentation url: https://github.com/foxminchan/BookWorm servers: - url: "{protocol}://{environment}.catalog.bookworm.com" description: Non-production environment variables: protocol: enum: - https - http - grpc default: https environment: enum: - dev - stg - qa default: dev - url: "{protocol}://catalog.bookworm.com" description: Production environment variables: protocol: enum: - https - grpc default: https security: - OAuth: - catalog_read - catalog_write paths: "/api/v1/publishers": put: tags: - Publisher summary: Update Publisher description: Updates publisher if it exists x-eventcatalog-message-type: command operationId: UpdatePublisherEndpoint security: - OAuth: - catalog_read - catalog_write requestBody: description: The command to update the publisher content: application/json: schema: "$ref": "#/components/schemas/UpdatePublisherCommand" example: id: "01961eb7-be10-7235-bed8-2fea41c28c02" name: "O'Reilly Media, Inc." required: true responses: "204": description: No Content "400": description: Bad Request content: application/problem+json: schema: "$ref": "#/components/schemas/HttpValidationProblemDetails" examples: empty-publisher-id: summary: Empty publisher ID value: type: "https://tools.ietf.org/html/rfc9110#section-15.5.1" title: "Validation failed" status: 400 detail: "One or more validation errors has occurred" errors: - propertyName: "'Id'" errorMessage: "'Id' must not be empty." attemptedValue: null customState: null severity: 0 errorCode: null formattedMessagePlaceholderValues: null publisher-too-long: summary: Publisher name too long value: type: "https://tools.ietf.org/html/rfc9110#section-15.5.1" title: "Validation failed" status: 400 detail: "One or more validation errors has occurred" errors: - propertyName: "'Name'" errorMessage: "'Name' must be at most 50 characters." attemptedValue: null customState: null severity: 0 errorCode: null formattedMessagePlaceholderValues: null "401": description: Unauthorized - Access token is missing or invalid. "403": description: Forbidden - The permission requirements are not met. get: tags: - Publisher summary: List Publishers description: Lists all publishers x-eventcatalog-message-type: query operationId: ListPublishersEndpoint responses: "200": description: OK content: application/json: schema: type: array items: "$ref": "#/components/schemas/PublisherDto" examples: publishers-empty: summary: No publishers found value: [] publishers-single: summary: Single publisher value: - id: "01961eb7-be10-7235-bed8-2fea41c28c02" name: "O'Reilly Media, Inc." publishers-multiple: summary: Multiple publishers value: - id: "01961eb7-be10-7235-bed8-2fea41c28c02" name: "O'Reilly Media, Inc." - id: "01961eb8-be10-7235-bed8-2fea41c28c03" name: "Addison-Wesley Professional" post: tags: - Publisher summary: Create Publisher description: Creates a new publisher x-eventcatalog-message-type: command operationId: CreatePublisherEndpoint security: - OAuth: - catalog_read - catalog_write requestBody: description: The command to create a publisher content: application/json: schema: "$ref": "#/components/schemas/CreatePublisherCommand" example: name: "O'Reilly Media, Inc." required: true responses: "200": description: OK content: application/json: schema: type: string format: uuid description: The unique identifier of the publisher example: "01961eb4-668d-7e7e-ae25-0fab379614f7" "400": description: Bad Request content: application/problem+json: schema: "$ref": "#/components/schemas/HttpValidationProblemDetails" examples: empty-publisher-name: summary: Empty publisher name value: type: "https://tools.ietf.org/html/rfc9110#section-15.5.1" title: "Validation failed" status: 400 detail: "One or more validation errors has occurred" errors: - propertyName: "'Name'" errorMessage: "'Name' must not be empty." attemptedValue: null customState: null severity: 0 errorCode: null formattedMessagePlaceholderValues: null publisher-name-too-long: summary: Publisher name too long value: type: "https://tools.ietf.org/html/rfc9110#section-15.5.1" title: "Validation failed" status: 400 detail: "One or more validation errors has occurred" errors: - propertyName: "'Name'" errorMessage: "'Name' must be at most 50 characters." attemptedValue: null customState: null severity: 0 errorCode: null formattedMessagePlaceholderValues: null "401": description: Unauthorized - Access token is missing or invalid. "403": description: Forbidden - The permission requirements are not met. "/api/v1/publishers/{id}": delete: tags: - Publisher summary: Delete Publisher description: Delete a publisher by identifier x-eventcatalog-message-type: command operationId: DeletePublisherEndpoint parameters: - name: id in: path description: The unique identifier of the publisher to be deleted required: true schema: type: string format: uuid description: The unique identifier of the publisher to be deleted example: "01961eb7-be10-7235-bed8-2fea41c28c02" responses: "204": description: No Content "401": description: Unauthorized - Access token is missing or invalid. "403": description: Forbidden - The permission requirements are not met. "404": description: Not Found content: application/problem+json: schema: "$ref": "#/components/schemas/ProblemDetails" example: type: "https://tools.ietf.org/html/rfc9110#section-15.5.1" title: "Not Found" status: 404 detail: "Publisher with id 01961eb7-be10-7235-bed8-2fea41c28c02 not found." "/api/v1/categories": put: tags: - Category summary: Update Category description: Update a category if it exists x-eventcatalog-message-type: command operationId: UpdateCategoryEndpoint security: - OAuth: - catalog_read - catalog_write requestBody: description: The command to update the category content: application/json: schema: "$ref": "#/components/schemas/UpdateCategoryCommand" example: id: "01961eb4-668d-7e7e-ae25-0fab379614f7" name: "Programming" required: true responses: "204": description: No Content "400": description: Bad Request content: application/problem+json: schema: "$ref": "#/components/schemas/HttpValidationProblemDetails" examples: empty-category-id: summary: Empty category ID value: type: "https://tools.ietf.org/html/rfc9110#section-15.5.1" title: "Validation failed" status: 400 detail: "One or more validation errors has occurred" errors: - propertyName: "'Id'" errorMessage: "'Id' must not be empty." attemptedValue: null customState: null severity: 0 errorCode: null formattedMessagePlaceholderValues: null category-too-long: summary: Category name too long value: type: "https://tools.ietf.org/html/rfc9110#section-15.5.1" title: "Validation failed" status: 400 detail: "One or more validation errors has occurred" errors: - propertyName: "'Name'" errorMessage: "'Name' must be at most 50 characters." attemptedValue: null customState: null severity: 0 errorCode: null formattedMessagePlaceholderValues: null "401": description: Unauthorized - Access token is missing or invalid. "403": description: Forbidden - The permission requirements are not met. "404": description: Not Found content: application/problem+json: schema: "$ref": "#/components/schemas/ProblemDetails" example: type: "https://tools.ietf.org/html/rfc9110#section-15.5.1" title: "Not Found" status: 404 detail: "Category with id 01961eb4-668d-7e7e-ae25-0fab379614f7 not found." get: tags: - Category summary: List Categories description: List all categories x-eventcatalog-message-type: query operationId: ListCategoriesEndpoint responses: "200": description: OK content: application/json: schema: type: array items: "$ref": "#/components/schemas/CategoryDto" examples: categories-empty: summary: No categories found value: [] categories-single: summary: Single category value: - id: "01961eb4-668d-7e7e-ae25-0fab379614f7" name: "Programming" categories-multiple: summary: Multiple categories value: - id: "01961eb4-668d-7e7e-ae25-0fab379614f7" name: "Programming" - id: "01961eb5-668d-7e7e-ae25-0fab379614f7" name: "Romance & Love" post: tags: - Category summary: Create Category description: Create a new category in the catalog system x-eventcatalog-message-type: command operationId: CreateCategoryEndpoint security: - OAuth: - catalog_read - catalog_write requestBody: description: The command to create a category content: application/json: schema: "$ref": "#/components/schemas/CreateCategoryCommand" example: name: "Programming" required: true responses: "200": description: OK content: application/json: schema: type: string format: uuid description: The unique identifier of the category example: "01961eb4-668d-7e7e-ae25-0fab379614f7" "400": description: Bad Request content: application/problem+json: schema: "$ref": "#/components/schemas/HttpValidationProblemDetails" examples: empty-category-name: summary: Empty category name value: type: "https://tools.ietf.org/html/rfc9110#section-15.5.1" title: "Validation failed" status: 400 detail: "One or more validation errors has occurred" errors: - propertyName: "'Name'" errorMessage: "'Name' must not be empty." attemptedValue: null customState: null severity: 0 errorCode: null formattedMessagePlaceholderValues: null category-name-too-long: summary: Category name too long value: type: "https://tools.ietf.org/html/rfc9110#section-15.5.1" title: "Validation failed" status: 400 detail: "One or more validation errors has occurred" errors: - propertyName: "'Name'" errorMessage: "'Name' must be at most 50 characters." attemptedValue: null customState: null severity: 0 errorCode: null formattedMessagePlaceholderValues: null "401": description: Unauthorized - Access token is missing or invalid. "403": description: Forbidden - The permission requirements are not met. "/api/v1/categories/{id}": delete: tags: - Category summary: Delete Category description: Delete a category by identifier x-eventcatalog-message-type: command operationId: DeleteCategoryEndpoint security: - OAuth: - catalog_read - catalog_write parameters: - name: id in: path description: The unique identifier of the category to be deleted required: true schema: type: string format: uuid description: The unique identifier of the category to be deleted example: "01961eb4-668d-7e7e-ae25-0fab379614f7" responses: "204": description: No Content "401": description: Unauthorized - Access token is missing or invalid. "403": description: Forbidden - The permission requirements are not met. "404": description: Not Found content: application/problem+json: schema: "$ref": "#/components/schemas/ProblemDetails" example: type: "https://tools.ietf.org/html/rfc9110#section-15.5.1" title: "Not Found" status: 404 detail: "Category with id 01961eb4-668d-7e7e-ae25-0fab379614f7 not found." "/api/v1/books": put: tags: - Book summary: Update Book description: Update a book if it exists x-eventcatalog-message-type: command operationId: UpdateBookEndpoint security: - OAuth: - catalog_read - catalog_write requestBody: description: The command to update the book content: multipart/form-data: schema: required: - Id - Name - Price - CategoryId - PublisherId - AuthorIds type: object allOf: - type: object properties: Id: type: string format: uuid description: The unique identifier of the book example: "01961eb4-668d-7e7e-ae25-0fab379614f7" - type: object properties: Name: type: string description: The name of the book example: "Software Architecture: The Hard Parts" maxLength: 50 - type: object properties: Description: type: string description: The description of the book example: "When two of your authors, Neal and Mark, were writing the book Fundamentals of Software Architecture, we kept coming across complex examples in architecture that we wanted to cover but that were too difficult." maxLength: 500 - type: object properties: Image: "$ref": "#/components/schemas/IFormFile" - type: object properties: Price: type: number format: double description: The price of the book example: 43.99 minimum: 0 - type: object properties: PriceSale: type: number format: double description: The sale price of the book example: 39.86 minimum: 0 maximum: 100 - type: object properties: CategoryId: type: string format: uuid description: The category of the book example: "01961eb2-5a5b-749f-b494-1063d73c96a3" - type: object properties: PublisherId: type: string format: uuid description: The publisher of the book example: "01961eb7-be10-7235-bed8-2fea41c28c02" - type: object properties: AuthorIds: type: array items: type: string format: uuid description: The authors of the book examples: - "01961eb4-0c29-799f-ab78-ad3e2beaa54a" - "01961ebd-2bc1-7de2-aa90-639662d9259e" - type: object properties: IsRemoveImage: type: boolean default: false description: Whether to remove the image of the book example: false example: Id: "01961eb4-668d-7e7e-ae25-0fab379614f7" Name: "Software Architecture: The Hard Parts" Description: "When two of your authors, Neal and Mark, were writing the book Fundamentals of Software Architecture, we kept coming across complex examples in architecture that we wanted to cover but that were too difficult." Image: "image.jpg" Price: 43.99 PriceSale: 39.86 CategoryId: "01961eb2-5a5b-749f-b494-1063d73c96a3" PublisherId: "01961eb7-be10-7235-bed8-2fea41c28c02" AuthorIds: - "01961eb4-0c29-799f-ab78-ad3e2beaa54a" - "01961ebd-2bc1-7de2-aa90-639662d9259e" IsRemoveImage: false required: true responses: "204": description: No Content "400": description: Bad Request content: application/problem+json: schema: "$ref": "#/components/schemas/HttpValidationProblemDetails" examples: empty-book-id: summary: Empty book ID value: type: "https://tools.ietf.org/html/rfc9110#section-15.5.1" title: "Validation failed" status: 400 detail: "One or more validation errors has occurred" errors: - propertyName: "'Id'" errorMessage: "'Id' must not be empty." attemptedValue: null customState: null severity: 0 errorCode: null formattedMessagePlaceholderValues: null empty-book-name: summary: Empty book name value: type: "https://tools.ietf.org/html/rfc9110#section-15.5.1" title: "Validation failed" status: 400 detail: "One or more validation errors has occurred" errors: - propertyName: "'Name'" errorMessage: "'Name' must not be empty." attemptedValue: null customState: null severity: 0 errorCode: null formattedMessagePlaceholderValues: null book-name-too-long: summary: Book name too long value: type: "https://tools.ietf.org/html/rfc9110#section-15.5.1" title: "Validation failed" status: 400 detail: "One or more validation errors has occurred" errors: - propertyName: "'Name'" errorMessage: "'Name' must be at most 50 characters." attemptedValue: null customState: null severity: 0 errorCode: null formattedMessagePlaceholderValues: null "401": description: Unauthorized - Access token is missing or invalid. "403": description: Forbidden - The permission requirements are not met. "404": description: Not Found content: application/problem+json: schema: "$ref": "#/components/schemas/ProblemDetails" example: type: "https://tools.ietf.org/html/rfc9110#section-15.5.1" title: "Not Found" status: 404 detail: "Book with id 01961eb4-668d-7e7e-ae25-0fab379614f7 not found." get: tags: - Book summary: List Books description: List all books with advanced filtering and pagination x-eventcatalog-message-type: query operationId: ListBooksEndpoint parameters: - name: PageIndex in: query description: Number of items to return in a single page of results schema: type: integer format: int32 default: 1 example: 1 minimum: 1 - name: PageSize in: query description: Number of items to return in a single page of results schema: type: integer format: int32 default: 20 example: 20 minimum: 1 - name: OrderBy in: query description: Property to order results by schema: type: string default: Name example: "Name" - name: IsDescending in: query description: Whether to order results in descending order schema: type: boolean default: false example: false - name: Search in: query description: Search term to filter results by schema: type: string example: "Software" - name: MinPrice in: query description: Minimum price to filter results by schema: type: number format: double - name: MaxPrice in: query description: Maximum price to filter results by schema: type: number format: double - name: CategoryId in: query description: Category IDs to filter results by schema: type: array items: type: string format: uuid - name: PublisherId in: query description: Publisher IDs to filter results by schema: type: array items: type: string format: uuid - name: AuthorIds in: query description: Author IDs to filter results by schema: type: array items: type: string format: uuid responses: "200": description: OK content: application/json: schema: "$ref": "#/components/schemas/PagedResultOfBookDto" examples: books-empty: summary: No books found value: items: [] pageIndex: 1 pageSize: 20 totalItems: 0 totalPages: 0 hasPreviousPage: false hasNextPage: false books-single: summary: Single book value: items: - id: "01961eb4-668d-7e7e-ae25-0fab379614f7" name: "Software Architecture: The Hard Parts" description: "When two of your authors, Neal and Mark, were writing the book Fundamentals of Software Architecture, we kept coming across complex examples in architecture that we wanted to cover but that were too difficult." image: "https://default.blob.core.windows.net/catalog/01961eb4-668d-7e7e-ae25-0fab379614f7-software-architecture-the-hard-parts.jpg?sv=2023-01-01&sr=b&st=2023-01-01T00%3A00%3A00Z&se=2023-01-01T01%3A00%3A00Z&sp=r&sig=XXXXX" price: 43.99 priceSale: 39.86 status: "InStock" category: id: "01961eb2-5a5b-749f-b494-1063d73c96a3" name: "Programming" publisher: id: "01961eb7-be10-7235-bed8-2fea41c28c02" name: "O'Reilly Media, Inc." authors: - id: "01961eb4-0c29-799f-ab78-ad3e2beaa54a" name: "Neal Ford" - id: "01961ebd-2bc1-7de2-aa90-639662d9259e" name: "Mark Richards" averageRating: 4.5 totalReviews: 591 pageIndex: 1 pageSize: 20 totalItems: 1 totalPages: 1 hasPreviousPage: false hasNextPage: false books-multiple: summary: Multiple books value: items: - id: "01961eb4-668d-7e7e-ae25-0fab379614f7" name: "Software Architecture: The Hard Parts" description: "When two of your authors, Neal and Mark, were writing the book Fundamentals of Software Architecture, we kept coming across complex examples in architecture that we wanted to cover but that were too difficult." image: "https://default.blob.core.windows.net/catalog/01961eb4-668d-7e7e-ae25-0fab379614f7-software-architecture-the-hard-parts.jpg?sv=2023-01-01&sr=b&st=2023-01-01T00%3A00%3A00Z&se=2023-01-01T01%3A00%3A00Z&sp=r&sig=XXXXX" price: 43.99 priceSale: 39.86 status: "InStock" category: id: "01961eb2-5a5b-749f-b494-1063d73c96a3" name: "Programming" publisher: id: "01961eb7-be10-7235-bed8-2fea41c28c02" name: "O'Reilly Media, Inc." authors: - id: "01961eb4-0c29-799f-ab78-ad3e2beaa54a" name: "Neal Ford" - id: "01961ebd-2bc1-7de2-aa90-639662d9259e" name: "Mark Richards" averageRating: 4.5 totalReviews: 591 - id: "01961eb5-668d-7e7e-ae25-0fab379614f7" name: "The Pragmatic Programmer: From Journeyman to Master" description: "The Pragmatic Programmer is a guide to the practice of programming - the art of building effective systems and the craft of creating great code. It offers advice on how to deal with the complexity of real-world software development, and the latest thinking on programming language design. The book is packed with concrete advice that you can use immediately to make your code better." image: "https://default.blob.core.windows.net/catalog/01961eb5-668d-7e7e-ae25-0fab379614f7-the-pragmatic-programmer.jpg?sv=2023-01-01&sr=b&st=2023-01-01T00%3A00%3A00Z&se=2023-01-01T01%3A00%3A00Z&sp=r&sig=XXXXX" price: 43.99 priceSale: 39.86 status: "InStock" category: id: "01961eb2-5a5b-749f-b494-1063d73c96a3" name: "Programming" publisher: id: "01961eb7-be10-7235-bed8-2fea41c28c02" name: "O'Reilly Media, Inc." authors: - id: "01961eb4-0c29-799f-ab78-ad3e2beaa54a" name: "Andrew Hunt" - id: "01961ebd-2bc1-7de2-aa90-639662d9259e" name: "David Thomas" averageRating: 4.5 totalReviews: 591 pageIndex: 1 pageSize: 20 totalItems: 2 totalPages: 1 hasPreviousPage: false hasNextPage: true "400": description: Bad Request content: application/problem+json: schema: "$ref": "#/components/schemas/HttpValidationProblemDetails" examples: invalid-page-index: summary: Invalid page index value: type: "https://tools.ietf.org/html/rfc9110#section-15.5.1" title: "Validation failed" status: 400 detail: "One or more validation errors has occurred" errors: - propertyName: "'PageIndex'" errorMessage: "'PageIndex' must be greater than 0." attemptedValue: null customState: null severity: 0 errorCode: null formattedMessagePlaceholderValues: null invalid-page-size: summary: Invalid page size value: type: "https://tools.ietf.org/html/rfc9110#section-15.5.1" title: "Validation failed" status: 400 detail: "One or more validation errors has occurred" errors: - propertyName: "'PageSize'" errorMessage: "'PageSize' must be greater than 0." attemptedValue: null customState: null severity: 0 errorCode: null formattedMessagePlaceholderValues: null post: tags: - Book summary: Create Book description: Create a book x-eventcatalog-message-type: command operationId: CreateBookEndpoint security: - OAuth: - catalog_read - catalog_write requestBody: description: The command to create a book content: multipart/form-data: schema: required: - Name - Price - CategoryId - PublisherId - AuthorIds type: object allOf: - type: object properties: Name: type: string description: The name of the book example: "Software Architecture: The Hard Parts" maxLength: 50 - type: object properties: Description: type: string description: The description of the book example: "When two of your authors, Neal and Mark, were writing the book Fundamentals of Software Architecture, we kept coming across complex examples in architecture that we wanted to cover but that were too difficult." maxLength: 500 - type: object properties: Image: "$ref": "#/components/schemas/IFormFile" description: The image of the book - type: object properties: Price: type: number format: double description: The price of the book example: 43.99 minimum: 0 - type: object properties: PriceSale: type: number format: double description: The sale price of the book example: 39.86 minimum: 0 maximum: 100 - type: object properties: CategoryId: type: string format: uuid description: The category of the book example: "01961eb2-5a5b-749f-b494-1063d73c96a3" - type: object properties: PublisherId: type: string format: uuid description: The publisher of the book example: "01961eb7-be10-7235-bed8-2fea41c28c02" - type: object properties: AuthorIds: type: array items: type: string format: uuid description: The authors of the book example: - "01961eb4-0c29-799f-ab78-ad3e2beaa54a" - "01961ebd-2bc1-7de2-aa90-639662d9259e" example: Name: "Software Architecture: The Hard Parts" Description: "When two of your authors, Neal and Mark, were writing the book Fundamentals of Software Architecture, we kept coming across complex examples in architecture that we wanted to cover but that were too difficult." Image: "https://default.blob.core.windows.net/catalog/01961eb4-668d-7e7e-ae25-0fab379614f7-software-architecture-the-hard-parts.jpg?sv=2023-01-01&sr=b&st=2023-01-01T00%3A00%3A00Z&se=2023-01-01T01%3A00%3A00Z&sp=r&sig=XXXXX" Price: 43.99 PriceSale: 39.86 CategoryId: "01961eb2-5a5b-749f-b494-1063d73c96a3" PublisherId: "01961eb7-be10-7235-bed8-2fea41c28c02" AuthorIds: - "01961eb4-0c29-799f-ab78-ad3e2beaa54a" - "01961ebd-2bc1-7de2-aa90-639662d9259e" required: true responses: "201": description: Created content: application/json: schema: type: string format: uuid description: The unique identifier of the book example: "01961eb4-668d-7e7e-ae25-0fab379614f7" "400": description: Bad Request content: application/problem+json: schema: "$ref": "#/components/schemas/HttpValidationProblemDetails" examples: invalid-name: summary: Invalid name value: type: "https://tools.ietf.org/html/rfc9110#section-15.5.1" title: "Validation failed" status: 400 detail: "One or more validation errors has occurred" errors: - propertyName: "'Name'" errorMessage: "'Name' must not be empty." attemptedValue: null customState: null severity: 0 errorCode: null formattedMessagePlaceholderValues: null invalid-price: summary: Invalid price value: type: "https://tools.ietf.org/html/rfc9110#section-15.5.1" title: "Validation failed" status: 400 detail: "One or more validation errors has occurred" errors: - propertyName: "'Price'" errorMessage: "'Price' must be greater than 0." attemptedValue: null customState: null severity: 0 errorCode: null formattedMessagePlaceholderValues: null "401": description: Unauthorized - Access token is missing or invalid. "403": description: Forbidden - The permission requirements are not met. "/api/v1/books/{id}": get: tags: - Book summary: Get Book description: Get a book by identifier x-eventcatalog-message-type: query operationId: GetBookEndpoint parameters: - name: id in: path description: The unique identifier of the book to be retrieved required: true schema: type: string format: uuid description: The unique identifier of the book to be retrieved example: "01961eb4-668d-7e7e-ae25-0fab379614f7" responses: "200": description: OK content: application/json: schema: "$ref": "#/components/schemas/BookDto" example: id: "01961eb4-668d-7e7e-ae25-0fab379614f7" name: "Software Architecture: The Hard Parts" description: "When two of your authors, Neal and Mark, were writing the book Fundamentals of Software Architecture, we kept coming across complex examples in architecture that we wanted to cover but that were too difficult." image: "https://default.blob.core.windows.net/catalog/01961eb4-668d-7e7e-ae25-0fab379614f7-software-architecture-the-hard-parts.jpg?sv=2023-01-01&sr=b&st=2023-01-01T00%3A00%3A00Z&se=2023-01-01T01%3A00%3A00Z&sp=r&sig=XXXXX" price: 43.99 priceSale: 39.86 category: id: "01961eb2-5a5b-749f-b494-1063d73c96a3" name: "Programming" publisher: id: "01961eb7-be10-7235-bed8-2fea41c28c02" name: "O'Reilly Media, Inc." authors: - id: "01961eb4-0c29-799f-ab78-ad3e2beaa54a" name: "Neal Ford" - id: "01961ebd-2bc1-7de2-aa90-639662d9259e" name: "Mark Richards" averageRating: 4.5 totalReviews: 591 "401": description: Unauthorized - Access token is missing or invalid. "403": description: Forbidden - The permission requirements are not met. "404": description: Not Found content: application/problem+json: schema: "$ref": "#/components/schemas/ProblemDetails" example: type: "https://tools.ietf.org/html/rfc9110#section-15.5.1" title: "Not Found" status: 404 detail: "Book with id 01961eb4-668d-7e7e-ae25-0fab379614f7 not found." delete: tags: - Book summary: Delete Book description: Delete a book if it exists x-eventcatalog-message-type: command operationId: DeleteBookEndpoint security: - OAuth: - catalog_read - catalog_write parameters: - name: id in: path description: The unique identifier of the book to be deleted required: true schema: type: string format: uuid description: The unique identifier of the book to be deleted example: "01961eb4-668d-7e7e-ae25-0fab379614f7" responses: "204": description: No Content "401": description: Unauthorized - Access token is missing or invalid. "403": description: Forbidden - The permission requirements are not met. "404": description: Not Found content: application/problem+json: schema: "$ref": "#/components/schemas/ProblemDetails" example: type: "https://tools.ietf.org/html/rfc9110#section-15.5.1" title: "Not Found" status: 404 detail: "Book with id 01961eb4-668d-7e7e-ae25-0fab379614f7 not found." "/grpc/v1/books/{id}": get: tags: - Book gRPC summary: Get Book (gRPC) description: Get a book by identifier using gRPC service x-eventcatalog-message-type: query x-grpc-service: BookGrpcService x-grpc-method: GetBook operationId: GetBookGrpcEndpoint parameters: - name: id in: path required: true schema: type: string format: uuid description: The unique identifier of the book responses: "200": description: Book details retrieved successfully content: application/json: schema: $ref: "#/components/schemas/BookGrpcResponse" "404": description: Book not found content: application/json: schema: type: object properties: id: type: string example: "" name: type: string example: "" "/grpc/v1/books/batch": post: tags: - Book gRPC summary: Get Books Batch (gRPC) description: Get multiple books by their identifiers using gRPC service x-eventcatalog-message-type: query x-grpc-service: BookGrpcService x-grpc-method: GetBooks operationId: GetBooksGrpcEndpoint requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/BooksGrpcRequest" responses: "200": description: Books retrieved successfully content: application/json: schema: $ref: "#/components/schemas/BooksGrpcResponse" "/api/v1/authors": put: tags: - Author summary: Update Author description: Update an author if it exists operationId: UpdateAuthorEndpoint security: - OAuth: - catalog_read - catalog_write requestBody: description: The command to update the author content: application/json: schema: "$ref": "#/components/schemas/UpdateAuthorCommand" example: id: "01961eb4-0c29-799f-ab78-ad3e2beaa54a" name: "Neal Ford" required: true responses: "204": description: No Content "400": description: Bad Request content: application/problem+json: schema: "$ref": "#/components/schemas/HttpValidationProblemDetails" examples: invalid-name: summary: Invalid name value: type: "https://tools.ietf.org/html/rfc9110#section-15.5.1" title: "Validation failed" status: 400 detail: "One or more validation errors has occurred" errors: - propertyName: "'Name'" errorMessage: "'Name' must not be empty." attemptedValue: null customState: null severity: 0 errorCode: null formattedMessagePlaceholderValues: null "401": description: Unauthorized - Access token is missing or invalid. "403": description: Forbidden - The permission requirements are not met. "404": description: Not Found content: application/problem+json: schema: "$ref": "#/components/schemas/ProblemDetails" example: type: "https://tools.ietf.org/html/rfc9110#section-15.5.1" title: "Not Found" status: 404 detail: "Author with id 01961eb4-0c29-799f-ab78-ad3e2beaa54a not found." get: tags: - Author summary: List Authors description: List all authors x-eventcatalog-message-type: query operationId: ListAuthorsEndpoint responses: "200": description: OK content: application/json: schema: type: array items: "$ref": "#/components/schemas/AuthorDto" examples: authors-empty: summary: No authors found value: [] authors-single: summary: Single author value: - id: "01961eb4-0c29-799f-ab78-ad3e2beaa54a" name: "Neal Ford" authors-multiple: summary: Multiple authors value: - id: "01961eb4-0c29-799f-ab78-ad3e2beaa54a" name: "Neal Ford" - id: "01961ebd-2bc1-7de2-aa90-639662d9259e" name: "Mark Richards" post: tags: - Author summary: Create Author description: Create a new author in the catalog system x-eventcatalog-message-type: command operationId: CreateAuthorEndpoint security: - OAuth: - catalog_read - catalog_write requestBody: description: The command to create an author content: application/json: schema: "$ref": "#/components/schemas/CreateAuthorCommand" example: name: "Neal Ford" required: true responses: "200": description: OK content: application/json: schema: type: string format: uuid description: The unique identifier of the author example: "01961eb4-0c29-799f-ab78-ad3e2beaa54a" "400": description: Bad Request content: application/problem+json: schema: "$ref": "#/components/schemas/HttpValidationProblemDetails" examples: empty-name: summary: Empty name value: type: "https://tools.ietf.org/html/rfc9110#section-15.5.1" title: "Validation failed" status: 400 detail: "One or more validation errors has occurred" errors: - propertyName: "'Name'" errorMessage: "'Name' must not be empty." attemptedValue: null customState: null severity: 0 errorCode: null formattedMessagePlaceholderValues: null name-too-long: summary: Name too long value: type: "https://tools.ietf.org/html/rfc9110#section-15.5.1" title: "Validation failed" status: 400 detail: "One or more validation errors has occurred" errors: - propertyName: "'Name'" errorMessage: "'Name' must be at most 50 characters." attemptedValue: null customState: null severity: 0 errorCode: null formattedMessagePlaceholderValues: null "401": description: Unauthorized - Access token is missing or invalid. "403": description: Forbidden - The permission requirements are not met. "/api/v1/authors/{id}": delete: tags: - Author summary: Delete Author description: Delete an author from the catalog system x-eventcatalog-message-type: command operationId: DeleteAuthorEndpoint security: - OAuth: - catalog_read - catalog_write parameters: - name: id in: path description: The unique identifier of the author to be deleted required: true schema: type: string format: uuid description: The unique identifier of the author to be deleted example: "01961eb4-0c29-799f-ab78-ad3e2beaa54a" responses: "204": description: No Content "400": description: Bad Request content: application/problem+json: schema: "$ref": "#/components/schemas/HttpValidationProblemDetails" example: type: "https://tools.ietf.org/html/rfc9110#section-15.5.1" title: "Validation failed" status: 400 detail: "One or more validation errors has occurred" errors: - propertyName: "'Id'" errorMessage: "Author has books and cannot be deleted" attemptedValue: null customState: null severity: 0 errorCode: null formattedMessagePlaceholderValues: null "401": description: Unauthorized - Access token is missing or invalid. "403": description: Forbidden - The permission requirements are not met. "404": description: Not Found content: application/problem+json: schema: "$ref": "#/components/schemas/ProblemDetails" example: type: "https://tools.ietf.org/html/rfc9110#section-15.5.1" title: "Not Found" status: 404 detail: "Author with id 01961eb4-0c29-799f-ab78-ad3e2beaa54a not found." components: schemas: AuthorDto: type: object readOnly: true description: The author of the book properties: id: type: string format: uuid description: The unique identifier of the author example: "01961eb4-0c29-799f-ab78-ad3e2beaa54a" name: type: - "string" - "null" description: The name of the author example: "Neal Ford" BookDto: type: object readOnly: true description: The book of the catalog properties: id: type: string format: uuid description: The unique identifier of the book example: "01961eb4-668d-7e7e-ae25-0fab379614f7" name: type: - "string" - "null" description: The name of the book example: "Software Architecture: The Hard Parts" description: type: - "string" - "null" description: The description of the book example: "When two of your authors, Neal and Mark, were writing the book Fundamentals of Software Architecture, we kept coming across complex examples in architecture that we wanted to cover but that were too difficult." imageUrl: type: - "string" - "null" description: The image URL of the book example: "https://default.blob.core.windows.net/catalog/01961eb4-668d-7e7e-ae25-0fab379614f7-software-architecture-the-hard-parts.jpg?sv=2023-01-01&sr=b&st=2023-01-01T00%3A00%3A00Z&se=2023-01-01T01%3A00%3A00Z&sp=r&sig=XXXXX" price: type: number format: double description: The price of the book example: 43.99 priceSale: type: - "number" - "null" format: double description: The sale price of the book example: 39.86 status: "$ref": "#/components/schemas/Status" category: "$ref": "#/components/schemas/CategoryDto" publisher: "$ref": "#/components/schemas/PublisherDto" authors: type: array items: "$ref": "#/components/schemas/AuthorDto" description: The authors of the book example: - id: "01961eb4-0c29-799f-ab78-ad3e2beaa54a" name: "Neal Ford" - id: "01961ebd-2bc1-7de2-aa90-639662d9259e" name: "Mark Richards" - id: "01961ebd-ba46-75c6-8834-ff51cc0ff650" name: "Pramod Sadalage" - id: "01961ebe-1001-7cf6-8071-94b6a622e1a4" name: "Zhamak Dehghani" averageRating: type: number format: double description: The average rating of the book example: 4.5 totalReviews: type: integer format: int32 description: The total number of reviews of the book example: 591 CategoryDto: type: object readOnly: true description: The category of the book properties: id: type: string format: uuid description: The unique identifier of the category example: "01961eb2-5a5b-749f-b494-1063d73c96a3" name: type: - "string" - "null" description: The name of the category example: "Technology" CreateAuthorCommand: required: - name type: object writeOnly: true description: The command to create an author properties: name: type: string description: The name of the author example: "Neal Ford" maxLength: 50 CreateCategoryCommand: required: - name type: object writeOnly: true description: The command to create a category properties: name: type: string description: The name of the category example: "Technology" maxLength: 50 CreatePublisherCommand: required: - name type: object writeOnly: true description: The command to create a publisher properties: name: type: string description: The name of the publisher example: "O'Reilly Media, Inc." maxLength: 50 HttpValidationProblemDetails: type: object readOnly: true x-scalar-ignore: true description: RFC 9110 (https://tools.ietf.org/html/rfc9110#section-15.5.1) properties: type: type: - "null" - string description: The type of the problem example: "https://tools.ietf.org/html/rfc9110#section-15.5.1" title: type: - "null" - string description: The title of the problem example: "Validation failed" status: pattern: ^-?(?:0|[1-9]\d*)$ type: - "null" - integer - string format: int32 description: The status code of the problem example: 400 detail: type: - "null" - string description: The detail of the problem example: "One or more validation errors has occurred" instance: type: - "null" - string format: uri description: The instance of the problem example: "/api/v1/categories" errors: type: object additionalProperties: type: array items: type: string description: The validation errors example: propertyName: "'Name'" errorMessage: "'Name' must not be empty." attemptedValue: null customState: null severity: 0 errorCode: null formattedMessagePlaceholderValues: null IFormFile: type: string description: The file to be uploaded PagedResultOfBookDto: required: - items - pageIndex - pageSize - totalItems - totalPages type: object readOnly: true description: The paged result of books properties: items: type: array items: "$ref": "#/components/schemas/BookDto" pageIndex: type: integer format: int32 description: The index of the page example: 1 pageSize: type: integer format: int32 description: The size of the page example: 20 totalItems: type: integer format: int64 description: The total number of items example: 100 totalPages: type: number format: double description: The total number of pages example: 5 hasPreviousPage: type: boolean description: Whether the page has a previous page example: true hasNextPage: type: boolean description: Whether the page has a next page example: true ProblemDetails: type: object readOnly: true x-scalar-ignore: true description: RFC 9110 (https://tools.ietf.org/html/rfc9110) properties: type: type: - "null" - string description: The type of the problem example: "https://tools.ietf.org/html/rfc9110#section-15.5.5" title: type: - "null" - string description: The title of the problem example: "Not Found" status: pattern: ^-?(?:0|[1-9]\d*)$ type: - "null" - integer - string format: int32 description: The status code of the problem example: 404 detail: type: - "null" - string description: The detail of the problem example: "Category with id 01961eb4-668d-7e7e-ae25-0fab379614f7 not found." instance: type: - "null" - string format: uri description: The instance of the problem example: "/api/v1/categories/01961eb4-668d-7e7e-ae25-0fab379614f7" PublisherDto: type: object readOnly: true description: The publisher of the book properties: id: type: string format: uuid description: The unique identifier of the publisher example: "01961eb7-be10-7235-bed8-2fea41c28c02" name: type: - "string" - "null" example: "O'Reilly Media, Inc." Status: type: string description: The status of the book enum: - InStock - OutOfStock x-enum-descriptions: InStock: The book is available for purchase OutOfStock: The book is currently not available UpdateAuthorCommand: required: - id - name type: object writeOnly: true description: The command to update an author properties: id: type: string format: uuid description: The unique identifier of the author example: "01961eb4-0c29-799f-ab78-ad3e2beaa54a" name: type: string description: The name of the author example: "Neal Ford" maxLength: 50 UpdateCategoryCommand: required: - id - name type: object writeOnly: true description: The command to update a category properties: id: type: string format: uuid description: The unique identifier of the category example: "01961eb2-5a5b-749f-b494-1063d73c96a3" name: type: string description: The name of the category example: "Technology" maxLength: 50 UpdatePublisherCommand: required: - id - name type: object writeOnly: true description: The command to update a publisher properties: id: type: string format: uuid description: The unique identifier of the publisher example: "01961eb7-be10-7235-bed8-2fea41c28c02" name: type: string description: The name of the publisher example: "O'Reilly Media, Inc." maxLength: 50 BooksGrpcRequest: type: object description: Request for getting multiple books via gRPC properties: bookIds: type: array items: type: string format: uuid description: Array of book unique identifiers example: ["01961eb4-668d-7e7e-ae25-0fab379614f7", "01961eb4-0c29-799f-ab78-ad3e2beaa54a"] required: - bookIds BookGrpcResponse: type: object description: gRPC response for a single book properties: id: type: string format: uuid description: The unique identifier of the book example: "01961eb4-668d-7e7e-ae25-0fab379614f7" name: type: string description: The name of the book example: "Software Architecture: The Hard Parts" price: type: number format: double description: The price of the book example: 49.99 priceSale: type: number format: double nullable: true description: The sale price of the book (optional) example: 39.99 status: "$ref": "#/components/schemas/BookGrpcStatus" BooksGrpcResponse: type: object description: gRPC response for multiple books properties: books: type: array items: "$ref": "#/components/schemas/BookGrpcResponse" description: Array of book responses BookGrpcStatus: type: string enum: - InStock - OutOfStock description: The availability status of the book example: "InStock" securitySchemes: OAuth: type: oauth2 description: OAuth2 security scheme for the BookWorm API flows: authorizationCode: authorizationUrl: "https://auth.bookworm.com/realms/bookworm/protocol/openid-connect/auth" tokenUrl: "https://auth.bookworm.com/realms/bookworm/protocol/openid-connect/token" scopes: catalog_read: Read for Catalog API catalog_write: Write for Catalog API x-usePkce: "SHA-256" x-scalar-client-id: "catalog" x-scalar-security-body: audience: "account" tags: - name: Publisher description: Operations related to publishers - name: Category description: Operations related to categories - name: Book description: Operations related to books - name: Author description: Operations related to authors - name: Book gRPC description: gRPC operations for books --- id: RatingService name: Rating Service version: 1.0.0 summary: >- Handles the collection, storage, and aggregation of user feedback and ratings for books on the BookWorm platform badges: - content: Event-Driven textColor: purple backgroundColor: purple icon: BoltIcon - content: PostgreSQL textColor: teal backgroundColor: teal icon: CircleStackIcon sends: - id: FeedbackCreatedEvent version: 1.0.0 to: - &ref_0 id: "catalog.{env}.events" version: 1.0.0 parameters: env: stg - *ref_0 - id: FeedbackDeletedEvent version: 1.0.0 to: - &ref_1 id: "catalog.{env}.events" version: 1.0.0 parameters: env: stg - *ref_1 receives: - id: BookUpdatedRatingFailedEvent version: 1.0.0 from: - &ref_2 id: "rating.{env}.events" version: 1.0.0 parameters: env: stg - *ref_2 schemaPath: openapi-v1.yml specifications: - type: openapi path: openapi-v1.yml name: OpenAPI V1 - type: asyncapi path: asyncapi-v1.yml name: AsyncAPI V1 owners: - nhanxnguyen repository: language: C# url: "https://github.com/foxminchan/BookWorm" writesTo: - id: RatingDatabase version: 1.0.0 readsFrom: - id: RatingDatabase version: 1.0.0 attachments: - url: >- https://foxminchan.github.io/BookWorm/docs/architecture-decisions/adr-004-postgresql-database title: ADR-004 - PostgreSQL Database description: Learn more about the PostgreSQL database choice for the BookWorm platform. type: architecture-decisions icon: FileTextIcon --- ## Overview The Rating Service represents a distinct bounded context within the BookWorm ecosystem, responsible for managing user feedback and ratings for books. As a core domain service, it implements Domain-Driven Design principles to maintain a clean separation of concerns while providing critical functionality for the user experience. ### Key Responsibilities - **Feedback Collection**: Capturing user ratings, reviews, and sentiment analysis - **Rating Aggregation**: Computing real-time rating metrics and statistics - **Content Moderation**: Validating and filtering review content for quality - **Event Broadcasting**: Publishing domain events for system-wide rating updates - **Analytics Support**: Providing rating trends and insights for business intelligence - **Fraud Detection**: Identifying and preventing rating manipulation ## Component Diagram ```mermaid C4Component title Component diagram for Rating Service Container_Boundary(rating, "Rating Service") { Container_Boundary(application, "Application") { Component(ratingEndpoint, "Rating Endpoints", ".NET", "Manages feedback and rating operations") Component(ratingFacade, "Rating Manager", ".NET", "Core business logic for rating management") Component(eventHandler, "Event Handler", ".NET", "Handles event publishing/subscribing") } Container_Boundary(infrastructure, "Infrastructure") { ComponentDb(ratingDb, "Rating DB", "PostgreSQL", "Stores user feedback and ratings") ComponentQueue(eventBus, "Event Bus", "RabbitMQ", "Handles async communication") } } Rel(ratingEndpoint, ratingFacade, "Uses", "Internal") Rel(ratingFacade, ratingDb, "Reads/Writes", "SQL") Rel(ratingFacade, eventHandler, "Uses", "Internal") BiRel(eventHandler, eventBus, "Publishes/Subscribes", "Async") UpdateLayoutConfig($c4ShapeInRow="3", $c4BoundaryInRow="1") ``` ### Domain Events The service emits domain events such as `FeedbackCreatedEvent` and `FeedbackDeletedEvent` to notify other bounded contexts about changes in the rating domain. These events enable loose coupling between services while maintaining data consistency. ### Integration Points Rating Service integrates with: - **Catalog Service**: Updates book rating information - **Notification Service**: Triggers notifications for review responses ### Implementation Details The service follows the CQRS pattern with separate command and query models, enabling optimized read and write operations. The repository pattern is used for data access, ensuring persistence concerns remain separated from domain logic. ## Core Features | Feature | Description | | ------------------- | -------------------------------------------------------------------------------------- | | **Add Feedback** | Allows users to submit feedback and ratings for books, storing them in the database. | | **Delete Feedback** | Supports the removal of user feedback, updating the book's rating metrics accordingly. | | **Get Feedbacks** | Provides an API endpoint to retrieve feedback for a specific book or user. | ## Architecture diagram ## Technical Architecture The Rating Service is built on a modern, cloud-native architecture following these key design principles: ### Command Query Responsibility Segregation (CQRS) ```mermaid flowchart TB subgraph Commands CC[CreateFeedbackCommand] DC[DeleteFeedbackCommand] end subgraph Queries LQ[ListFeedbackQuery] end subgraph Domain F[Feedback Aggregate] end subgraph Infrastructure DB[(Cosmos DB)] end CC --> F DC --> F F --> DB DB --> LQ ``` The service implements CQRS pattern with: - **Command Stack**: Handles write operations through commands like `CreateFeedbackCommand` and `DeleteFeedbackCommand` - **Query Stack**: Manages read operations with queries like `ListFeedbackQuery` - **Mediator Pipeline**: Processes commands and queries with cross-cutting concerns: - Validation behavior - Activity tracking for observability - Logging behavior ### Event-Driven Architecture ```mermaid sequenceDiagram participant Client participant Rating Service participant Event Bus participant Catalog Service Client->>Rating Service: POST /api/v1/feedbacks Rating Service->>Rating Service: Create Feedback Rating Service->>Event Bus: Publish FeedbackCreatedIntegrationEvent Event Bus->>Catalog Service: Consume FeedbackCreatedIntegrationEvent Catalog Service->>Catalog Service: Update Book Rating ``` The service publishes domain events that are transformed into integration events for cross-service communication: - **Domain Events**: `FeedbackCreatedEvent`, `FeedbackDeletedEvent` - **Integration Events**: `FeedbackCreatedIntegrationEvent`, `FeedbackDeletedIntegrationEvent` - **Event Bus**: Implemented with MassTransit for reliable message delivery - **Inbox/Outbox Pattern**: Ensures at-least-once delivery semantics ## Infrastructure This service is built using C# and .NET Core, leveraging Entity Framework Core for data access and Azure Cosmos Database for persistence. The service is containerized using Docker and deployed to Azure Kubernetes Service (AKS) for scalability and reliability. ```mermaid architecture-beta group api(logos:microsoft-azure)[API] service server(logos:docker-icon)[Server] in api service db(logos:postgresql)[Database] in api db:L -- R:server ``` ## Performance Optimizations - **Read Replicas**: Queries directed to read replicas for scalability - **Caching Strategy**: Multi-level caching with Redis for hot data - **Batch Processing**: Aggregated rating updates processed in batches - **Connection Pooling**: Optimized database connections with pooling - **Async Processing**: Non-blocking I/O throughout the stack ## Key Metrics - **Business Metrics** - Average rating per book - Total feedbacks submitted - Moderation approval rate - User engagement rate - **Technical Metrics** - API response times (P50, P95, P99) - Cache hit ratio - Event processing lag - Database query performance - Moderation service latency ## Raw Schema:openapi-v1.yml --- openapi: 3.1.1 info: title: Rating Service API description: Handles the collection, storage, and aggregation of user feedback and ratings for books on the BookWorm platform contact: name: Nhan Nguyen url: https://github.com/foxminchan email: nguyenxuannhan407@gmail.com license: name: MIT url: https://opensource.org/licenses/MIT version: "1.0" externalDocs: description: Documentation url: https://github.com/foxminchan/BookWorm servers: - url: "{protocol}://{environment}.rating.bookworm.com" description: Non-production environment variables: protocol: enum: - https - http default: https environment: enum: - dev - stg - qa default: dev - url: "{protocol}://rating.bookworm.com" description: Production environment variables: protocol: enum: - https default: https security: - OAuth: - rating_read - rating_write paths: "/api/v1/feedbacks/visualize": get: tags: - Rating summary: Visualizer Workflow deprecated: true description: Get the workflow for the visualizer x-eventcatalog-message-type: query operationId: VisualizeWorkflowEndpoint security: - OAuth: - rating_read - rating_write parameters: - name: Type in: query description: The type of visualization to generate schema: $ref: "#/components/schemas/Visualizations" responses: "200": description: OK content: text/plain: schema: type: string description: The workflow in the specified visualization format example: | flowchart TD HandoffStart["HandoffStart (Start)"]; language["language"]; summarize["summarize"]; HandoffEnd["HandoffEnd"]; rating["rating"]; HandoffStart --> language; language --> summarize; language --> HandoffEnd; summarize --> rating; summarize --> HandoffEnd; rating --> language; rating --> HandoffEnd; "400": description: Bad Request content: application/problem+json: schema: "$ref": "#/components/schemas/HttpValidationProblemDetails" examples: invalid-type: summary: "Invalid visualization type" value: type: "https://tools.ietf.org/html/rfc9110#section-15.5.1" title: "Validation failed" status: 400 detail: "One or more validation errors has occurred" errors: - propertyName: "'Type'" errorMessage: "'Type' has a range of values which does not include '4'." attemptedValue: null customState: null severity: 0 errorCode: null formattedMessagePlaceholderValues: null "401": description: Unauthorized - Access token is missing or invalid. "403": description: Forbidden - Access token does not have the required permissions. "/api/v1/feedbacks": get: tags: - Feedback summary: List Feedbacks description: List feedbacks for a book with pagination and filtering x-eventcatalog-message-type: query operationId: ListFeedbacksEndpoint parameters: - name: BookId in: query description: The ID of the book to get feedback for required: true schema: type: string format: uuid description: The unique identifier of the book example: "01961eb4-668d-7e7e-ae25-0fab379614f7" - name: PageIndex in: query description: Number of items to return in a single page of results schema: type: integer format: int32 default: 1 description: The page index to return minimum: 1 example: 1 - name: PageSize in: query description: Number of items to return in a single page of results schema: type: integer format: int32 default: 20 description: The page size to return minimum: 1 example: 20 - name: OrderBy in: query description: Property to order results by schema: type: string default: Rating description: The property to order results by example: Rating - name: IsDescending in: query description: Whether to order results in descending order schema: type: boolean default: false description: Whether to order results in descending order example: false responses: "200": description: OK content: application/json: schema: "$ref": "#/components/schemas/PagedResultOfFeedbackDto" examples: feedbacks-empty: summary: No feedbacks found value: items: [] pageIndex: 1 pageSize: 20 totalItems: 0 totalPages: 0 hasPreviousPage: false hasNextPage: false feedbacks-single: summary: Single feedback value: items: - id: "01961eb4-668d-7e7e-ae25-0fab379614f7" firstName: "John" lastName: "Doe" comment: "This is a comment" rating: 5 bookId: "01961eb4-668d-7e7e-ae25-0fab379614f7" pageIndex: 1 pageSize: 20 totalItems: 1 totalPages: 1 hasPreviousPage: false hasNextPage: false feedbacks-multiple: summary: Multiple feedbacks value: items: - id: "01961eb4-668d-7e7e-ae25-0fab379614f7" firstName: "John" lastName: "Doe" comment: "This is a comment" rating: 5 bookId: "01961eb4-668d-7e7e-ae25-0fab379614f7" - id: "01961eb4-668d-7e7e-ae25-0fab379614f8" firstName: "Jane" lastName: "Doe" comment: "This is another comment" rating: 4 bookId: "01961eb4-668d-7e7e-ae25-0fab379614f7" pageIndex: 1 pageSize: 20 totalItems: 2 totalPages: 1 hasPreviousPage: false hasNextPage: false "400": description: Bad Request content: application/problem+json: schema: "$ref": "#/components/schemas/HttpValidationProblemDetails" examples: invalid-page-index: summary: Invalid page index value: type: "https://tools.ietf.org/html/rfc9110#section-15.5.1" title: "Validation failed" status: 400 detail: "One or more validation errors has occurred" errors: - propertyName: "'PageIndex'" errorMessage: "'PageIndex' must be greater than 0." attemptedValue: null customState: null severity: 0 errorCode: null formattedMessagePlaceholderValues: null invalid-page-size: summary: Invalid page size value: type: "https://tools.ietf.org/html/rfc9110#section-15.5.1" title: "Validation failed" status: 400 detail: "One or more validation errors has occurred" errors: - propertyName: "'PageSize'" errorMessage: "'PageSize' must be greater than 0." attemptedValue: null customState: null severity: 0 errorCode: null formattedMessagePlaceholderValues: null post: tags: - Feedback summary: Create Feedback description: Create a new feedback x-eventcatalog-message-type: command security: - OAuth: - rating_read - rating_write operationId: CreateFeedbackEndpoint requestBody: description: The command to create a new feedback content: application/json: schema: "$ref": "#/components/schemas/CreateFeedbackCommand" example: bookId: "02f1e2d3-4567-890a-bcde-f1234567890a" firstName: "John" lastName: "Doe" comment: "This is a comment" rating: 5 required: true responses: "200": description: OK content: application/json: schema: type: string format: uuid description: The unique identifier of the feedback example: "01961eb4-668d-7e7e-ae25-0fab379614f7" "400": description: Bad Request content: application/problem+json: schema: "$ref": "#/components/schemas/HttpValidationProblemDetails" examples: empty-book-id: summary: Empty book ID value: type: "https://tools.ietf.org/html/rfc9110#section-15.5.1" title: "Validation failed" status: 400 detail: "One or more validation errors has occurred" errors: - propertyName: "'BookId'" errorMessage: "'BookId' must not be empty." attemptedValue: null customState: null severity: 0 errorCode: null formattedMessagePlaceholderValues: null empty-first-name: summary: Empty first name value: type: "https://tools.ietf.org/html/rfc9110#section-15.5.1" title: "Validation failed" status: 400 detail: "One or more validation errors has occurred" errors: - propertyName: "'FirstName'" errorMessage: "'FirstName' must not be empty." attemptedValue: null customState: null severity: 0 errorCode: null formattedMessagePlaceholderValues: null empty-last-name: summary: Empty last name value: type: "https://tools.ietf.org/html/rfc9110#section-15.5.1" title: "Validation failed" status: 400 detail: "One or more validation errors has occurred" errors: - propertyName: "'LastName'" errorMessage: "'LastName' must not be empty." attemptedValue: null customState: null severity: 0 errorCode: null formattedMessagePlaceholderValues: null "/api/v1/feedbacks/{id}/summarize": get: tags: - Feedback summary: Summarize Feedback description: Summarize the feedback for a book by its ID x-eventcatalog-message-type: query operationId: SummarizeFeedbackEndpoint security: - OAuth: - rating_read - rating_write parameters: - name: id in: path description: The unique identifier of the book to be summarized required: true schema: type: string format: uuid description: The unique identifier of the book example: "01961eb4-668d-7e7e-ae25-0fab379614f7" responses: "200": description: OK content: application/json: schema: $ref: "#/components/schemas/SummarizeResult" "401": description: Unauthorized "403": description: Forbidden "404": description: Not Found content: application/problem+json: schema: $ref: "#/components/schemas/ProblemDetails" "/api/v1/feedbacks/{id}": delete: tags: - Feedback summary: Delete Feedback description: Delete a feedback if it exists x-eventcatalog-message-type: command operationId: DeleteFeedbackEndpoint security: - OAuth: - rating_read - rating_write parameters: - name: id in: path description: The unique identifier of the feedback to be deleted required: true schema: type: string format: uuid description: The unique identifier of the feedback to be deleted example: "01961eb4-668d-7e7e-ae25-0fab379614f7" responses: "204": description: No Content "401": description: Unauthorized - Access token is missing or invalid. "403": description: Forbidden - The permission requirements are not met. "404": description: Not Found content: application/problem+json: schema: "$ref": "#/components/schemas/ProblemDetails" examples: feedback-not-found: summary: Feedback not found value: type: "https://tools.ietf.org/html/rfc9110#section-15.5.1" title: "Not found" status: 404 detail: "Feedback with id 01961eb4-668d-7e7e-ae25-0fab379614f7 not found." components: schemas: CreateFeedbackCommand: required: - bookId - firstName - lastName - rating type: object writeOnly: true description: The command to create a new feedback example: bookId: "01961eb4-668d-7e7e-ae25-0fab379614f7" firstName: "John" lastName: "Doe" comment: "This is a comment" rating: 5 properties: bookId: type: string format: uuid description: The unique identifier of the book example: "01961eb4-668d-7e7e-ae25-0fab379614f7" firstName: type: - "string" - "null" description: The first name of the user maxLength: 50 example: "John" lastName: type: string description: The last name of the user maxLength: 50 example: "Doe" comment: type: - "string" - "null" description: The comment of the user maxLength: 1000 example: "This is a comment" rating: type: integer format: int32 description: The rating of the user minimum: 1 maximum: 5 example: 5 FeedbackDto: type: object readOnly: true description: The feedback of the user properties: id: type: string format: uuid description: The unique identifier of the feedback example: "01961eb4-668d-7e7e-ae25-0fab379614f7" firstName: type: string description: The first name of the user example: "John" lastName: type: string description: The last name of the user example: "Doe" comment: type: - "string" - "null" description: The comment of the user example: "This is a comment" rating: type: integer format: int32 description: The rating of the user example: 5 bookId: type: string format: uuid description: The unique identifier of the book example: "01961eb4-668d-7e7e-ae25-0fab379614f7" HttpValidationProblemDetails: type: object readOnly: true x-scalar-ignore: true description: RFC 9110 (https://tools.ietf.org/html/rfc9110#section-15.5.1) properties: type: type: - "null" - string description: The type of the problem example: "https://tools.ietf.org/html/rfc9110#section-15.5.1" title: type: - "null" - string description: The title of the problem example: "Validation failed" status: pattern: ^-?(?:0|[1-9]\d*)$ type: - "null" - integer - string format: int32 description: The status code of the problem example: 400 detail: type: - "null" - string description: The detail of the problem example: "One or more validation errors has occurred" instance: type: - "null" - string format: uri description: The instance of the problem example: "/api/v1/feedbacks" errors: type: object additionalProperties: type: array items: type: string description: The validation errors example: propertyName: "'Name'" errorMessage: "'Name' must not be empty." attemptedValue: null customState: null severity: 0 errorCode: null formattedMessagePlaceholderValues: null PagedResultOfFeedbackDto: type: object readOnly: true description: The paged result of the feedback properties: items: type: array items: "$ref": "#/components/schemas/FeedbackDto" pageIndex: type: integer format: int32 description: The page index example: 1 pageSize: type: integer format: int32 description: The page size example: 20 totalItems: type: integer format: int64 description: The total number of items example: 100 totalPages: type: number format: double description: The total number of pages example: 5 hasPreviousPage: type: boolean description: Whether there is a previous page example: true hasNextPage: type: boolean description: Whether there is a next page example: true ProblemDetails: type: object readOnly: true x-scalar-ignore: true description: RFC 9110 (https://tools.ietf.org/html/rfc9110) properties: type: type: - "null" - string description: The type of the problem example: "https://tools.ietf.org/html/rfc9110#section-15.5.5" title: type: - "null" - string description: The title of the problem example: "Not found" status: pattern: ^-?(?:0|[1-9]\d*)$ type: - "null" - integer - string format: int32 description: The status code of the problem example: 404 detail: type: - "null" - string description: The detail of the problem example: "Feedback with id 01961eb4-668d-7e7e-ae25-0fab379614f7 was not found." instance: type: - "null" - string format: uri description: The instance of the problem example: "/api/v1/feedbacks/01961eb4-668d-7e7e-ae25-0fab379614f7" Visualizations: enum: - Mermaid - Dot default: Mermaid securitySchemes: OAuth: type: oauth2 description: OAuth2 security scheme for the BookWorm API flows: authorizationCode: authorizationUrl: "https://auth.bookworm.com/realms/bookworm/protocol/openid-connect/auth" tokenUrl: "https://auth.bookworm.com/realms/bookworm/protocol/openid-connect/token" scopes: rating_read: Read for Rating API rating_write: Write for Rating API x-usePkce: "SHA-256" x-scalar-client-id: "rating" x-scalar-security-body: audience: "account" tags: - name: Feedback description: Endpoints for managing user ratings and feedback for books on the BookWorm platform ## Raw Schema:asyncapi-v1.yml --- asyncapi: 3.0.0 info: title: Rating Service API version: 1.0.0 description: Handles the collection, storage, and aggregation of user feedback and ratings for books on the BookWorm platform contact: name: Nhan Nguyen url: "https://github.com/foxminchan" license: name: MIT url: "https://opensource.org/licenses/MIT" defaultContentType: application/vnd.masstransit+json servers: development: host: dev.eventbus:5672 protocol: amqp protocolVersion: 0.9.1 summary: RabbitMQ server for development environment description: RabbitMQ server for development environment security: - type: userPassword description: An authentication method for the server tags: - name: env:development description: >- Development environment configuration for local testing and debugging staging: host: stg.eventbus:5672 protocol: amqp protocolVersion: 0.9.1 summary: RabbitMQ server for staging environment description: RabbitMQ server for staging environment security: - type: userPassword description: An authentication method for the server tags: - name: env:staging description: >- Staging environment configuration for testing and debugging qa: host: qa.eventbus:5672 protocol: amqp protocolVersion: 0.9.1 summary: RabbitMQ server for QA environment description: RabbitMQ server for QA environment security: - type: userPassword description: An authentication method for the server tags: - name: env:qa description: >- QA environment configuration for testing and debugging production: host: prod.eventbus:5672 protocol: amqp protocolVersion: 0.9.1 summary: RabbitMQ server for production environment description: RabbitMQ server for production environment security: - type: userPassword description: An authentication method for the server tags: - name: env:production description: >- Production environment configuration for production channels: rating-book-updated-rating-failed: address: rating-book-updated-rating-failed description: Emit message when book rating update failed messages: BookUpdatedRatingFailedIntegrationEvent.message: $ref: "#/components/messages/bookUpdatedRatingFailedIntegrationEvent" catalog-feedback-created: address: catalog-feedback-created description: Emit message when feedback is created messages: FeedbackCreatedEvent.message: $ref: "#/components/messages/feedbackCreatedEvent" catalog-feedback-deleted: address: catalog-feedback-deleted description: Emit message when feedback is deleted messages: FeedbackDeletedEvent.message: $ref: "#/components/messages/feedbackDeletedEvent" operations: BookUpdatedRatingFailedIntegrationEvent: title: Book updated rating failed summary: Book updated rating failed description: Represents a successful integration event when a book rating update fails action: receive channel: $ref: "#/channels/rating-book-updated-rating-failed" messages: - $ref: >- #/channels/rating-book-updated-rating-failed/messages/BookUpdatedRatingFailedIntegrationEvent.message FeedbackCreatedEvent: title: Feedback created summary: Feedback created description: Represents a successful integration event when a feedback is created action: send channel: $ref: "#/channels/catalog-feedback-created" messages: - $ref: >- #/channels/catalog-feedback-created/messages/FeedbackCreatedEvent.message FeedbackDeletedEvent: title: Feedback deleted summary: Feedback deleted description: Represents a successful integration event when a feedback is deleted action: send channel: $ref: "#/channels/catalog-feedback-deleted" messages: - $ref: >- #/channels/catalog-feedback-deleted/messages/FeedbackDeletedEvent.message components: schemas: bookUpdatedRatingFailedIntegrationEvent: id: bookUpdatedRatingFailedIntegrationEvent description: Represents a successful integration event when a book rating update fails allOf: - $ref: "#/components/schemas/integrationEvent" - type: object additionalProperties: false properties: feedbackId: type: string format: guid description: The unique identifier of the feedback example: "01961eb4-668d-7e7e-ae25-0fab379614f7" integrationEvent: id: integrationEvent type: object description: Base event structure containing common metadata for all integration events in the system additionalProperties: false properties: id: type: string format: guid description: The unique identifier of the integration event example: "01961eb4-668d-7e7e-ae25-0fab379614f7" creationDate: type: string format: date-time description: The creation date of the integration event example: "2021-01-01T00:00:00Z" feedbackCreatedEvent: id: feedbackCreatedEvent description: Represents a successful integration event when a feedback is created allOf: - $ref: "#/components/schemas/domainEvent" - type: object additionalProperties: false properties: bookId: type: string format: guid description: The unique identifier of the book example: "01961eb4-668d-7e7e-ae25-0fab379614f7" rating: type: integer format: int32 description: The rating of the feedback example: 5 minimum: 1 maximum: 5 feedbackId: type: string format: guid description: The unique identifier of the feedback example: "01961eb4-668d-7e7e-ae25-0fab379614f7" domainEvent: id: domainEvent description: Base event structure containing common metadata for all domain events in the system type: object x-abstract: true additionalProperties: false properties: dateOccurred: type: string format: date-time feedbackDeletedEvent: id: feedbackDeletedEvent description: Represents a successful integration event when a feedback is deleted allOf: - $ref: "#/components/schemas/domainEvent" - type: object additionalProperties: false properties: bookId: type: string format: guid description: The unique identifier of the book example: "01961eb4-668d-7e7e-ae25-0fab379614f7" rating: type: integer format: int32 description: The rating of the feedback example: 5 minimum: 1 maximum: 5 feedbackId: type: string format: guid description: The unique identifier of the feedback example: "01961eb4-668d-7e7e-ae25-0fab379614f7" messages: bookUpdatedRatingFailedIntegrationEvent: payload: $ref: "#/components/schemas/bookUpdatedRatingFailedIntegrationEvent" name: bookUpdatedRatingFailedIntegrationEvent feedbackCreatedEvent: payload: $ref: "#/components/schemas/feedbackCreatedEvent" name: feedbackCreatedEvent feedbackDeletedEvent: payload: $ref: "#/components/schemas/feedbackDeletedEvent" name: feedbackDeletedEvent --- id: SchedulerService name: Scheduler Service version: 1.0.0 summary: >- Service responsible for scheduling and triggering periodic tasks in the BookWorm e-commerce system. badges: - content: Background Jobs textColor: purple backgroundColor: purple icon: ClockIcon - content: Event Publisher textColor: blue backgroundColor: blue icon: ArrowPathIcon - content: Maintenance textColor: orange backgroundColor: orange icon: WrenchScrewdriverIcon - content: High Priority textColor: red backgroundColor: red icon: ExclamationTriangleIcon sends: - id: CleanUpSentEmailIntegrationEvent version: 1.0.0 to: - &ref_0 id: "notification.{env}.events" version: 1.0.0 parameters: env: stg - *ref_0 - id: ResendErrorEmailIntegrationEvent version: 1.0.0 to: - &ref_1 id: "notification.{env}.events" version: 1.0.0 parameters: env: stg - *ref_1 receives: [] schemaPath: asyncapi-v1.yml specifications: - type: asyncapi path: asyncapi-v1.yml name: AsyncAPI V1 owners: - nhanxnguyen repository: language: C# url: "https://github.com/foxminchan/BookWorm" writesTo: - id: SchedulerDatabase version: 1.0.0 readsFrom: - id: SchedulerDatabase version: 1.0.0 --- ## Overview The Scheduler Service is a critical infrastructure component within BookWorm's [microservices architecture](https://foxminchan.github.io/BookWorm/architecture-decisions/adr-001-microservices-architecture), responsible for managing background jobs and automated maintenance tasks. Built using the TickerQ framework, this service orchestrates time-based operations that ensure system health, performance optimization, and reliable email delivery across the platform. ### Key Responsibilities - **Email Maintenance**: Manages cleanup and retry operations for email notifications - **System Health**: Performs regular maintenance tasks to optimize system performance - **Background Processing**: Executes scheduled jobs without blocking user operations - **Event Publishing**: Triggers integration events for cross-service communication - **Error Recovery**: Implements retry mechanisms for failed operations - **Resource Optimization**: Manages system resources through automated cleanup processes ## Component Diagram ```mermaid C4Component title Component diagram for Scheduler Service Container_Boundary(scheduler, "Scheduler Service") { Container_Boundary(application, "Application") { Component(tickerEngine, "TickerQ Engine", ".NET", "Job scheduling framework") Component(cleanupJob, "Cleanup Job", ".NET", "Email cleanup operations") Component(resendJob, "Resend Job", ".NET", "Email retry operations") Component(eventPublisher, "Event Publisher", ".NET", "Integration event publishing") } Container_Boundary(infrastructure, "Infrastructure") { ComponentDb(schedulerDb, "Scheduler DB", "SQL Server", "Job state and configuration") ComponentQueue(eventBus, "Event Bus", "RabbitMQ", "Async event publishing") } } System_Ext(notificationService, "Notification Service", "Processes scheduler events") Rel(tickerEngine, cleanupJob, "Triggers", "Daily") Rel(tickerEngine, resendJob, "Triggers", "Hourly") Rel(cleanupJob, eventPublisher, "Uses", "Internal") Rel(resendJob, eventPublisher, "Uses", "Internal") Rel(eventPublisher, eventBus, "Publishes", "AMQP") Rel(eventPublisher, schedulerDb, "Logs", "TCP") Rel(eventBus, notificationService, "Events", "Async") UpdateLayoutConfig($c4ShapeInRow="3", $c4BoundaryInRow="1") ``` ## Architecture diagram ## Scheduled Jobs The Scheduler Service manages the following automated tasks: | Job | Schedule | Priority | Purpose | Target Service | | --------------------- | ----------- | -------- | ------------------------------- | -------------------- | | `CleanUpSentEmailJob` | Daily 00:00 | Normal | Cleanup processed email records | Notification Service | | `ResendErrorEmailJob` | Hourly | High | Retry failed email deliveries | Notification Service | ### Job Implementation Details #### CleanUpSentEmailJob - **Class**: `CleanUpSentEmailJob` - **Cron Expression**: `"0 0 * * *"` (Daily at midnight) - **Priority**: Normal - **Implementation**: Publishes `CleanUpSentEmailIntegrationEvent` and saves changes to database #### ResendErrorEmailJob - **Class**: `ResendErrorEmailJob` - **Cron Expression**: `"0 * * * *"` (Every hour) - **Priority**: `TickerTaskPriority.High` - **Implementation**: Publishes `ResendErrorEmailIntegrationEvent` and saves changes to database ### Job Execution Flow ```mermaid stateDiagram-v2 [*] --> JobScheduled: Cron Trigger JobScheduled --> ExecuteJob: TickerQ Engine ExecuteJob --> PublishEvent: Job Logic PublishEvent --> LogExecution: Event Published LogExecution --> SaveChanges: Database Update SaveChanges --> [*]: Job Complete ExecuteJob --> HandleError: Job Failed HandleError --> LogError: Error Handling LogError --> [*]: Job Failed ``` ## Event Publishing The service publishes integration events to trigger downstream processing: ### Email Cleanup Event - **Event**: `CleanUpSentEmailIntegrationEvent` - **Schedule**: Daily at midnight (`0 0 * * *`) - **Purpose**: Triggers cleanup of successfully sent email records - **Target**: Notification Service outbox cleanup ### Email Resend Event - **Event**: `ResendErrorEmailIntegrationEvent` - **Schedule**: Every hour (`0 * * * *`) - **Priority**: High priority processing - **Purpose**: Triggers retry mechanism for failed email deliveries - **Target**: Notification Service error recovery ## Technical Architecture ### Job Framework - **Framework**: TickerQ.Utilities for .NET - **Execution Model**: Background service with cron scheduling using `[TickerFunction]` attributes - **Persistence**: PostgreSQL for job state and operational store - **Dashboard**: Built-in TickerQ Dashboard at `/tickerq` endpoint with basic authentication - **Concurrency**: Max concurrency set to `Environment.ProcessorCount` - **Instance Identification**: Uses `Environment.MachineName` for instance identification ### Database Configuration - **Primary Database**: Azure PostgreSQL via Aspire integration - **Context**: `SchedulerDbContext` implementing `ISchedulerDbContext` - **Outbox Pattern**: EntityFramework outbox for reliable event publishing - **Migrations**: EF Core migrations with operational store support ### Event Integration - **Message Bus**: RabbitMQ via MassTransit - **Pattern**: Outbox pattern with EntityFramework for transactional messaging - **Serialization**: JSON with MassTransit envelope - **Duplicate Detection**: 5-minute duplicate detection window - **Query Delay**: 1-second query delay for outbox processing ## Infrastructure The Scheduler Service is deployed as a containerized application on Microsoft Azure, leveraging Azure PostgreSQL for persistence and RabbitMQ for event publishing. The service includes a built-in dashboard for monitoring job execution. ```mermaid architecture-beta group prod[Production Environment] group dev[Development Environment] service app(logos:docker)[Scheduler Service] in prod service postgres(logos:microsoft-azure)[Azure PostgreSQL] in prod service bus(logos:rabbitmq)[RabbitMQ] in prod service monitor(logos:microsoft-azure)[Azure Monitor] in prod service notification(logos:docker)[Notification Service] in prod service dashboard(logos:docker)[TickerQ Dashboard] in prod service appDev(logos:docker)[Scheduler Service] in dev service postgresDev(logos:postgresql)[PostgreSQL] in dev service busDev(logos:rabbitmq)[RabbitMQ] in dev service notificationDev(logos:docker)[Notification Service] in dev service dashboardDev(logos:docker)[TickerQ Dashboard] in dev app:B --> T:postgres app:R --> L:bus app:L --> R:monitor app:T --> B:dashboard bus:R --> L:notification appDev:B --> T:postgresDev appDev:R --> L:busDev appDev:T --> B:dashboardDev busDev:R --> L:notificationDev ``` ## Monitoring & Observability ### Health Checks - **Database Connectivity**: Verifies PostgreSQL connection via Aspire health checks - **Message Bus**: Confirms RabbitMQ availability through MassTransit health checks - **Job Status**: Monitors active and failed job executions via TickerQ Dashboard ### Logging - **Structured Logging**: Built-in .NET logging with structured data - **Job Execution**: TickerQ framework provides detailed execution logs - **Error Tracking**: Comprehensive error logging with stack traces - **Dashboard Monitoring**: Real-time job status and execution history via `/tickerq` endpoint ### Management Dashboard - **TickerQ Dashboard**: Accessible at `/tickerq` endpoint - **Authentication**: Basic authentication enabled for security - **Features**: Job monitoring, execution history, error tracking, and manual job triggers - **Real-time Updates**: Live job status and execution statistics ### Alerting - **Failed Jobs**: Immediate alerts for job execution failures - **High Error Rate**: Threshold-based alerts for email delivery issues - **Resource Usage**: Memory and CPU utilization monitoring ## Security Considerations - **Database Security**: Encrypted connections with Azure SQL - **Message Security**: TLS encryption for RabbitMQ communication - **Access Control**: Service principal authentication - **Audit Trail**: Complete logging of all job executions - **Resource Limits**: CPU and memory constraints to prevent resource exhaustion ## Performance Optimizations - **Async Processing**: All operations are fully asynchronous - **Connection Pooling**: Reuses database and message bus connections - **Batch Operations**: Groups related operations when possible - **Resource Management**: Proper disposal of resources and connections - **Parallel Execution**: Concurrent job processing with throttling limits ## Raw Schema:asyncapi-v1.yml --- asyncapi: 3.0.0 info: title: Scheduler Service API version: 1.0.0 description: Background job scheduler that manages automated tasks and maintenance operations for the BookWorm platform contact: name: Nhan Nguyen url: "https://github.com/foxminchan" license: name: MIT url: "https://opensource.org/licenses/MIT" defaultContentType: application/vnd.masstransit+json servers: development: host: dev.eventbus:5672 protocol: amqp protocolVersion: 0.9.1 summary: RabbitMQ server for development environment description: RabbitMQ server for development environment security: - type: userPassword description: An authentication method for the server tags: - name: env:development description: >- Development environment configuration for local testing and debugging staging: host: stg.eventbus:5672 protocol: amqp protocolVersion: 0.9.1 summary: RabbitMQ server for staging environment description: RabbitMQ server for staging environment security: - type: userPassword description: An authentication method for the server tags: - name: env:staging description: >- Staging environment configuration for testing and debugging qa: host: qa.eventbus:5672 protocol: amqp protocolVersion: 0.9.1 summary: RabbitMQ server for QA environment description: RabbitMQ server for QA environment security: - type: userPassword description: An authentication method for the server tags: - name: env:qa description: >- QA environment configuration for testing and debugging production: host: prod.eventbus:5672 protocol: amqp protocolVersion: 0.9.1 summary: RabbitMQ server for production environment description: RabbitMQ server for production environment security: - type: userPassword description: An authentication method for the server tags: - name: env:production description: >- Production environment configuration for production channels: "BookWorm.Contracts:CleanUpSentEmailIntegrationEvent": address: "BookWorm.Contracts:CleanUpSentEmailIntegrationEvent" description: Event emitted to trigger cleanup of sent emails for system maintenance messages: CleanUpSentEmailIntegrationEvent.message: $ref: "#/components/messages/cleanUpSentEmailIntegrationEvent" "BookWorm.Contracts:ResendErrorEmailIntegrationEvent": address: "BookWorm.Contracts:ResendErrorEmailIntegrationEvent" description: Event emitted to trigger resending of failed emails messages: ResendErrorEmailIntegrationEvent.message: $ref: "#/components/messages/resendErrorEmailIntegrationEvent" operations: CleanUpSentEmailIntegrationEvent: title: Clean up sent email summary: Clean up sent email description: Represents an integration event to trigger cleanup of sent emails for system maintenance action: send channel: $ref: "#/channels/BookWorm.Contracts:CleanUpSentEmailIntegrationEvent" messages: - $ref: >- #/channels/BookWorm.Contracts:CleanUpSentEmailIntegrationEvent/messages/CleanUpSentEmailIntegrationEvent.message ResendErrorEmailIntegrationEvent: title: Resend error email summary: Resend error email description: Represents an integration event to trigger resending of failed emails action: send channel: $ref: "#/channels/BookWorm.Contracts:ResendErrorEmailIntegrationEvent" messages: - $ref: >- #/channels/BookWorm.Contracts:ResendErrorEmailIntegrationEvent/messages/ResendErrorEmailIntegrationEvent.message components: schemas: cleanUpSentEmailIntegrationEvent: id: cleanUpSentEmailIntegrationEvent description: Represents an integration event to trigger cleanup of sent emails for system maintenance allOf: - $ref: "#/components/schemas/integrationEvent" - type: object additionalProperties: false integrationEvent: id: integrationEvent type: object description: Base event structure containing common metadata for all integration events in the system additionalProperties: false properties: id: type: string format: guid description: The unique identifier of the integration event example: "01961eb4-668d-7e7e-ae25-0fab379614f7" creationDate: type: string format: date-time description: The creation date of the integration event example: "2021-01-01T00:00:00Z" resendErrorEmailIntegrationEvent: id: resendErrorEmailIntegrationEvent description: Represents an integration event to trigger resending of failed emails allOf: - $ref: "#/components/schemas/integrationEvent" - type: object additionalProperties: false messages: cleanUpSentEmailIntegrationEvent: payload: $ref: "#/components/schemas/cleanUpSentEmailIntegrationEvent" name: cleanUpSentEmailIntegrationEvent resendErrorEmailIntegrationEvent: payload: $ref: "#/components/schemas/resendErrorEmailIntegrationEvent" name: resendErrorEmailIntegrationEvent --- id: catalog name: Catalog summary: The Catalog domain manages products and user reviews for the BookWorm platform version: 1.0.0 services: - id: ProductService version: 1.0.0 - id: RatingService version: 1.0.0 - id: ChatService version: 1.0.0 entities: - id: Book version: 1.0.0 - id: Author version: 1.0.0 - id: BookAuthor version: 1.0.0 - id: Category version: 1.0.0 - id: Publisher version: 1.0.0 - id: Feedback version: 1.0.0 - id: ChatHistoryItem version: 1.0.0 badges: - content: Subdomain backgroundColor: blue textColor: blue icon: BoltIcon - content: Product Management backgroundColor: purple textColor: white icon: BookOpenIcon - content: User Reviews backgroundColor: red textColor: white icon: StarIcon - content: Chat backgroundColor: green textColor: white icon: ChatBubbleBottomCenterTextIcon owners: - nhanxnguyen --- ## Overview The Catalog domain is responsible for managing the products that are available for purchase in the BookWorm platform and handling product ratings and reviews. Built following our microservices architecture principles, it consists of three main services: 1. **Product Service**: Serves as the central repository for all book-related information including metadata, inventory status, pricing, and categorization. It allows customers to browse the extensive collection of books, view detailed product information, and search based on various criteria. 2. **Rating Service**: Manages customer reviews, ratings, and feedback for books in the catalog. It provides functionality for users to rate books, write reviews, and view aggregated rating statistics. 3. **Chat Service**: Provides AI-powered conversational capabilities for book recommendations and customer support, implementing our [AI Integration Strategy](https://foxminchan.github.io/BookWorm/architecture-decisions/adr-009-ai-integration). The domain provides robust management capabilities for product categories, series, collections, publishers, and user-generated content. It maintains relationships between books, such as series orders, related titles, author bibliographies, and user reviews. Data persistence follows our [PostgreSQL database strategy](https://foxminchan.github.io/BookWorm/architecture-decisions/adr-004-postgresql-database), while service communication uses [event-driven patterns](https://foxminchan.github.io/BookWorm/architecture-decisions/adr-002-event-driven-cqrs) for loose coupling. For administrators, the Catalog domain offers comprehensive management tools for both product information and user-generated content, allowing them to: - Add and update book information - Manage inventory levels - Control pricing and discounts - Moderate user reviews and ratings - Generate reports on product performance and user feedback As a core domain in the BookWorm ecosystem, Catalog integrates with other services like the Basket domain for purchasing flows and the Ordering domain for inventory verification during checkout processes. ## Bounded context ### Entity Map --- id: integration name: Integration summary: Cross-cutting concerns and integrations between different services in the BookWorm e-commerce system version: 1.0.0 services: - id: MCPTools version: 1.0.0 - id: SchedulerService version: 1.0.0 badges: - content: Subdomain backgroundColor: blue textColor: blue icon: BoltIcon - content: Integration backgroundColor: purple textColor: white icon: LinkIcon - content: AI-Powered backgroundColor: green textColor: white icon: CpuChipIcon - content: Background Jobs backgroundColor: orange textColor: white icon: ClockIcon owners: - nhanxnguyen --- ## Overview The Integration subdomain in the BookWorm system serves as the central hub for cross-cutting concerns and facilitates seamless interactions between various services and external systems. This subdomain encapsulates two critical aspects of the platform: AI-powered integrations through the Model Context Protocol (MCP) and automated system maintenance through background job scheduling. Built following our [microservices architecture](https://foxminchan.github.io/BookWorm/architecture-decisions/adr-001-microservices-architecture) principles, this subdomain ensures that the overall system operates cohesively and efficiently while maintaining clean boundaries with other bounded contexts. ### Strategic Importance The Integration subdomain addresses several key business and technical requirements: - **AI Integration**: Enables AI-powered applications to interact with BookWorm catalog data through standardized protocols - **System Reliability**: Provides automated maintenance and cleanup operations to ensure system health - **Cross-Service Communication**: Facilitates seamless event-driven communication between services - **External System Integration**: Manages connections and interactions with third-party services and AI platforms ## Services Architecture ```mermaid C4Container title Integration Subdomain - Container Diagram Container_Boundary(integration, "Integration Subdomain") { Container(mcpTools, "MCP Tools", "ASP.NET Core", "Model Context Protocol server for AI integrations") Container(scheduler, "Scheduler Service", "ASP.NET Core + TickerQ", "Background job scheduler for maintenance") } Container_Boundary(external, "External Systems") { Container(aiClients, "AI Clients", "Claude, OpenAI", "AI applications using MCP protocol") Container(catalogService, "Catalog Service", "ASP.NET Core", "BookWorm catalog data source") Container(notificationService, "Notification Service", "ASP.NET Core", "Email notification handling") } Container_Boundary(infrastructure, "Infrastructure") { ContainerDb(postgres, "PostgreSQL", "Database", "Job state and operational data") ContainerQueue(eventBus, "RabbitMQ", "Message Bus", "Event-driven communication") } Rel(aiClients, mcpTools, "MCP Protocol", "HTTP/JSON-RPC") Rel(mcpTools, catalogService, "API Calls", "HTTP/REST") Rel(scheduler, eventBus, "Publishes Events", "AMQP") Rel(eventBus, notificationService, "Integration Events", "AMQP") Rel(scheduler, postgres, "Job State", "TCP") UpdateLayoutConfig($c4ShapeInRow="2", $c4BoundaryInRow="1") ``` ## Service Details ### MCP Tools Service **Purpose**: AI-powered Model Context Protocol server enabling AI applications to interact with BookWorm catalog data **Key Features**: - **MCP Protocol Implementation**: Full HTTP-based MCP server following specification - **Tool Registry**: Auto-discovery of MCP tools via .NET attributes - **Catalog Integration**: Seamless integration with BookWorm Catalog Service - **AI Assistant Support**: Pre-defined prompts for customer service interactions - **OpenAPI Documentation**: Self-documenting API with MCP extensions **Integration Points**: - External AI clients (Claude, OpenAI assistants, etc.) - BookWorm Catalog Service for book data retrieval - OpenTelemetry for observability ### Scheduler Service **Purpose**: Background job scheduler managing automated maintenance tasks and system health operations **Key Features**: - **TickerQ Framework**: Robust job scheduling with cron expressions - **Event-Driven**: Publishes integration events for cross-service communication - **Dashboard**: Built-in monitoring and management interface - **Outbox Pattern**: Reliable event publishing with transactional guarantees - **High Availability**: Instance identification and missed job recovery **Scheduled Jobs**: - **Email Cleanup**: Daily cleanup of processed email records (`0 0 * * *`) - **Email Resend**: Hourly retry of failed email deliveries (`0 * * * *`) ## Event Flow ```mermaid sequenceDiagram participant S as Scheduler Service participant E as Event Bus participant N as Notification Service participant AI as AI Client participant M as MCP Tools participant C as Catalog Service Note over S,N: Background Maintenance Flow S->>E: CleanUpSentEmailIntegrationEvent (Daily) S->>E: ResendErrorEmailIntegrationEvent (Hourly) E->>N: Process cleanup/resend events Note over AI,C: AI Integration Flow AI->>M: MCP Tool Request (SearchCatalog) M->>C: HTTP API Call (Book Search) C-->>M: Book Data Response M-->>AI: MCP Tool Response ``` ## Integration Patterns The Integration subdomain employs several key patterns: ### Event-Driven Architecture - **Publisher**: Scheduler Service publishes maintenance events - **Consumer**: Notification Service processes scheduler events - **Reliability**: Outbox pattern ensures reliable event delivery ### Protocol Gateway - **MCP Server**: Standardized interface for AI applications - **HTTP Transport**: Stateless communication for scalability - **Tool Discovery**: Attribute-based registration of available tools ### Background Processing - **Cron Scheduling**: Time-based job execution - **Transactional**: Database operations with event publishing - **Monitoring**: Built-in dashboard for operational visibility ## Technology Stack | Component | Technology | Purpose | | -------------------- | ------------------------------- | ------------------------------- | | **Runtime** | .NET | Core application framework | | **Job Scheduling** | TickerQ | Background job processing | | **MCP Protocol** | ModelContextProtocol.AspNetCore | AI integration server | | **Database** | PostgreSQL | Job state and operational data | | **Message Bus** | RabbitMQ + MassTransit | Event-driven communication | | **HTTP Client** | Refit | Type-safe API integration | | **Observability** | OpenTelemetry | Distributed tracing and metrics | | **Containerization** | Docker | Deployment and scaling | ## Business Value ### Operational Excellence - **Automated Maintenance**: Reduces manual operations through scheduled cleanup - **System Reliability**: Proactive error recovery and system health monitoring - **Monitoring**: Real-time visibility into background operations ### AI Integration - **Standardized Protocol**: Industry-standard MCP for AI tool integration - **Extensible**: Easy addition of new AI-powered tools and capabilities - **Performance**: Optimized for AI assistant response times ### Developer Experience - **Self-Documenting**: OpenAPI specifications for both services - **Type Safety**: Strong typing throughout the integration layer - **Testability**: Clean architecture supporting comprehensive testing ## Security & Compliance ### Authentication & Authorization - **Basic Auth**: TickerQ Dashboard protected access - **Service-to-Service**: Secure communication between internal services - **AI Client Auth**: Configurable authentication for MCP clients ### Data Protection - **Encryption**: TLS for all external communications - **Audit Trail**: Comprehensive logging of all operations - **Data Minimization**: Only necessary data exposed through integrations ### Operational Security - **Resource Limits**: Configured constraints on job execution - **Error Handling**: Graceful degradation and error recovery - **Monitoring**: Real-time alerting on security events --- id: orders name: Orders summary: Manages the lifecycle of customer orders in the BookWorm e-commerce system version: 1.0.0 services: - id: OrderingService version: 1.0.0 - id: BasketService version: 1.0.0 - id: FinanceService version: 1.0.0 - id: NotificationService version: 1.0.0 entities: - id: CustomerBasket version: 1.0.0 - id: BasketItem version: 1.0.0 - id: OutBox version: 1.0.0 - id: Buyer version: 1.0.0 - id: Order version: 1.0.0 - id: OrderItem version: 1.0.0 badges: - content: Subdomain backgroundColor: blue textColor: blue icon: BoltIcon - content: Order Management backgroundColor: gray textColor: white icon: ListBulletIcon - content: Shopping Cart backgroundColor: green textColor: white icon: ShoppingCartIcon - content: Finance Processing backgroundColor: orange textColor: white icon: BanknotesIcon - content: System Notifications backgroundColor: red textColor: white icon: BellIcon owners: - nhanxnguyen --- ## Overview The Ordering domain represents a strategic core domain within the BookWorm system, responsible for managing the complete lifecycle of customer orders from creation through fulfillment. Built on our [microservices architecture](https://foxminchan.github.io/BookWorm/architecture-decisions/adr-001-microservices-architecture), it embodies critical business capabilities that directly impact the organization's competitive advantage. ### Domain Characteristics - **Bounded Context**: The Ordering domain has a well-defined boundary with explicit integration points between its constituent services (Ordering, Basket, Finance, and Notification) through gRPC communication, managed via our [API Gateway](https://foxminchan.github.io/BookWorm/architecture-decisions/adr-008-api-gateway). - **Service Architecture**: The domain is composed of four specialized services: - **Ordering Service**: Core service managing order lifecycle and state - **Basket Service**: Handles shopping cart and item management - **Finance Service**: Manages payment processing and financial transactions - **Notification Service**: Handles customer communications and alerts - **Event Sourcing**: The domain implements event sourcing as its persistence mechanism following our [event-driven CQRS pattern](https://foxminchan.github.io/BookWorm/architecture-decisions/adr-002-event-driven-cqrs), storing the complete history of order-related events rather than just the current state. This approach provides a comprehensive audit trail and enables temporal queries and state reconstruction at any point in time. - **Data Storage**: Uses [PostgreSQL as the primary database](https://foxminchan.github.io/BookWorm/architecture-decisions/adr-004-postgresql-database) for transactional data and Redis for caching and session management. ### Business Rules and Invariants - Orders must contain at least one item to be submitted - Order status transitions follow a predefined state machine (e.g., Placed -> Completed or Cancelled) - Cancellation is only permitted before the "Shipped" status - Price calculations and summaries are validated against business rules - Basket items must be reserved before order confirmation - Financial transactions must be atomic and consistent The domain handles complex business scenarios like partial fulfillment, order modifications, and cancellations while maintaining consistency and enforcing business rules throughout the order lifecycle. ## Architecture diagram ### Entity Map ## Ordering process (sequence diagram) ```mermaid sequenceDiagram actor Customer participant OrderingService participant FinanceService participant BasketService participant NotificationService Customer->>OrderingService: Place Order OrderingService->>BasketService: Get Basket Items BasketService-->>OrderingService: Return Basket Items OrderingService->>FinanceService: Process Payment FinanceService->>BasketService: Reserve Basket Items alt Basket Reserved Successfully BasketService->>FinanceService: Confirm Reservation FinanceService->>OrderingService: Confirm Payment par OrderingService->>NotificationService: Send Order Confirmation NotificationService-->>Customer: Order Placed Successfully and OrderingService->>BasketService: Clear Basket end else Basket Reservation Failed BasketService->>FinanceService: Reservation Failed FinanceService->>OrderingService: Payment Failed OrderingService->>Customer: Order Placement Failed end ``` --- id: store name: Store summary: The core business domain encompassing all e-commerce operations for BookWorm version: 1.0.0 owners: - nhanxnguyen - full-stack domains: - id: orders - id: catalog - id: integration badges: - content: Core domain backgroundColor: blue textColor: blue icon: RectangleGroupIcon - content: Business Critical backgroundColor: red textColor: red icon: ShieldCheckIcon - content: Event-Driven textColor: purple backgroundColor: purple icon: BoltIcon - content: Microservices textColor: blue backgroundColor: blue icon: CubeIcon repository: language: C# url: "https://github.com/foxminchan/BookWorm" --- ## Overview The Store domain represents the heart of BookWorm's business operations, orchestrating all aspects of the online bookstore experience from product discovery to order fulfillment. ## 🎯 Domain Vision To create a seamless, scalable, and maintainable e-commerce platform that delights book lovers while demonstrating modern software architecture best practices. ## 🏗️ Strategic Design ### Domain Boundaries The Store domain is strategically divided into two subdomains based on our [microservices architecture](https://foxminchan.github.io/BookWorm/architecture-decisions/adr-001-microservices-architecture): 1. **[Catalog Subdomain](/docs/domains/catalog/1.0.0)** - Core subdomain - Product information management - Search and discovery - Ratings and reviews - AI-powered recommendations 2. **[Orders Subdomain](/docs/domains/orders/1.0.0)** - Core subdomain - Shopping cart management - Order processing workflow - Payment orchestration - Customer notifications ### Ubiquitous Language | Term | Definition | | ----------- | -------------------------------------------------------- | | **Book** | A physical or digital publication available for purchase | | **Basket** | A temporary collection of items selected by a customer | | **Order** | A confirmed purchase request with payment information | | **Catalog** | The complete inventory of available books | | **Rating** | Customer feedback including stars and written reviews | | **Saga** | A distributed transaction pattern for order processing | ## 💼 Event Storming We conducted an [event storming](https://www.eventstorming.com/) session for the Store domain to understand its complexities and subdomain interactions. This session helped us map key events, commands, and aggregates, aligning our technical implementation with business needs for robust functionality. Our implementation follows an [event-driven architecture with CQRS pattern](https://foxminchan.github.io/BookWorm/architecture-decisions/adr-002-event-driven-cqrs) to ensure scalability and loose coupling between services. ## 📊 High-level architecture To effectively visualize and understand the architecture of the BookWorm system, we will employ the [C4 model](https://c4model.com/). This model allows us to represent the system at various levels of abstraction, providing a comprehensive view of its structure and interactions. Our architecture follows several key architectural decisions: - [Microservices Architecture](https://foxminchan.github.io/BookWorm/architecture-decisions/adr-018-k6-performance-testing) for service boundaries - [Aspire for Cloud-Native Development](https://foxminchan.github.io/BookWorm/architecture-decisions/adr-003-aspire-cloud-native) for orchestration - [Container-First Deployment Strategy](https://foxminchan.github.io/BookWorm/architecture-decisions/adr-007-aca-deployment) for consistency - [API Gateway Pattern](https://foxminchan.github.io/BookWorm/architecture-decisions/adr-008-api-gateway) for unified access ### System Context Diagram ```mermaid C4Context title System Context Diagram for BookWorm Store System Enterprise_Boundary(bookworm, "BookWorm Store") { Person(customer, "Customer", "A user who wants to browse and purchase books") System_Boundary(application, "Application") { System(bookworm, "BookWorm Store", "Business critical system") } System_Boundary(external, "External Systems") { System_Ext(email, "Email Service", "Handles email notifications") System_Ext(identity, "Identity Provider", "Handles user authentication") } System_Boundary(infrastructure, "Infrastructure") { ContainerDb(database, "Database", "Stores information") SystemQueue(eventBus, "Event Bus", "Handles message passing between services") } } Rel(customer, bookworm, "Makes requests", "HTTPS") Rel(bookworm, email, "Sends emails", "SMTP") Rel(bookworm, identity, "Authenticates users", "OAuth2") Rel(bookworm, database, "Reads/Writes", "TCP") Rel(bookworm, eventBus, "Publishes/Subscribes", "AMQP") UpdateLayoutConfig($c4ShapeInRow="3", $c4BoundaryInRow="2") ``` ### Container Diagram ```mermaid C4Container title Container Diagram for BookWorm Store System Person(customer, "Customer", "A user browsing and purchasing books") System_Ext(email, "Email Service", "Email notifications") System_Ext(identity, "Identity Provider", "User authentication") Container(gateway, "API Gateway", ".NET", "Unified entry point for all requests") Container(catalogServices, "Catalog Services", ".NET", "Product catalog, ratings, and chat") Container(orderServices, "Order Services", ".NET", "Orders, basket, and payments") Container(supportServices, "Support Services", ".NET", "Notifications, scheduling, and AI tools") ContainerDb(databases, "Databases", "SQL/NoSQL", "Persistent data storage") SystemQueue(eventBus, "Event Bus", "AMQP", "Async messaging between services") Rel(customer, gateway, "Uses", "HTTPS") Rel(gateway, identity, "Authenticates", "OAuth2") Rel(gateway, catalogServices, "Routes to", "HTTPS") Rel(gateway, orderServices, "Routes to", "HTTPS") Rel(catalogServices, databases, "Reads/Writes", "TCP") Rel(orderServices, databases, "Reads/Writes", "TCP") Rel(supportServices, databases, "Reads/Writes", "TCP") BiRel(catalogServices, eventBus, "Pub/Sub", "AMQP") BiRel(orderServices, eventBus, "Pub/Sub", "AMQP") BiRel(supportServices, eventBus, "Pub/Sub", "AMQP") Rel(orderServices, catalogServices, "Gets product info", "gRPC") Rel(supportServices, email, "Sends emails", "SMTP") UpdateLayoutConfig($c4ShapeInRow="3", $c4BoundaryInRow="1") ``` The data storage strategy is detailed in our [PostgreSQL database decision](https://foxminchan.github.io/BookWorm/architecture-decisions/adr-004-postgresql-database), with event-driven communication patterns outlined in our [Event-Driven CQRS approach](https://foxminchan.github.io/BookWorm/architecture-decisions/adr-002-event-driven-cqrs). AI-powered features are implemented following our [AI Integration Strategy](https://foxminchan.github.io/BookWorm/architecture-decisions/adr-009-ai-integration), while real-time communication uses [SignalR](https://foxminchan.github.io/BookWorm/architecture-decisions/adr-006-signalr-realtime). ## 🔄 Interactive Architecture Diagram The following diagram provides an interactive view of the architecture of the BookWorm system. You can explore the components, their relationships, and the flow of data between them. ## Current Production Architecture Below is the interactive event-driven architecture diagram for the BookWorm Store system. ## 📈 Domain Metrics ### Service Level Objectives (SLOs) | Metric | Target | Description | | ---------------------- | ------- | ----------------------------- | | **Availability** | 99.9% | Uptime for critical services | | **Response Time** | < 200ms | P95 latency for API calls | | **Order Success Rate** | > 98% | Successful order completions | | **Event Processing** | < 1s | Time to process domain events | ### Key Performance Indicators (KPIs) - **Orders per Hour**: Average throughput capacity - **Catalog Search Speed**: Time to return search results - **Cart Abandonment Rate**: Percentage of uncompleted orders - **Service Health Score**: Composite reliability metric ## 📈 Domain Metrics ### Service Level Objectives (SLOs) | Metric | Target | Description | | ---------------------- | ------- | ----------------------------- | | **Availability** | 99.9% | Uptime for critical services | | **Response Time** | < 200ms | P95 latency for API calls | | **Order Success Rate** | > 98% | Successful order completions | | **Event Processing** | < 1s | Time to process domain events | ### Key Performance Indicators (KPIs) - **Orders per Hour**: Average throughput capacity - **Catalog Search Speed**: Time to return search results - **Cart Abandonment Rate**: Percentage of uncompleted orders - **Service Health Score**: Composite reliability metric ## 🛡️ Security & Compliance ### Security Measures - **Authentication**: JWT-based user authentication via [Keycloak Identity Management](https://foxminchan.github.io/BookWorm/architecture-decisions/adr-005-keycloak-identity) - **Authorization**: Role-based access control (RBAC) - **Encryption**: TLS 1.3 for all communications - **Data Protection**: PCI DSS compliance for payments ### Audit & Compliance - Complete audit trail via event sourcing - GDPR compliance for user data - Regular security assessments - Automated compliance checking ## 🔍 Observability Our observability strategy is built on modern cloud-native principles, following our [Aspire cloud-native development approach](https://foxminchan.github.io/BookWorm/architecture-decisions/adr-003-aspire-cloud-native) and [container-first deployment strategy](https://foxminchan.github.io/BookWorm/architecture-decisions/adr-007-aca-deployment). ### Three Pillars 1. **Metrics**: Prometheus + Grafana dashboards 2. **Logging**: Structured logging with Serilog 3. **Tracing**: Distributed tracing with OpenTelemetry ### Monitoring Dashboards - Service health and availability - Business metrics and KPIs - Performance bottleneck detection - Error rate tracking --- id: full-stack name: Full Stack Engineering Team summary: The Full Stack Engineering Team is responsible for the complete technology stack of BookWorm, including back-end services, infrastructure, and DevOps practices. members: - nhanxnguyen owners: - nhanxnguyen --- # Full Stack Engineering Team ## Team Overview The Full Stack Engineering Team forms the backbone of BookWorm's technical implementation, responsible for the complete technology stack from back-end services to infrastructure and DevOps practices. Our multidisciplinary team combines expertise in .NET microservices, cloud infrastructure, and modern development methodologies to deliver a robust, scalable e-commerce platform. ## Core Competencies ### Back-End Development - **Microservices Architecture**: Designing and implementing loosely coupled, highly cohesive services - **.NET Core Expertise**: Building high-performance APIs and services using the latest .NET technologies - **Domain-Driven Design**: Implementing bounded contexts and strategic design patterns - **CQRS & Event Sourcing**: Applying advanced architectural patterns for complex domains - **Data Persistence**: Working with SQL Server, PostgreSQL, MongoDB, and Redis for various data needs ### Infrastructure & DevOps - **Cloud Platform Engineering**: Designing and implementing Azure-based infrastructure - **Containerization**: Deploying applications using Docker and Kubernetes - **Infrastructure as Code**: Managing infrastructure using Terraform and Azure Resource Manager - **CI/CD Pipelines**: Automating build, test, and deployment processes - **Monitoring & Observability**: Implementing comprehensive monitoring with Application Insights and Prometheus ### Security & Compliance - **Identity Management**: Implementing OAuth 2.0 and OpenID Connect protocols - **API Security**: Securing APIs with proper authentication and authorization - **Data Protection**: Ensuring compliance with data protection regulations - **DevSecOps**: Integrating security practices throughout the development lifecycle ## BookWorm Contributions The Full Stack team has been instrumental in: 1. **Architectural Foundation**: Establishing the microservices architecture and communication patterns 2. **Core Services Implementation**: - Developing the Catalog service for book inventory management - Building the Ordering service with complex business workflows - Creating the Rating service for customer feedback - Implementing the Basket service for shopping cart functionality 3. **Infrastructure Setup**: - Designing the Azure-based cloud infrastructure - Implementing Kubernetes clusters for container orchestration - Setting up CI/CD pipelines for automated deployments - Configuring monitoring and alerting systems 4. **Technical Documentation**: - Creating comprehensive API documentation - Documenting architectural decisions and patterns - Developing this EventCatalog for event-driven communication ## Development Practices Our team follows industry best practices: - **Agile Methodology**: Working in 2-week sprints with daily stand-ups - **Test-Driven Development**: Writing tests before implementation - **Pair Programming**: Collaborating on complex problems - **Code Reviews**: Ensuring code quality through peer reviews - **Continuous Integration**: Automatically building and testing code changes - **Continuous Deployment**: Deploying to production multiple times per week ## Technologies & Tools | Category | Technologies | | ---------- | ------------------------------------------------ | | Languages | C#, TypeScript | | Frameworks | ASP.NET Core, Entity Framework Core, Mediator | | Databases | SQL Server, PostgreSQL, MongoDB, Redis | | Messaging | RabbitMQ | | Cloud | Azure (AKS, App Service, Functions, CosmosDB) | | DevOps | GitHub Actions, Azure DevOps, Docker, Kubernetes | --- id: nhanxnguyen name: Nhan Nguyen summary: .NET Developer | DevSecOps Enthusiast | Cloud Native Advocate avatarUrl: https://avatars.githubusercontent.com/u/56079798?v=4 role: Software Engineer owners: - nhanxnguyen --- Hello, I'm a .NET enthusiast with a strong passion for DevSecOps and Cloud Native solutions. I specialize in developing efficient, scalable applications that address both business and security requirements. ## About Me I'm a software engineer at KMS Technology, where I work on projects focused on Healthcare domain. I have experience in developing and maintaining .NET applications, with a strong emphasis on security and performance. I'm also interested in cloud-native technologies and DevSecOps practices. ## Contact - [GitHub](https://github.com/foxminchan) - [LinkedIn](https://www.linkedin.com/in/nxnhan/) --- _Nhan Nguyen_ _Software Engineer @ [KMS Technology](https://kms-technology.com/)_ --- id: Author name: Author version: 1.0.0 identifier: id aggregateRoot: true summary: Author domain entity, encapsulates author information and business rules properties: - name: id type: UUID required: true description: Unique identifier for the author - name: name type: string required: true description: A name of the author owners: - nhanxnguyen --- ## Overview The author entity represents a person who writes books. It encapsulates the author's information, including the unique identifier, name, and a collection of books written by the author. The entity is responsible for maintaining its internal state and enforcing its own business rules. For example, when a book is added to the author's collection, the entity will validate that the book was written by the author and that the book is not already part of the collection. The entity also provides a way to retrieve the list of books written by the author, which can be used to display the author's bibliography. ### Entity Properties ### Entity Relationships - An author writes multiple **books** --- id: BasketItem name: BasketItem version: 1.0.0 identifier: id aggregateRoot: false summary: Basket item domain entity, encapsulates basket item information and business rules properties: - name: id type: UUID required: true description: Unique identifier for the book references: Book relationType: belongsTo referencesIdentifier: id - name: quantity type: integer required: true description: Quantity of the book in the basket owners: - nhanxnguyen --- ## Overview The basket item entity represents a basket item in the orders. It encapsulates basket item information and business rules such as a unique identifier, a book identifier, and a quantity. A basket item is an aggregate root that captures the user's intent to purchase a specific book. The entity enforces invariants across its properties and manages the lifecycle of the book in the basket. ### Entity Properties ### Entity Relationships - A basket item is associated with one **book** - A basket item is associated with one **basket** --- id: Book name: Book version: 1.0.0 identifier: id aggregateRoot: true summary: Book domain entity, encapsulates book information and business rules properties: - name: id type: UUID required: true description: Unique identifier for the book - name: name type: string required: true description: A title of the book - name: description type: string required: false description: Description of the book - name: image type: string required: false description: Image of the book - name: price type: Price required: true description: Price of the book include Original price and Sale price - name: status type: int required: true description: Status of the book - name: averageRating type: double required: true description: Average rating of the book - name: totalReviews type: int required: true description: Total number of reviews for the book - name: categoryId type: UUID required: true description: Unique identifier for the category references: Category relationType: hasOne referencesIdentifier: id - name: publisherId type: UUID required: true description: Unique identifier for the publisher references: Publisher relationType: hasOne referencesIdentifier: id - name: isDeleted type: bool required: true description: Whether the book is deleted or not - name: createdAt type: DateTime required: true description: The date and time the book was created - name: lastModifiedAt type: DateTime required: false description: The date and time the book was updated - name: version type: UUID required: true description: Version of the record for optimistic concurrency control owners: - nhanxnguyen --- ## Overview This entity represents a book in the catalog. It encapsulates book information and business rules, such as a unique identifier, a title, a description, an image, a price, a status, an average rating, a total number of reviews, a category, a publisher, and a flag indicating whether the book is deleted or not. The entity also has a version property for optimistic concurrency control. ### Entity Properties ### Entity Relationships - A book is categorized under one **category** - A book is published by one **publisher** - A book is written by multiple **authors** - A book has multiple **reviews** - A book is ordered by multiple **orders** - A book is added to multiple **baskets** --- id: BookAuthor name: BookAuthor version: 1.0.0 identifier: id aggregateRoot: false summary: Book author domain entity, encapsulates book author information and business rules properties: - name: id type: UUID required: true description: Unique identifier for the book author - name: bookId type: UUID required: true description: Unique identifier for the book references: Book relationType: hasMany referencesIdentifier: id - name: authorId type: UUID required: true description: Unique identifier for the author references: Author relationType: belongsTo referencesIdentifier: id owners: - nhanxnguyen --- ## Overview The book author entity represents a relationship between a book and an author. It has a unique identifier, a book identifier, and an author identifier. This entity encapsulates the many-to-many relationship between books and authors, allowing for a book to have multiple authors and for an author to have written multiple books. The entity also enforces referential integrity, ensuring that the book and author identifiers exist in their respective tables. ### Entity Properties ### Entity Relationships - A book author is associated with one **book** - A book author is associated with one **author** --- id: Buyer name: Buyer version: 1.0.0 identifier: id aggregateRoot: true summary: Buyer domain entity, encapsulates buyer information and business rules properties: - name: id type: UUID required: true description: Unique identifier for the buyer - name: name type: string required: true description: Name of the buyer - name: address type: Address required: true description: Address of the buyer owners: - nhanxnguyen --- ## Overview The buyer entity represents a buyer in the orders domain. It encapsulates buyer information and business rules such as a unique identifier. The buyer is the person who places an order and is associated with the order. The buyer is responsible for providing payment information and shipping address. ### Entity Properties ### Entity Relationships - A buyer is associated with one **order** --- id: Category name: Category version: 1.0.0 identifier: id aggregateRoot: true summary: Category domain entity, encapsulates category information and business rules properties: - name: id type: UUID required: true description: Unique identifier for the category - name: name type: string required: true description: A name of the category owners: - nhanxnguyen --- ## Overview The category entity represents a category in the catalog. It encapsulates category information and business rules such as a unique identifier, a name, and a collection of books categorized under the category. The category entity also enforces rules such as requiring a non-empty name and ensuring that the category is not deleted. ### Entity Properties ### Entity Relationships - A category has multiple **books** --- id: ChatHistoryItem name: ChatHistoryItem version: 1.0.0 identifier: Key aggregateRoot: false summary: ChatHistoryItem model, encapsulates chat history information for vector storage properties: - name: Key type: string required: false description: Unique key identifier for the chat history item in the vector store - name: ThreadId type: string required: false description: Identifier for the conversation thread this message belongs to - name: Timestamp type: DateTimeOffset required: false description: The date and time when the message was created - name: SerializedMessage type: string required: false description: Serialized representation of the complete message object - name: MessageText type: string required: false description: The text content of the message - name: UserId type: string required: false description: Identifier for the user who sent the message owners: - nhanxnguyen --- ## Overview The `ChatHistoryItem` model represents a chat message stored in the vector database. This lightweight model is used for storing and retrieving chat history using Microsoft.Extensions.VectorData attributes. Unlike traditional domain entities, this model is optimized for vector storage operations. ### Model Properties ### Storage Details - **Vector Store**: Uses `[VectorStoreKey]` and `[VectorStoreData]` attributes for persistence - **Purpose**: Enables semantic search and retrieval of chat history - **Framework**: Microsoft.Extensions.VectorData ### Relationships - A chat history item is associated with a **thread** via ThreadId - A chat history item is owned by a **user** via UserId --- id: CustomerBasket name: CustomerBasket version: 1.0.0 identifier: id aggregateRoot: true summary: Customer basket domain entity, encapsulates customer basket information and business rules properties: - name: id type: UUID required: true description: Unique identifier for the customer basket - name: userId type: UUID required: true description: Unique identifier for the user - name: items type: array items: type: BasketItem required: true description: A list of items in the basket references: BasketItem relationType: hasMany referencesIdentifier: id owners: - nhanxnguyen --- ## Overview The customer basket entity represents a customer basket in the orders. It encapsulates customer basket information and business rules such as a unique identifier, a user identifier, and a list of items. ### Entity Properties ### Entity Relationships - A customer basket is associated with one **user** - A customer basket has multiple **items** --- id: Feedback name: Feedback version: 1.0.0 identifier: id aggregateRoot: true summary: Feedback domain entity, encapsulates feedback information and business rules properties: - name: id type: UUID required: true description: Unique identifier for the feedback - name: bookId type: UUID required: true description: Unique identifier for the book references: Book relationType: hasMany referencesIdentifier: id - name: firstName type: string required: true description: A first name of the user - name: lastName type: string required: true description: A last name of the user - name: rating type: int required: true description: A rating of the book - name: comment type: string required: false description: A comment of the book owners: - nhanxnguyen --- ## Overview The feedback entity represents a feedback in the catalog. It encapsulates feedback information and business rules such as a unique identifier, a book identifier, a user identifier, a rating, and a comment. The feedback entity also enforces rules such as requiring a non-empty rating and ensuring that the feedback is not deleted. ### Entity Properties ### Entity Relationships - A feedback is associated with one **book** --- id: Order name: Order version: 1.0.0 identifier: id aggregateRoot: true summary: Order domain entity, encapsulates order information and business rules properties: - name: id type: UUID required: true description: Unique identifier for the order - name: status type: string required: true description: Status of the order - name: note type: string required: false description: Note of the order - name: buyerId type: UUID required: true description: Unique identifier of the buyer references: Buyer relationType: hasOne referencesIdentifier: id - name: isDeleted type: boolean required: true description: Whether the order has been deleted - name: createdAt type: DateTime required: true description: Time when the order was created - name: lastModifiedAt type: DateTime required: true description: Time when the order was last modified - name: version type: int required: true description: Version of the record for optimistic concurrency control owners: - nhanxnguyen --- ## Overview The order entity represents an order in the orders domain. It encapsulates order information and business rules such as a unique identifier. The order is the final step in the ordering process and is associated with the buyer and basket. ### Entity Properties ### Entity Relationships - A **buyer** has one or more **orders** - A **basket** is associated with one **order** - An **order** consists of one or more **order items** --- id: OrderItem name: OrderItem version: 1.0.0 identifier: id aggregateRoot: false summary: OrderItem domain entity, encapsulates order item information and business rules properties: - name: id type: UUID required: true description: Unique identifier for the order item - name: bookId type: UUID required: true description: Unique identifier of the book references: Book relationType: belongsTo referencesIdentifier: id - name: quantity type: int required: true description: Quantity of the order item - name: price type: decimal required: true description: Price of the order item - name: orderId type: UUID required: true description: Unique identifier of the order references: Order relationType: hasOne referencesIdentifier: id owners: - nhanxnguyen --- ## Overview The order item entity represents an order item in the orders domain. It encapsulates order item information and business rules such as a unique identifier. The order item is the final step in the ordering process and is associated with the order and book. ### Entity Properties ### Entity Relationships - An **order** consists of one or more **order items** - A **book** is associated with one **order item** --- id: OutBox name: OutBox version: 1.0.0 identifier: id aggregateRoot: false summary: Outbox domain entity, encapsulates outbox information and business rules properties: - name: id type: UUID required: true description: Unique identifier for the outbox - name: toName type: string required: true description: Name of the recipient - name: toEmail type: string required: true description: Email of the recipient - name: subject type: string required: true description: Subject of the notification - name: body type: string required: true description: Body of the notification - name: isSent type: boolean required: true description: Whether the notification has been sent - name: sentAt type: DateTime required: false description: Time when the notification was sent - name: createdAt type: DateTime required: true description: Time when the notification was created owners: - nhanxnguyen --- ## Overview The outbox entity represents an outbox in the orders domain. It encapsulates outbox information and business rules such as a unique identifier, the recipient's name and email, the subject and body of the notification, whether the notification has been sent, and timestamps for when the notification was created and sent. This entity is used by the notification service to send notifications to customers about their order status. ### Entity Properties --- id: Publisher name: Publisher version: 1.0.0 identifier: id aggregateRoot: true summary: Publisher domain entity, encapsulates publisher information and business rules properties: - name: id type: UUID required: true description: Unique identifier for the publisher - name: name type: string required: true description: A name of the publisher owners: - nhanxnguyen --- ## Overview The publisher entity represents a publisher in the catalog. It encapsulates publisher information and business rules such as a unique identifier, a name, and a collection of books published by the publisher. The publisher entity also enforces rules such as requiring a non-empty name and ensuring that the publisher is not deleted. ### Entity Properties ### Entity Relationships - A publisher has multiple **books** --- id: basket.{env}.events name: Basket Events Channel version: 1.0.0 summary: | A dedicated message channel that handles all basket-related events in the BookWorm e-commerce system. owners: - nhanxnguyen address: basket.{env}.events protocols: - amqp - grpc parameters: env: enum: - dev - stg - prod default: dev examples: - dev - stg - prod description: "Environment to use for the channel" --- ## Overview The Basket Events Channel serves as a specialized conduit for managing all basket-related activities within the BookWorm e-commerce platform. It enables seamless interaction between the Basket Service and various other components, handling key events like basket initialization, modifications, removals, and the initiation of order placement. This channel guarantees dependable message transmission and upholds the eventual consistency across the basket domain. ## Event Types - **PlaceOrderCommand**: Triggers the process of placing an order from the basket. --- id: catalog.{env}.events name: Catalog Events Channel version: 1.0.0 summary: | A specialized messaging conduit for managing all catalog-related activities within the BookWorm e-commerce platform. owners: - nhanxnguyen address: catalog.{env}.events protocols: - amqp - grpc parameters: env: enum: - dev - stg - prod default: dev examples: - dev - stg - prod description: "Environment to use for the channel" --- ## Overview The Catalog Events Channel serves as a specialized conduit for managing all catalog-related activities within the BookWorm e-commerce platform. It enables seamless interaction between the Catalog Service and various other components, handling key events such as product creation, updates, deletions, and inventory adjustments. This channel guarantees dependable message transmission and upholds the eventual consistency across the catalog domain. ## Event Types - **FeedbackCreated**: Initiates the creation of a new feedback entry. - **FeedbackDeleted**: Removes an existing feedback entry. --- id: chat.{env}.events name: Chat Events Channel version: 1.0.0 summary: | A specialized messaging conduit for managing all chat-related activities within the BookWorm e-commerce platform. owners: - nhanxnguyen address: chat.{env}.events protocols: - redis - ws parameters: env: enum: - dev - stg - prod default: dev examples: - dev - stg - prod description: "Environment to use for the channel" --- ## Overview The Chat Events Channel serves as a specialized messaging conduit for managing all chat-related activities within the BookWorm e-commerce platform. It enables seamless interaction between the Chat Service and various other components, handling key events such as message creation, updates, and deletions. This channel ensures reliable message delivery and maintains eventual consistency across the chat domain. --- id: finance.{env}.events name: Finance Events Channel version: 1.0.0 summary: | An orchestration channel for managing all ordering processes in the BookWorm e-commerce platform. owners: - nhanxnguyen address: finance.{env}.events protocols: - amqp parameters: env: enum: - dev - stg - prod default: dev examples: - dev - stg - prod description: "Environment to use for the channel" --- ## Overview The Finance Events Channel serves as a specialized conduit for managing all finance-related activities within the BookWorm e-commerce platform. It enables seamless interaction between the Finance Service and other components, handling key events like payment processing, invoice generation, and financial reporting. This channel ensures dependable message delivery and upholds the eventual consistency across the finance domain. ## Event Types **BasketDeletedComplete**: Indicates that a basket has been successfully removed. **BasketDeletedFailed**: Notifies that an attempt to delete a basket has failed. **UserCheckedOut**: Starts the process of creating a new order. **OrderStatusChangedToCancel**: Shows that an order has been cancelled. **OrderStatusChangedToComplete**: Shows that an order has been completed. --- id: notification.{env}.events name: Notification Events Channel version: 1.0.0 summary: | A dedicated message channel that handles all notification-related events in the BookWorm e-commerce system. owners: - nhanxnguyen address: notification.{env}.events protocols: - amqp parameters: env: enum: - dev - stg - prod default: dev examples: - dev - stg - prod description: "Environment to use for the channel" --- ## Overview The Notification Events Channel serves as a specialized conduit for managing all notification-related activities within the BookWorm e-commerce platform. It enables seamless communication between the Notification Service and other components, handling events like email, SMS, and push notifications. This channel ensures reliable message delivery and upholds the eventual consistency across the notification domain. ## Event Types **PlaceOrderCommand**: Initiates the process of creating a new order. **CancelOrderCommand**: Indicates that an order has been cancelled. **CompleteOrderCommand**: Indicates that an order has been completed. --- id: ordering.{env}.events name: Ordering Events Channel version: 1.0.0 summary: | A channel for handling all ordering activities in the BookWorm e-commerce platform. owners: - nhanxnguyen address: ordering.{env}.events protocols: - amqp - ws parameters: env: enum: - dev - stg - prod default: dev examples: - dev - stg - prod description: "Environment to use for the channel" --- ## Overview The Ordering Events Channel serves as a specialized conduit for managing all ordering-related activities within the BookWorm e-commerce platform. It enables seamless interaction between the Ordering Service and various other components, handling key events like order creation, updates, and deletions. This channel ensures dependable message delivery and upholds the eventual consistency across the ordering domain. ## Event Types **DeleteBasketCompleteCommand**: Signals that a basket has been successfully deleted. **DeleteBasketFailedCommand**: Signals that an attempt to delete a basket has failed. --- id: rating.{env}.events name: Rating Events Channel version: 1.0.0 summary: | A specialized messaging conduit for managing all rating-related activities within the BookWorm e-commerce platform. owners: - nhanxnguyen address: rating.{env}.events protocols: - amqp parameters: env: enum: - dev - stg - prod default: dev examples: - dev - stg - prod description: "Environment to use for the channel" --- ## Overview The Rating Events Channel serves as a specialized conduit for managing all rating-related activities within the BookWorm e-commerce platform. It enables seamless interaction between the Rating Service and various other components, handling key events like rating creation, updates, and deletions. This channel ensures dependable message delivery and upholds the eventual consistency across the rating domain. ## Event Types **BookUpdatedRatingFailed**: Indicates that the rating update for a book has failed. --- id: scheduler.{env}.events name: Scheduler Events Channel version: 1.0.0 summary: | A dedicated message channel that handles all scheduler-related events in the BookWorm e-commerce system. owners: - nhanxnguyen address: scheduler.{env}.events protocols: - amqp parameters: env: enum: - dev - stg - prod default: dev examples: - dev - stg - prod description: "Environment to use for the channel" --- ## Overview The Scheduler Events Channel serves as a specialized conduit for managing all scheduled task-related activities within the BookWorm e-commerce platform. It enables seamless communication between the Scheduler Service and other components, handling events like email cleanup and error recovery operations. This channel ensures reliable message delivery and upholds the eventual consistency across the scheduler domain. ## Event Types **CleanUpSentEmailIntegrationEvent**: Triggers the cleanup process for sent emails to maintain system performance and storage efficiency. This event is published daily at midnight to remove processed email records from the notification system. **ResendErrorEmailIntegrationEvent**: Initiates the retry mechanism for failed email deliveries. This event is published hourly with high priority to ensure prompt recovery of email delivery failures and maintain customer communication reliability. --- id: BasketDatabase name: Basket Database version: 1.0.0 container_type: database technology: redis@8.2 authoritative: true access_mode: readWrite classification: internal retention: 30d residency: eastasia summary: In-memory data store for customer shopping baskets using Redis with distributed caching capabilities. owners: - nhanxnguyen --- ## Overview The Basket Database is a high-performance Redis-based data store that manages customer shopping baskets in the BookWorm e-commerce system. Operating on Redis 8.2, this in-memory database provides ultra-fast read and write operations for shopping cart data, ensuring seamless user experience during the shopping journey. Unlike traditional relational databases, Redis's key-value architecture delivers sub-millisecond response times, making it ideal for the transient, high-frequency nature of shopping basket operations. ### Why Redis for Baskets? **Performance** - Sub-millisecond read/write latency (typically less than 1ms) - In-memory operations eliminate disk I/O bottlenecks - Supports 100,000+ operations per second per node **Ephemeral Nature** - Shopping baskets are temporary by nature - Most baskets are abandoned (60-70% abandonment rate) - No need for durable storage of transient data - Automatic expiration support (TTL) **Scalability** - Horizontal scaling through Redis Cluster - Built-in replication for high availability - No complex table joins or indexing overhead **Simplicity** - Simple key-value operations: GET, SET, DELETE - Hash operations for structured basket data - Native JSON serialization support ### Connection Management The repository uses `IConnectionMultiplexer` from StackExchange.Redis with: - Connection pooling for optimal resource usage - Semaphore-based locking (`SemaphoreSlim`) to prevent race conditions - Thread-safe database access - Automatic reconnection on failures ## Performance & Optimization ### Performance Characteristics **Target Metrics**: - Read latency: p99 less than 5ms - Write latency: p99 less than 10ms - Throughput: 50,000+ ops/second - Availability: 99.9% uptime **Actual Performance**: - Average read: 1-2ms - Average write: 2-3ms - Peak throughput: 80,000+ ops/second ### Optimization Techniques **JSON Serialization**: - Source-generated serializers (zero reflection) - UTF8 encoding for efficient wire format - Minimal allocations during serialization **Connection Pooling**: - Reused multiplexer connections - Single `IConnectionMultiplexer` instance per service - Thread-safe database access via semaphore **Hash Operations**: - O(1) complexity for get/set/delete operations - No full scan operations - Efficient memory usage with Redis hashing ## Monitoring & Observability ### Key Performance Indicators **Operational Metrics**: - **Hit Rate**: Percentage of successful basket retrievals - **Write Success Rate**: Should be greater than 99.9% - **Connection Health**: Active connections, failed connections - **Latency Distribution**: p50, p95, p99 response times **Business Metrics**: - **Active Baskets**: Current number of baskets in Redis - **Average Items per Basket**: Indicates customer engagement - **Basket Abandonment Rate**: Baskets not converted to orders - **Basket Size Distribution**: Understand typical basket patterns ### Health Checks The Basket Service includes Redis health checks: - Periodic connection validation - Database availability checks - Memory usage monitoring - Exposed via `/health` endpoint ## Security & Data Classification ### Classification Details - **Classification**: Internal - Contains customer shopping data - **Access Mode**: Read/Write - Full CRUD operations for authorized services - **Residency**: East Asia region - Data locality for primary user base - **Authoritative**: True - Single source of truth for active basket state ### Security Considerations **Data Protection**: - Customer IDs stored as GUIDs (non-sequential, harder to enumerate) - No sensitive payment information stored in baskets - Product references only (IDs, not full product data) - Network encryption in transit (TLS) **Access Control**: - Service-level authentication via Keycloak - No direct external access to Redis - API gateway enforces authorization - Customer can only access their own basket ## Integration Points ### Upstream Dependencies **Catalog Service (gRPC)**: - Validates product existence during basket operations - Retrieves current product prices and availability - Ensures basket items reference valid products ### Downstream Consumers **Ordering Service**: - Reads basket data when order is placed - Triggers basket deletion after successful order creation - Receives `PlaceOrderCommand` event from basket **Notification Service**: - May read basket data for abandoned cart reminders - Sends notifications based on basket state changes ## Event-Driven Integration ### Published Events **BasketDeletedCompletedEvent**: - Published when basket is successfully deleted - Triggers cleanup in dependent services - Contains customer ID and timestamp **BasketDeletedFailedEvent**: - Published when basket deletion fails - Triggers retry logic or manual intervention - Contains customer ID, error details, and timestamp ### Consumed Commands **PlaceOrderCommand**: - Received from order workflow - Triggers basket-to-order conversion - Results in basket deletion and order creation ## Disaster Recovery & High Availability ### Backup Strategy **Primary**: Redis persistence disabled (ephemeral data) - Shopping baskets are recreatable - No backup needed for transient data - Reduces storage costs and complexity **Optional**: Redis RDB snapshots for debugging - Periodic snapshots for production troubleshooting - Not used for disaster recovery - Retained for 7 days maximum ### High Availability **Redis Configuration**: - Master-replica replication - Sentinel for automatic failover - Sub-second failover time - Aspire handles connection string updates **Failover Behavior**: - Minimal data loss (replication lag less than 100ms) - Automatic client reconnection - Circuit breaker for degraded scenarios - Graceful degradation: return empty basket if Redis unavailable ## Future Enhancements ### Planned Improvements **Redis Cluster**: - Horizontal scaling for higher throughput - Sharding for larger datasets - Multi-region replication for global customers **Advanced Caching**: - Cache-aside pattern for product data - Local in-memory cache (L1) with Redis as L2 - Reduce Redis roundtrips for frequently accessed baskets **Analytics Integration**: - Stream basket changes to analytics platform - Real-time dashboards for basket metrics - Machine learning for abandonment prediction **TTL Management**: - Automatic expiration of abandoned baskets - Configurable TTL based on customer behavior - Periodic cleanup jobs for expired data --- id: CatalogBlob name: Catalog Blob Storage version: 1.0.0 container_type: objectStore technology: azure@latest authoritative: true access_mode: readWrite classification: internal retention: 2y residency: eastasia summary: Blob storage for product images and media assets. owners: - nhanxnguyen --- ## Overview The Catalog Blob Storage is a dedicated Azure Blob Storage container that serves as the central repository for all product-related media assets in the BookWorm system. This storage solution handles book cover images, author photos, publisher logos, and other visual content that enriches the product catalog experience. By separating media storage from the database, we achieve better performance, scalability, and cost optimization while maintaining fast content delivery to customers. ## Purpose & Responsibility The blob storage acts as a content delivery foundation for the catalog service, enabling: - **Product Visualization**: Storing high-quality book cover images that help customers make informed purchase decisions - **Brand Representation**: Managing publisher logos and author photographs for enhanced credibility - **Multi-Resolution Support**: Maintaining multiple image sizes for responsive design across devices - **Media Asset Lifecycle**: Handling the full lifecycle of media assets from upload to deletion ## Storage Structure ### Image Size Variants Each product image is stored in multiple resolutions to optimize performance and bandwidth: - **thumbnail**: 150x200px - Grid views and search results - **medium**: 400x600px - Product cards and mobile views - **large**: 800x1200px - Detail pages and zoom functionality - **original**: Native resolution - Master copy for future processing This multi-resolution approach ensures fast page loads while maintaining visual quality where it matters most. ## Technical Implementation ### Azure Blob Storage Configuration - **Storage Tier**: Hot tier for frequently accessed book cover images - **Redundancy**: Zone-redundant storage (ZRS) for high availability - **Access Tier Policies**: Automatic tier transition to cool storage for images not accessed in 90 days - **Versioning**: Enabled for change tracking and accidental deletion recovery - **Soft Delete**: 30-day retention for deleted blobs ### Content Types Supported image formats optimized for web delivery: - **WebP**: Primary format for modern browsers (better compression) - **JPEG**: Fallback format for broad compatibility - **PNG**: For images requiring transparency (logos) ### CDN Integration The blob storage is fronted by Azure CDN for: - **Global Distribution**: Edge caching in multiple regions - **Performance**: Sub-100ms image delivery worldwide - **Bandwidth Optimization**: Reduced origin server load - **HTTPS Delivery**: Secure content delivery by default ## Access Patterns & Performance ### Read Operations (90% of traffic) - Product catalog browsing and search - Product detail page views - Shopping cart and checkout displays - Cached at CDN edge for 24 hours - Average response time: < 50ms (CDN hit) ### Write Operations (10% of traffic) - New product image uploads - Image updates/replacements - Bulk image imports during catalog expansion - Background thumbnail generation - CDN cache invalidation on update ### Performance Optimization - **Lazy Loading**: Images load on-demand as users scroll - **Progressive JPEGs**: Display low-resolution preview while loading full image - **Image Compression**: Automatic optimization on upload (80-85% quality) - **Responsive Images**: Serve appropriate size based on device and viewport ## Data Classification & Governance - **Classification**: Internal - Product images are public-facing but managed internally - **Access Mode**: Read/Write - Catalog service has full control; public has read-only access via CDN - **Retention**: 2 years - Images retained for this period after product deletion - **Residency**: East Asia region - Optimized for primary market with CDN for global reach - **Authoritative**: True - Single source of truth for all product media assets ## Security & Access Control ### Authentication & Authorization - **Service Identity**: Managed Identity for Catalog service access - **Public Access**: Read-only via CDN with signed URLs for time-limited access - **Upload Security**: Server-side validation of file types and sizes - **CORS Configuration**: Restricted to BookWorm domains only ### Content Security - **Malware Scanning**: All uploads scanned before storage - **Content Moderation**: Automated checks for inappropriate content - **File Type Validation**: Server-side MIME type verification - **Size Limits**: Maximum 10MB per image to prevent abuse - **Rate Limiting**: Upload throttling to prevent DoS attacks ### Data Protection - **Encryption at Rest**: Azure Storage Service Encryption (SSE) with Microsoft-managed keys - **Encryption in Transit**: TLS 1.2+ for all data transfers - **Access Logging**: All operations logged for audit trails - **Immutable Storage**: Legal hold capability for compliance scenarios ## Integration Points The Catalog Blob Storage integrates seamlessly with: - **Catalog Service**: Primary consumer for image upload and management - **CDN**: Content delivery to end users - **Image Processing Pipeline**: Automated thumbnail generation and optimization - **Backup Service**: Regular snapshots for disaster recovery - **Monitoring Service**: Performance metrics and health checks ## Lifecycle Management ### Upload Workflow 1. Catalog service requests upload URL with SAS token 2. Client uploads image directly to blob storage 3. Azure Function triggers for post-processing 4. Thumbnails generated asynchronously 5. CDN cache pre-warmed for popular items 6. Database updated with blob URLs ### Update Workflow 1. New image uploaded with versioning 2. Old version retained per policy 3. CDN cache invalidated 4. Thumbnails regenerated 5. Database references updated ### Deletion Workflow 1. Soft delete marks blob as deleted 2. 30-day recovery window maintained 3. After retention: permanent deletion 4. Orphan detection runs weekly 5. Unused blobs archived or purged ## Cost Optimization ### Storage Costs - **Hot Tier**: Frequently accessed images (< 90 days old or > 100 views/month) - **Cool Tier**: Infrequently accessed images (older products, low traffic) - **Archive Tier**: Long-term retention (deleted products, historical records) ### Bandwidth Costs - CDN caching reduces bandwidth by ~85% - Compression reduces transfer sizes by ~60% - Smart format selection (WebP vs JPEG) saves ~30% bandwidth ### Operational Efficiency - Automated tier transitions based on access patterns - Regular cleanup of orphaned blobs - Duplicate detection and deduplication - Monitoring alerts for cost anomalies ## Monitoring & Observability ### Key Metrics - **Storage Capacity**: Total size, growth rate, tier distribution - **Request Metrics**: Read/write operations, success rate, latency - **CDN Performance**: Cache hit ratio, origin requests, bandwidth saved - **Cost Tracking**: Storage costs, egress costs, operation costs ### Health Checks - Availability checks every 5 minutes - End-to-end upload/download tests - CDN purge functionality validation - Backup verification runs daily ### Alerting - Storage capacity approaching limits (greater than 80%) - Elevated error rates (greater than 1% failed requests) - Unusual access patterns (potential security issues) - CDN cache hit ratio drops (below 70%) - Cost anomalies (greater than 20% increase week-over-week) ## Disaster Recovery ### Backup Strategy - **Continuous Replication**: ZRS provides zone-level redundancy - **Daily Snapshots**: Point-in-time recovery capability - **Geo-Replication**: Optional read-access geo-redundant storage (RA-GRS) - **Recovery Time Objective (RTO)**: < 4 hours - **Recovery Point Objective (RPO)**: < 1 hour ### Business Continuity - Automatic failover to secondary region if primary fails - CDN serves cached content during blob storage outages - Graceful degradation: System functions without images (alt text displayed) - Regular DR drills every quarter ## Future Enhancements - **AI-Powered Image Tagging**: Automatic metadata extraction from images - **Smart Cropping**: AI-based focal point detection for responsive images - **Video Support**: Expanding to product videos and 3D previews - **Progressive Web App Integration**: Offline image caching - **Real-time Image Editing**: On-the-fly transformations via CDN - **Blockchain Verification**: Content authenticity and provenance tracking --- id: CatalogCache name: Catalog Cache version: 1.0.0 container_type: cache technology: redis@8.2 authoritative: true access_mode: readWrite classification: internal retention: 2y residency: eastasia summary: Distributed cache for frequently accessed product catalog data. owners: - nhanxnguyen --- ## Overview The Catalog Cache is a high-performance Redis-based distributed caching layer that sits between the Catalog Service and its primary data sources. Operating on Redis 8.2, this cache dramatically reduces database load and improves response times by storing frequently accessed product information in memory. In an e-commerce environment where the same products are viewed thousands of times per hour, the cache transforms expensive database queries into lightning-fast memory lookups, ensuring customers experience consistent sub-millisecond response times even during peak traffic. ## The Caching Strategy ### Why Cache? E-commerce catalog queries follow the classic 80/20 rule: 20% of products generate 80% of the traffic. The Catalog Cache exploits this pattern by: - **Reducing Database Load**: Offloading 85-90% of read operations from PostgreSQL - **Improving Latency**: Response times drop from 50-100ms (database) to 1-5ms (cache) - **Enabling Scale**: Supporting 10x more concurrent users without database upgrades - **Cost Efficiency**: Reducing database instance size requirements by 60% - **High Availability**: Acting as a buffer during database maintenance or brief outages ### Caching Patterns We employ multiple caching strategies based on data characteristics: **Cache-Aside (Lazy Loading)** - Most common pattern for product data - Load data into cache only when requested - First request: cache miss → load from DB → store in cache - Subsequent requests: cache hit → return immediately **Write-Through** - For critical product updates - Write to database and cache simultaneously - Ensures cache is always consistent - Used for inventory status changes **Write-Behind (Write-Back)** - For high-frequency updates (view counts, clicks) - Write to cache immediately, queue database update - Reduces database write pressure - Periodic batch updates to database **Refresh-Ahead** - For predictable access patterns - Proactively refresh cache before expiration - Used for popular products and featured items - Prevents cache misses during high traffic ## Performance Optimization ### Memory Management **Eviction Policy: `allkeys-lru`** - Least Recently Used eviction when memory limit reached - Ensures most valuable data stays cached - Alternative: `volatile-lru` (only evict keys with TTL) **Memory Limits** ``` Max Memory: 8GB per node Used Memory: Typically 60-70% (4.8-5.6GB) Reserved: 30-40% for peaks and operations ``` **Memory Optimization Techniques** - Compression: gzip JSON before caching (30-40% size reduction) - Serialization: Use MessagePack instead of JSON (20-30% smaller) - Lazy Loading: Don't cache everything; let usage patterns decide - Expiration: Aggressive TTLs for large objects ## Monitoring & Observability ### Key Performance Indicators **Cache Effectiveness Metrics** - **Hit Rate**: Target greater than 85% for product queries - **Miss Rate**: Should be less than 15% - **Eviction Rate**: Should be less than 5% (indicates memory pressure) - **Latency**: p95 should be less than 10ms, p99 less than 20ms **Resource Metrics** - **Memory Usage**: Current vs. max memory - **CPU Usage**: Should remain below 70% - **Network Throughput**: Ops/second, bandwidth - **Connections**: Active connections, rejected connections **Business Metrics** - **Cache Value**: Revenue from cached vs. non-cached pages - **Cost Savings**: Database queries prevented - **User Experience**: Page load time improvement ## Best Practices ### Do's ✅ - **Set TTLs**: Always set expiration to prevent unbounded growth - **Handle Misses**: Implement robust fallback to database - **Monitor Hit Rates**: Continuously track cache effectiveness - **Use Pipelining**: Batch multiple operations when possible - **Compress Large Values**: Reduce memory and network overhead - **Version Your Cache**: Include version in keys for safe invalidation - **Log Cache Operations**: Debug issues with proper logging - **Test Failover**: Regular DR drills ensure reliability ### Don'ts ❌ - **Don't Cache Everything**: Cache only frequently accessed data - **Don't Ignore Evictions**: High eviction rate indicates problems - **Don't Use KEYS**: Scanning keys blocks Redis; use SCAN instead - **Don't Store PII**: Keep sensitive data out of cache - **Don't Forget TTLs**: Stale data causes confusion and bugs - **Don't Block Operations**: Use async operations exclusively - **Don't Ignore Errors**: Cache failures should be logged and alerted - **Don't Over-Invalidate**: Excessive invalidation defeats the purpose ## Future Enhancements ### Advanced Features - **Redis Search**: Full-text search capabilities for cached products - **Redis JSON**: Native JSON support for better query performance - **Redis TimeSeries**: Tracking product views and trends - **Redis Graph**: Relationship-based recommendations - **Redis Bloom**: Probabilistic existence checks ### Optimization Opportunities - **Predictive Prefetching**: ML-based cache warming - **Geographic Distribution**: Multi-region cache clusters - **Smart Compression**: AI-powered compression selection - **Dynamic TTL**: Machine learning for optimal expiration times - **Cache Coherence Protocol**: Multi-layer cache synchronization ### Operational Improvements - **Automated Scaling**: Scale based on hit rate and memory usage - **Self-Healing**: Automatic recovery from transient failures - **Chaos Engineering**: Regular failure injection testing - **Performance Budgets**: SLO-based alerting and optimization - **Cost Analytics Dashboard**: Real-time ROI visualization --- id: CatalogDatabase name: Catalog Database version: 1.0.0 container_type: database technology: postgres@18 authoritative: true access_mode: readWrite classification: internal retention: 2y residency: eastasia summary: Primary database for product catalog information. owners: - nhanxnguyen attachments: - url: https://drive.google.com/file/d/1ZUFxSDuI66xw5SEimbvTefveG0GskpqI/view?usp=sharing title: ERD of the Catalog Database description: Learn more about the schema of the Catalog Database type: "diagrams" icon: FileTextIcon --- ## Overview The Catalog Database is the authoritative source for all product catalog information in the BookWorm system. Built on PostgreSQL 18, it manages the complete lifecycle of book products, including their metadata, categorization, authorship, and publisher information. This database serves as the backbone of the product catalog service, enabling efficient product discovery, management, and synchronization across the BookWorm platform. ## Schema Design The database follows Domain-Driven Design (DDD) principles with the following aggregate roots: ### Book Aggregate The central entity representing books in the catalog system. Each book contains: - **Core Information**: Name, description, and image - **Pricing**: Base price and optional sale price (managed as a value object) - **Status**: Current availability status (InStock, OutOfStock, etc.) - **Rating Metrics**: Average rating and total reviews count - **Relationships**: Links to Category, Publisher, and multiple Authors through BookAuthor junction table ### Category Aggregate Organizes books into logical groupings: - **Name**: Unique category identifier (e.g., Fiction, Non-Fiction, Science, History) - Used for product organization and filtering ### Author Aggregate Represents book authors in the system: - **Name**: Author's full name - **Books**: Many-to-many relationship with books through BookAuthor junction table - Supports co-authored books ### Publisher Aggregate Manages publisher information: - **Name**: Publisher company name - **Books**: One-to-many relationship with books ### BookAuthor (Junction Entity) Enables many-to-many relationships between books and authors, supporting co-authored publications. Includes soft delete capability for maintaining historical data integrity. ## Key Features ### Soft Delete Support Books and BookAuthor entities implement soft delete patterns, ensuring: - Historical data preservation for audit trails - Safe deletion without breaking referential integrity - Ability to recover accidentally deleted records - Compliance with data retention policies ### Audit Trail All entities inherit auditable properties tracking: - Creation timestamp - Last modification timestamp - Created by user - Modified by user ### Domain Events The Book aggregate emits domain events (e.g., `BookCreatedEvent`) for: - Event-driven architecture integration - Cross-service communication - Audit logging - Real-time updates to other services (Basket, Ordering) ## Data Classification & Governance - **Classification**: Internal - Not exposed directly to external systems - **Access Mode**: Read/Write - Full CRUD operations for authorized services - **Retention**: 2 years - Aligns with business and regulatory requirements - **Residency**: East Asia region - Optimized for primary user base location - **Authoritative**: True - Single source of truth for catalog data ## Performance Considerations - Indexed on frequently queried fields (Name, CategoryId, PublisherId, Status) - Optimized for read-heavy workloads typical of e-commerce catalog browsing - Supports efficient filtering and searching through specification patterns - Materialized rating data (AverageRating, TotalReviews) for fast retrieval ## Integration Points The Catalog Database integrates with: - **Basket Service**: Product information for cart items - **Ordering Service**: Product details for order processing - **Rating Service**: Aggregated rating data updates - **Search Service**: Product indexing for full-text search - **Finance Service**: Pricing information for revenue calculations ## Security - Database access restricted to Catalog Service only - Connection strings managed through secure configuration - Row-level security policies for multi-tenant scenarios (if applicable) - Encryption at rest for sensitive data - Audit logging for all data modifications --- id: CatalogVectorDB name: Catalog Vector Database version: 1.0.0 container_type: objectStore technology: qdrant@latest authoritative: true access_mode: readWrite classification: internal retention: 2y residency: eastasia summary: Vector database for enhanced product search and recommendations. owners: - nhanxnguyen --- ## Overview The Catalog Vector Database is a specialized Qdrant-powered vector store that enables intelligent, semantic search and AI-driven product recommendations in the BookWorm platform. Unlike traditional keyword-based search, this database stores high-dimensional vector embeddings that capture the semantic meaning of books, allowing customers to discover products through natural language queries, conceptual similarity, and contextual understanding. This modern approach transforms the search experience from exact-match lookups to understanding intent and meaning. ## Why Vector Search? Traditional database searches fall short when customers search with phrases like "books about overcoming adversity" or "something similar to The Alchemist." Vector search bridges this gap by: - **Understanding Intent**: Grasping what customers mean, not just what they type - **Semantic Similarity**: Finding books with similar themes, even without shared keywords - **Contextual Recommendations**: Suggesting products based on meaning and relationships - **Multilingual Support**: Searching across languages through shared semantic space - **Personalization**: Creating nuanced recommendations based on user behavior patterns ## Architecture & Design ### Vector Embeddings Each book in the catalog is transformed into a high-dimensional vector embedding (typically 768 or 1536 dimensions) that encodes its semantic essence. These embeddings are generated from: **Primary Embedding Sources:** - Book title and subtitle **Embedding Model:** - Using state-of-the-art transformer models (e.g., OpenAI text-embedding-3-large, EmbeddingGemma) - Dimension: 1536 for optimal balance between accuracy and performance - Updated periodically as book metadata changes ## Core Capabilities ### 1. Semantic Search Transform natural language queries into meaningful results: **Example Query**: "inspiring stories about personal growth" - Query converted to vector embedding - Similarity search across book embeddings - Returns books about self-improvement, memoirs, motivational content - No need for exact keyword matches **Search Features:** - Typo tolerance through semantic understanding - Synonym awareness (e.g., "scary" matches "horror") - Concept matching (e.g., "space exploration" finds sci-fi and non-fiction) - Multi-field fusion (title + description + category) ### 2. Similarity-Based Recommendations "Customers who liked this might also enjoy": - **Content-Based**: Find books with similar vector embeddings - **Collaborative Filtering**: Enhanced with user preference vectors - **Hybrid Approach**: Combines semantic similarity with rating scores - **Cold Start Solution**: Works even for new books with limited interactions ### 3. Personalized Discovery Tailored recommendations for each user: - User preference vector built from interaction history - Periodic updates as users browse and purchase - Balance between exploration (new discoveries) and exploitation (proven preferences) - Diversity optimization to avoid filter bubbles ### 4. Contextual Search Search refined by context: - **Mood-based**: "Something uplifting for a rainy day" - **Occasion-based**: "Gift for a 12-year-old interested in science" - **Comparative**: "Like Harry Potter but for adults" - **Attribute-focused**: "Fast-paced thrillers with strong female leads" ## Technical Implementation ### Indexing Strategy **HNSW Algorithm** (Hierarchical Navigable Small World): - Fast approximate nearest neighbor search - Trade-off between recall and latency configurable - Typical recall: 95-99% at sub-50ms latency - Memory-efficient with scalar quantization ### Performance Characteristics - **Search Latency**: 20-50ms for top-k retrieval (k=20) - **Indexing Speed**: 1000+ vectors per second - **Concurrent Queries**: 100+ simultaneous searches - **Collection Size**: Scales to millions of vectors - **Memory Usage**: ~4KB per vector with quantization ### Batch Processing For large-scale updates or reindexing: - **Nightly Sync**: Verify all books have current vectors - **Weekly Re-embedding**: Update vectors for books with significant review changes - **Monthly Full Reindex**: Rebuild with latest embedding models - **Incremental Updates**: Only process changed records ## Data Classification & Governance - **Classification**: Internal - Vector representations are derived data, not direct customer data - **Access Mode**: Read/Write - Catalog service has full control; Search service has read access - **Retention**: 2 years - Aligned with catalog database retention policy - **Residency**: East Asia region - Co-located with primary database for low latency - **Authoritative**: True - Single source for semantic search capabilities ## Performance Optimization ### Query Optimization - **Caching**: Popular queries cached in Redis for 1 hour - **Pre-computed Recommendations**: Top similar items cached per book - **Batch Queries**: Multiple lookups grouped for efficiency - **Approximate Search**: Trade 1-2% accuracy for 3-5x speed improvement ### Index Optimization - **Quantization**: Reduces memory footprint by 4x - **Memory Mapping**: Large collections partially on disk - **Sharding**: Distribute load across multiple Qdrant nodes - **Hot/Cold Separation**: Frequently accessed vectors kept in memory ### Embedding Generation - **Batch Processing**: Generate embeddings in batches of 100 - **Caching**: Cache embeddings for unchanged content - **Model Selection**: Balance between quality and cost - **Fallback Strategy**: Pre-computed embeddings for common queries ## Integration Points The Vector Database integrates with: - **Catalog Service**: Primary data source for book metadata - **Search Service**: Consumer for semantic search queries - **Recommendation Engine**: Powers "You might also like" features - **Chat Service**: Enables conversational product discovery - **Analytics Service**: Tracks search patterns and recommendation effectiveness ## Monitoring & Observability ### Key Metrics **Performance Metrics:** - Query latency (p50, p95, p99) - Indexing throughput (vectors/second) - Memory utilization - Disk I/O operations - Cache hit ratio **Business Metrics:** - Search result relevance (click-through rate) - Recommendation acceptance rate - Null result rate (queries with no good matches) - Query diversity (unique queries vs. total) **Data Quality Metrics:** - Embedding staleness (time since last update) - Vector coverage (percentage of books indexed) - Sync lag (delay from database to vector store) ### Health Checks - Collection integrity verification - Index rebuild status - Replication lag monitoring - Query success rate tracking - Embedding model availability ### Alerting Thresholds - Query latency exceeding 100ms (p95) - Indexing backlog greater than 1000 items - Memory utilization above 85% - Sync lag greater than 5 minutes - Search error rate above 0.5% ## Security & Access Control ### Authentication - **Service-to-Service**: API key authentication - **Network Security**: Private network access only - **TLS Encryption**: All connections encrypted - **Role-Based Access**: Read-only for search services, read-write for indexing services ### Data Protection - **Embedding Privacy**: Vectors don't expose raw customer data - **Payload Filtering**: Sensitive fields excluded from payloads - **Audit Logging**: All write operations logged - **Backup Strategy**: Daily snapshots to object storage ## Cost Optimization ### Compute Costs - **Right-sizing**: Instance sized for actual query load - **Auto-scaling**: Scale up during peak hours, down during off-peak - **Spot Instances**: Use for batch reindexing jobs - **Query Batching**: Reduce API calls to embedding services ### Storage Costs - **Quantization**: Reduce vector size by 75% with minimal quality loss - **Compression**: Payload data compressed - **Archival**: Old versions moved to cold storage - **Deduplication**: Identify and remove duplicate vectors ### API Costs - **Embedding Cache**: Reduce calls to OpenAI by 60-70% - **Batch Processing**: Lower cost per embedding - **Model Selection**: Use smaller models for simple content - **Rate Limiting**: Prevent runaway embedding costs ## Use Cases & Examples ### Discovery Scenarios **1. Natural Language Search** - User: "Books about time travel paradoxes" - System: Returns sci-fi novels exploring temporal mechanics - No exact keyword match required **2. Exploratory Browsing** - User: "Something like The Martian but not sci-fi" - System: Suggests survival stories, problem-solving narratives - Cross-genre conceptual matching **3. Gift Recommendations** - User: "Educational book for curious 10-year-old" - System: Age-appropriate science, history, adventure books - Contextual understanding of requirements **4. Mood-Based Discovery** - User: "Need something to cheer me up" - System: Humorous books, uplifting stories, feel-good fiction - Emotional context understanding ### Business Applications - **Intelligent Search**: Replace basic keyword search with semantic understanding - **Smart Recommendations**: "Customers who bought X also bought Y" with deeper accuracy - **Content Curation**: Automatically generate themed collections and reading lists - **Trend Detection**: Identify emerging themes and topics through vector clustering - **Inventory Optimization**: Stock books similar to bestsellers ## Future Enhancements ### Advanced Capabilities - **Multi-modal Embeddings**: Include book cover images in vector representation - **Temporal Vectors**: Capture changing preferences and trends over time - **Graph-Enhanced Vectors**: Combine vector similarity with knowledge graphs - **Few-Shot Learning**: Improve recommendations for users with minimal history - **Explainable Recommendations**: Surface why specific books were recommended ### Integration Expansions - **Voice Search**: Natural language queries from smart speakers - **Visual Search**: "Find books with covers like this" - **Cross-Domain**: Recommend books based on movie/music preferences - **Real-time Personalization**: Dynamic vector updates based on browsing session - **A/B Testing Framework**: Experiment with different embedding models and search parameters ### Performance Improvements - **GPU Acceleration**: Faster embedding generation and search - **Distributed Architecture**: Multi-region deployment for global performance - **Smart Pre-fetching**: Predictive caching based on user patterns - **Incremental Updates**: Real-time vector updates without full reindex - **Hybrid Search**: Combine vector and traditional search for optimal results --- id: ChatDatabase name: Chat Database version: 1.0.0 container_type: database technology: qdrant@latest authoritative: true access_mode: readWrite classification: internal retention: 2y residency: eastasia summary: Vector database for storing and retrieving chat history with semantic search capabilities. owners: - nhanxnguyen --- ## Overview The Chat Database uses **Qdrant**, a vector database, to store chat history items with semantic search capabilities. This enables efficient retrieval of conversation history and supports AI agent orchestration by maintaining context across chat sessions. ## Architecture ### Vector Storage Implementation The database is accessed through `VectorChatMessageStore`, which implements the `ChatMessageStore` interface from Microsoft.Agents.AI. This provides: - **Vector Collections**: Chat messages are stored in vector collections using `VectorStoreCollection` - **Thread-based Organization**: Messages are grouped by `ThreadId` for conversation continuity - **User Isolation**: Messages are filtered by `UserId` to ensure data privacy - **Temporal Ordering**: Messages are stored with timestamps for chronological retrieval ### Data Model Messages are stored as `ChatHistoryItem` records with the following structure: ```csharp { Key: string, // ThreadDbKey + MessageId (unique identifier) ThreadId: string, // Conversation thread identifier (UUIDv7) Timestamp: DateTimeOffset, // Message creation time SerializedMessage: string, // Full ChatMessage JSON MessageText: string, // Extracted message text for search UserId: string // User who sent the message } ``` ## Key Features ### 1. Thread Management - Each conversation thread is identified by a UUIDv7-based `ThreadDbKey` - Thread keys are generated on first message and maintained throughout the session - Enables concurrent conversations without interference ### 2. Message Retrieval - Retrieves up to `MaxMessages` per query (configurable via AppSettings) - Messages are ordered by timestamp in descending order - Results are reversed to maintain chronological order - Filters by both thread ID and user ID for security ### 3. Message Persistence - Uses upsert operations for idempotent writes - Serializes full `ChatMessage` objects with JSON source generation - Maintains metadata (timestamp, user ID) for filtering and analytics - Extracts message text for semantic search capabilities ### 4. State Serialization - Thread state can be serialized to JSON for session management - Enables conversation resumption across sessions - Supports state hydration from serialized JSON elements ## Technical Details ### Vector Store Configuration - **Collection Type**: `VectorStoreCollection` - **Key Type**: Guid (for collection operations) - **Record Type**: ChatHistoryItem with vector data attributes ### Query Operations ```csharp // Retrieval query structure x => x.ThreadId == ThreadDbKey && x.UserId == userId // Ordering OrderBy: x => x.Descending(y => y.Timestamp) // Limit MaxMessages (from AppSettings) ``` ### Serialization - Uses `JsonSerializerOptions` with camelCase naming policy - Source-generated serialization context for performance - Property name case-insensitive deserialization ## Security - **User Isolation**: All queries filter by authenticated user ID from ClaimsPrincipal - **Thread Isolation**: Messages are scoped to specific conversation threads - **Access Control**: Read/write access controlled through authentication claims ## Performance Considerations - **Batch Operations**: Uses `UpsertAsync` for efficient bulk writes - **Query Limits**: Configurable `MaxMessages` prevents over-fetching - **Async Operations**: All database operations are asynchronous - **Collection Caching**: Ensures collection exists before operations ## Integration The vector store integrates with: - **Microsoft.Agents.AI**: Implements `ChatMessageStore` interface - **Microsoft.Extensions.VectorData**: Uses vector data attributes and collections - **ASP.NET Core Authentication**: Leverages `ClaimsPrincipal` for user context - **Qdrant**: Underlying vector database engine --- id: FinanceDatabase name: Finance Database version: 1.0.0 container_type: database technology: postgres@18 authoritative: true access_mode: readWrite classification: internal retention: 7y residency: eastasia summary: PostgreSQL database managing saga state and transactional outbox for distributed order processing workflows. owners: - nhanxnguyen attachments: - url: https://drive.google.com/file/d/1Bj5HxDzKb7CoxAmdN8mDQWCId2NP9phV/view?usp=sharing title: ERD of the Finance Database description: Learn more about the schema of the Finance Database type: "diagrams" icon: FileTextIcon --- ## Overview The Finance Database is the authoritative persistence layer for the Finance Service's saga orchestration engine. Built on PostgreSQL 18, this database manages the complete lifecycle of order processing state machines, ensuring reliable distributed transaction coordination across multiple microservices. It implements the Transactional Outbox pattern through MassTransit's Entity Framework Core integration, guaranteeing exactly-once message delivery and maintaining data consistency in the face of network failures, service outages, and partial system failures. ## Database Purpose & Role ### Saga State Management The Finance Database serves as the durable state store for the **Order State Machine**, which orchestrates complex, multi-step order processing workflows. Each order flows through multiple states (Placed, Completed, Cancelled, Failed) with the database tracking: - Current state of each order saga instance - Business data (order ID, basket ID, customer info, total amount) - Temporal data (placement date, retry counts, timeout tokens) - Correlation identifiers for distributed tracing ### Transactional Outbox Pattern To ensure reliable message delivery without distributed transactions (2PC), the database implements the Transactional Outbox pattern: - **Atomic Writes**: Business state and outgoing messages are written in the same database transaction - **Guaranteed Delivery**: Messages are published only after successful transaction commit - **Exactly-Once Semantics**: Prevents duplicate message sends even during failures - **Fault Tolerance**: Messages survive service crashes and restarts ### Inbox Deduplication The Inbox State table prevents duplicate message processing: - **Idempotency**: Tracks consumed messages by message ID and consumer ID - **Duplicate Detection**: Rejects already-processed messages automatically - **At-Least-Once to Exactly-Once**: Converts unreliable message delivery to reliable processing ## Schema Design ### Core Tables #### `order_state` - Saga State Machine Instances The heart of the Finance Database, storing the state of each order processing saga with the following columns: - **correlation_id** (uuid, PRIMARY KEY): Unique saga instance identifier - **order_id** (uuid, NOT NULL): Business order identifier - **basket_id** (uuid, NOT NULL): Source basket identifier - **email** (varchar(255)): Customer email - **current_state** (text, NOT NULL): Current state (Placed, Completed, etc.) - **total_money** (numeric(18,2)): Order total amount - **order_placed_date** (timestamptz): When order was initiated - **full_name** (text): Customer full name - **place_order_timeout_token_id** (uuid): Scheduled timeout message identifier - **timeout_retry_count** (int, DEFAULT 0): Number of retry attempts - **version** (int, NOT NULL): Optimistic concurrency version **Key Features**: - **Correlation ID**: Primary key, used to correlate all messages for a single saga instance - **Current State**: String-based state representation (mapped to state machine states) - **Optimistic Concurrency**: Version field prevents concurrent update conflicts - **Timeout Tracking**: Retry count and token ID manage timeout-based retries - **Business Data**: Captures essential order information for saga execution **State Lifecycle**: Initially → Placed → Completed / Cancelled / Failed #### `outbox_message` - Transactional Outbox Stores messages to be published, ensuring atomic state change + message send with the following columns: - **sequence_number** (bigint, PRIMARY KEY IDENTITY): Auto-incrementing sequence - **message_id** (uuid, NOT NULL): Unique message identifier - **message_type** (text, NOT NULL): Full message type name - **body** (text, NOT NULL): Serialized message content - **content_type** (varchar(256), NOT NULL): Serialization format (JSON) - **enqueue_time** (timestamptz): When message was stored - **sent_time** (timestamptz, NOT NULL): When message was published - **conversation_id** (uuid): Conversation tracking - **correlation_id** (uuid): Saga correlation ID - **initiator_id** (uuid): Original message initiator - **request_id** (uuid): Request-response tracking - **source_address** (varchar(256)): Message source endpoint - **destination_address** (varchar(256)): Target endpoint - **response_address** (varchar(256)): Reply-to address - **fault_address** (varchar(256)): Error handling address - **expiration_time** (timestamptz): Message TTL - **headers** (text): Additional message headers - **properties** (text): Custom message properties - **inbox_message_id** (uuid): Related inbox message - **inbox_consumer_id** (uuid): Related inbox consumer - **outbox_id** (uuid): Related outbox state **Key Features**: - **Sequential Processing**: Auto-incrementing sequence ensures ordered delivery - **Message Metadata**: Complete envelope information for routing and correlation - **Delivery Tracking**: Enqueue and sent times track processing latency - **Foreign Keys**: Links to inbox_state and outbox_state for transactional consistency **Workflow**: 1. Business transaction writes to `order_state` and `outbox_message` atomically 2. Background worker polls `outbox_message` for unsent messages 3. Messages are published to message broker (RabbitMQ) 4. `sent_time` is updated after successful publish #### `outbox_state` - Outbox Tracking Maintains the current position of outbox message processing per endpoint with the following columns: - **outbox_id** (uuid, PRIMARY KEY): Unique outbox identifier - **lock_id** (uuid, NOT NULL): Distributed lock identifier - **row_version** (bytea): Optimistic concurrency token - **created** (timestamptz, NOT NULL): Outbox creation time - **delivered** (timestamptz): Last delivery time - **last_sequence_number** (bigint): Last processed message sequence **Key Features**: - **Processing Cursor**: Tracks which messages have been delivered - **Distributed Locking**: Prevents multiple workers from processing the same outbox - **Checkpoint Recovery**: Enables resume from last processed sequence after failure #### `inbox_state` - Message Deduplication Tracks consumed messages to ensure idempotent processing with the following columns: - **id** (bigint, PRIMARY KEY IDENTITY): Internal identifier - **message_id** (uuid, NOT NULL): Incoming message identifier - **consumer_id** (uuid, NOT NULL): Consumer endpoint identifier - **lock_id** (uuid, NOT NULL): Processing lock - **row_version** (bytea): Concurrency token - **received** (timestamptz, NOT NULL): When message arrived - **receive_count** (int, NOT NULL): Delivery attempt count - **expiration_time** (timestamptz): Message expiration - **consumed** (timestamptz): When message was processed - **delivered** (timestamptz): When processing completed - **last_sequence_number** (bigint): Sequence tracking - **UNIQUE CONSTRAINT** on (message_id, consumer_id): Deduplication constraint **Key Features**: - **Composite Uniqueness**: `(message_id, consumer_id)` ensures once-per-consumer processing - **Retry Tracking**: `receive_count` monitors delivery attempts - **State Tracking**: Timestamps track message lifecycle (received → consumed → delivered) - **Lock Management**: `lock_id` prevents concurrent processing of the same message **Deduplication Flow**: 1. Message arrives from message broker 2. Check `inbox_state` for `(message_id, consumer_id)` tuple 3. If exists: reject as duplicate 4. If new: insert record and process message 5. Update `consumed` and `delivered` timestamps on completion #### `__EFMigrationsHistory` - Schema Version Tracking Standard Entity Framework Core migrations table with the following columns: - **migration_id** (varchar(150), PRIMARY KEY): Migration identifier - **product_version** (varchar(32), NOT NULL): EF Core version Tracks applied database migrations for schema evolution management. ## Data Flow & Integration ### Saga Lifecycle **1. Saga Initialization** (`OrderPlaced` Event) - UserCheckedOutEvent received by Finance Service - INSERT into order_state table - INSERT into outbox_message table (PlaceOrderCommand) - Transaction COMMIT - Outbox Worker publishes message to message broker **2. Saga Progression** (`OrderCompleted`/`OrderCancelled` Events) - StatusChangedEvent received - CHECK inbox_state for deduplication - UPDATE order_state (transition to new state) - INSERT into outbox_message (completion commands) - Transaction COMMIT **3. Saga Completion** - Final state transition executed - order_state record remains as historical record - outbox_message archived or purged - inbox_state retained for deduplication window ### Timeout & Retry Management **Scheduled Timeout Handling**: 1. `PlaceOrderTimeoutSchedule` published with `place_order_timeout_token_id` 2. Timeout message delivered after configured delay (e.g., 5 minutes) 3. If order still in `Placed` state: increment `timeout_retry_count` 4. If `timeout_retry_count < MaxAttempts`: retry `PlaceOrderCommand` 5. If `timeout_retry_count >= MaxAttempts`: transition to `Failed` state **Retry Configuration** (from `OrderStateMachineSettings`): - `MaxAttempts`: Maximum retry count (typically 3-5) - `MaxRetryTimeout`: Delay between retries (typically 5-10 minutes) ### Message Correlation All messages related to a single order saga share the same `correlation_id`. For example, a single order flow would have: UserCheckedOutEvent, OrderState, PlaceOrderCommand, and OrderCompletedEvent all sharing the same correlation_id value. This enables distributed tracing and debugging across service boundaries. ## Performance & Optimization ### Indexing Strategy **Critical Indexes**: - Primary keys (automatically indexed): order_state(correlation_id), outbox_message(sequence_number), outbox_state(outbox_id), inbox_state(id) - Unique constraints (automatically indexed): inbox_state(message_id, consumer_id) - Recommended additional indexes: idx_order_state_order_id, idx_order_state_current_state, idx_outbox_message_sent_time (on sent_time WHERE sent_time IS NULL), idx_inbox_state_consumed (on consumed WHERE consumed IS NULL) **Performance Characteristics**: - **Saga Lookup**: O(1) via primary key (correlation_id) - **Outbox Polling**: Sequential scan with WHERE clause (minimized by sent_time index) - **Inbox Deduplication**: O(1) via unique constraint - **State Queries**: O(log n) via current_state index ### Query Patterns **High-Frequency Queries**: 1. **Get Saga State**: Lookup by correlation_id 2. **Poll Outbox**: Select unsent messages ordered by sequence_number 3. **Check Inbox**: Existence check by message_id and consumer_id **Write Patterns**: - **Insert Saga**: Single INSERT on saga initialization - **Update Saga**: Single UPDATE per state transition (optimistic concurrency) - **Insert Outbox**: 1-3 INSERTs per state transition (one per published message) - **Insert Inbox**: Single INSERT per consumed message ### Database Sizing **Estimated Growth** (per 1,000 orders/day): - **order_state**: ~200 KB/day (200 bytes/row × 1,000 rows) - **outbox_message**: ~1.5 MB/day (1.5 KB/row × 1,000 rows, purged after 7 days) - **inbox_state**: ~500 KB/day (500 bytes/row × 1,000 rows, purged after 30 days) **Annual Storage** (without archival): - **order_state**: ~73 MB/year (retained indefinitely) - **outbox_message**: ~10.5 MB (rolling 7-day window) - **inbox_state**: ~15 MB (rolling 30-day window) **Total Database Size** (3-year retention): ~250 MB + indexes (~500 MB total) ## Data Lifecycle & Retention ### Retention Policy - **order_state**: 7 years (financial and regulatory compliance) - **outbox_message**: 7 days (after successful delivery) - **inbox_state**: 30 days (deduplication window) - **outbox_state**: Indefinite (small metadata table) ### Archival Strategy **Hot Storage** (Active Database): - Current order sagas (Placed, In-Progress) - Recent completed/cancelled orders (last 90 days) - Active outbox/inbox records **Warm Storage** (Separate Archive Database): - Completed orders (90 days - 2 years) - Queryable for customer service and reporting **Cold Storage** (Data Lake / Object Storage): - Historical orders (2+ years) - Compliance and audit records - Immutable, compressed archives ### Cleanup Jobs **Automated Maintenance**: - **Daily**: Purge old outbox messages (older than 7 days after sent) - **Weekly**: Purge old inbox messages (consumed more than 30 days ago) - **Monthly**: Archive old order states (older than 2 years) to separate archive database, then delete from primary database ## Monitoring & Observability ### Key Performance Indicators **Saga Metrics**: - **Active Sagas**: Count of orders in non-terminal states - **Avg Saga Duration**: Time from Placed to Completed/Cancelled - **Saga Success Rate**: Percentage reaching Completed state - **Saga Failure Rate**: Percentage reaching Failed state - **Retry Rate**: Average retries per saga **Outbox Metrics**: - **Outbox Backlog**: Count of unsent messages - **Outbox Latency**: Time between enqueue and send - **Outbox Throughput**: Messages processed per second - **Outbox Errors**: Failed delivery attempts **Inbox Metrics**: - **Duplicate Rate**: Percentage of rejected duplicate messages - **Processing Latency**: Time from received to consumed - **Inbox Backlog**: Count of unprocessed messages **Database Metrics**: - **Connection Pool Usage**: Active vs. available connections - **Query Performance**: p95/p99 latency per query type - **Table Sizes**: Growth rate of each table - **Lock Contention**: Wait times for row locks ### Health Checks **Database Connectivity**: - Verify database is reachable and accepting connections with simple query **Saga Processing**: - Check for stalled sagas (orders in Placed state for more than 1 hour) - Alert if count exceeds threshold **Outbox Health**: - Check for backlogged outbox messages (unsent messages count) - Alert if more than 1000 pending messages ### Logging & Tracing **Structured Logging**: - Saga state transitions logged with correlation_id - Outbox message processing logged with sequence_number - Database errors logged with query context **Distributed Tracing**: - OpenTelemetry spans for saga operations - Database queries tracked with correlation_id - End-to-end tracing across service boundaries ## Security & Compliance ### Data Classification - **Classification**: Internal - Contains financial transaction data - **Access Mode**: Read/Write - Finance Service only - **Residency**: East Asia region - Data sovereignty compliance - **Authoritative**: True - Single source of truth for order saga state ### Security Measures **Access Control**: - Database user with least-privilege permissions - No direct external access (internal microservices only) - Service-level authentication via connection strings - Row-level security not required (single-tenant per service) **Data Protection**: - Encryption at rest (Azure PostgreSQL managed encryption) - Encryption in transit (TLS 1.2+) - No sensitive payment data stored (PCI DSS scope reduction) - Customer PII limited to email and name **Audit Trail**: - All saga state changes tracked with timestamps - Outbox/inbox provide complete message audit trail - Database transaction logs retained for 30 days - Long-term audit via archived order_state records ### Compliance Considerations **Financial Regulations**: - 7-year retention for order financial records - Immutable audit trail via event sourcing pattern - Complete transaction history reconstructable from saga state **GDPR / Privacy**: - Customer PII (email, name) stored in order_state - Right to erasure: anonymize PII while retaining financial data - Data export: query order_state by customer identifier ## Disaster Recovery & High Availability ### Backup Strategy **Automated Backups** (Azure PostgreSQL Flexible Server): - **Point-in-Time Recovery (PITR)**: 30-day retention - **Backup Frequency**: Continuous (transaction log streaming) - **Recovery Objective**: RPO < 5 minutes, RTO < 1 hour - **Geo-Redundancy**: Backups replicated to secondary region **Manual Snapshots**: - Pre-deployment snapshots before schema migrations - Retained for 90 days - Used for rollback scenarios ### High Availability **Azure PostgreSQL HA Configuration**: - **Availability Zone Redundancy**: Synchronous replica in different AZ - **Automatic Failover**: Sub-60-second failover time - **Read Replicas**: Optional for read-heavy workloads - **Connection Pooling**: PgBouncer for connection management ### Disaster Recovery Procedures **Failure Scenarios**: 1. **Database Connection Failure**: - Connection pool retry with exponential backoff - Circuit breaker trips after 5 consecutive failures - Graceful degradation: return 503 Service Unavailable 2. **Data Corruption**: - Restore from PITR to point before corruption - Replay outbox messages from backup - Reconcile saga state with downstream services 3. **Complete Database Loss**: - Failover to geo-redundant replica (RTO ~1 hour) - Restore from latest backup snapshot - Message broker retains unacknowledged messages **Recovery Testing**: - Quarterly disaster recovery drills - Automated backup restoration tests - Chaos engineering (random database shutdowns) ## Integration with MassTransit ### Entity Framework Core Integration The Finance Database is tightly integrated with MassTransit's saga persistence through the FinanceDbContext. The configuration includes: - DbContext registered with Npgsql provider for PostgreSQL - Saga state machine (OrderStateMachine) configured with OrderState entity - Entity Framework repository configured to use existing DbContext - Entity Framework Outbox pattern enabled for transactional messaging - PostgreSQL-specific optimizations applied **Key Integration Points**: - **Saga Repository**: MassTransit reads/writes `order_state` via EF Core - **Outbox Pattern**: Messages written to `outbox_message` in same transaction - **Inbox Pattern**: Deduplication via `inbox_state` checks - **Optimistic Concurrency**: EF Core version field prevents concurrent updates ### Message Serialization **JSON Serialization**: - Message bodies stored as JSON text in `outbox_message.body` - Content-Type: `application/vnd.masstransit+json` - Backward-compatible schema evolution **Message Headers**: - Stored as JSON in `outbox_message.headers` - Includes routing metadata, correlation IDs, timestamps --- id: NotificationDatabase name: Notification Database version: 1.0.0 container_type: database technology: postgres@18 authoritative: true access_mode: readWrite classification: internal retention: 7y residency: eastasia summary: PostgreSQL database managing transactional email outbox and message delivery tracking for customer notifications. owners: - nhanxnguyen attachments: - url: https://drive.google.com/file/d/1qE9q0Op-J6skiEk6C3G8zxE18lyqfZVA/view?usp=sharing title: ERD of the Notification Database description: Learn more about the schema of the Notification Database type: "diagrams" icon: FileTextIcon --- ## Overview The Notification Database is the persistence layer for the Notification Service, managing the complete lifecycle of transactional email communications in the BookWorm e-commerce system. Built on PostgreSQL 18, this database implements a custom email outbox pattern alongside MassTransit's standard outbox/inbox tables, ensuring reliable email delivery with comprehensive audit trails. It serves as the authoritative source for all customer notification attempts, tracking creation, delivery status, and retention for compliance and debugging purposes. ## Database Purpose & Role ### Email Outbox Pattern The Notification Database implements a specialized email outbox pattern for reliable email delivery: - **Atomic Storage**: Email data stored transactionally with event processing - **Guaranteed Delivery**: Emails persist until successfully sent - **Retry Management**: Failed emails remain in outbox for retry attempts - **Audit Trail**: Complete history of all email communications - **Idempotency**: Prevents duplicate email sends through deduplication ### Transactional Messaging Integration The database integrates with MassTransit for event-driven communication: - **Inbox Pattern**: Deduplicates incoming order events - **Outbox Pattern**: Ensures atomic event processing and email creation - **Message Correlation**: Links emails to originating order events - **Fault Tolerance**: Survives service restarts and network failures ### Compliance & Auditing Long-term retention supports business and regulatory requirements: - **Communication History**: 7-year retention of all sent emails - **Customer Service**: Historical reference for support inquiries - **Dispute Resolution**: Evidence of notification delivery - **GDPR Compliance**: Right to access communication history - **Business Analytics**: Email delivery metrics and trends ## Schema Design ### Core Tables #### `outboxes` - Email Outbox Queue The primary table storing pending and sent transactional emails with the following columns: - **id** (uuid, PRIMARY KEY): Unique email identifier - **to_name** (varchar(100), NOT NULL, DEFAULT ''): Recipient's full name - **to_email** (varchar(255), NOT NULL, DEFAULT ''): Recipient's email address - **subject** (varchar(100), NOT NULL, DEFAULT ''): Email subject line - **body** (varchar(10000), NOT NULL, DEFAULT ''): Email body content (HTML or plain text) - **is_sent** (boolean, NOT NULL): Delivery status flag - **sequence_number** (bigint, IDENTITY, NOT NULL): Auto-incrementing sequence for processing order - **created_at** (timestamptz, NOT NULL): When email was created - **sent_at** (timestamptz): When email was successfully delivered **Key Features**: - **Sequence Number**: Ensures FIFO processing of emails - **Status Tracking**: Boolean flag for quick filtering of pending emails - **Timestamp Audit**: Creation and delivery times for SLA monitoring - **Recipient Info**: Both name and email for personalized delivery - **Content Storage**: Complete email content for retry and audit purposes **Email Lifecycle**: - Created: Email added to outbox with is_sent = false - Processing: Background worker picks up emails by sequence_number - Sent: Email delivered successfully, is_sent = true, sent_at = current timestamp - Failed: Email remains with is_sent = false for retry (with exponential backoff) #### `outbox_message` - MassTransit Transactional Outbox Stores integration events to be published, ensuring atomic email creation and event publishing with the following columns: - **sequence_number** (bigint, PRIMARY KEY IDENTITY): Auto-incrementing sequence - **message_id** (uuid, NOT NULL): Unique message identifier - **message_type** (text, NOT NULL): Full message type name - **body** (text, NOT NULL): Serialized message content (JSON) - **content_type** (varchar(256), NOT NULL): Serialization format - **enqueue_time** (timestamptz): When message was stored - **sent_time** (timestamptz, NOT NULL): When message was published - **conversation_id** (uuid): Conversation tracking - **correlation_id** (uuid): Event correlation ID - **initiator_id** (uuid): Original message initiator - **request_id** (uuid): Request-response tracking - **source_address** (varchar(256)): Message source endpoint - **destination_address** (varchar(256)): Target endpoint - **response_address** (varchar(256)): Reply-to address - **fault_address** (varchar(256)): Error handling address - **expiration_time** (timestamptz): Message TTL - **headers** (text): Additional message headers (JSON) - **properties** (text): Custom message properties (JSON) - **inbox_message_id** (uuid): Related inbox message (foreign key) - **inbox_consumer_id** (uuid): Related inbox consumer (foreign key) - **outbox_id** (uuid): Related outbox state (foreign key) **Key Features**: - **Sequential Processing**: Ensures message ordering - **Complete Envelope**: All routing and correlation metadata - **Foreign Key Relationships**: Links to inbox_state and outbox_state for consistency - **Audit Trail**: Tracks enqueue and delivery times #### `outbox_state` - Outbox Processing State Tracks the current position of outbox message processing with the following columns: - **outbox_id** (uuid, PRIMARY KEY): Unique outbox identifier - **lock_id** (uuid, NOT NULL): Distributed lock identifier - **row_version** (bytea): Optimistic concurrency token - **created** (timestamptz, NOT NULL): Outbox creation time - **delivered** (timestamptz): Last delivery time - **last_sequence_number** (bigint): Last processed message sequence **Key Features**: - **Processing Cursor**: Tracks last processed message - **Distributed Locking**: Prevents concurrent processing by multiple workers - **Recovery Support**: Enables resume from last sequence after failures #### `inbox_state` - Message Deduplication Tracks consumed order events to ensure idempotent email creation with the following columns: - **id** (bigint, PRIMARY KEY IDENTITY): Internal identifier - **message_id** (uuid, NOT NULL): Incoming message identifier - **consumer_id** (uuid, NOT NULL): Consumer endpoint identifier - **lock_id** (uuid, NOT NULL): Processing lock - **row_version** (bytea): Concurrency token - **received** (timestamptz, NOT NULL): When message arrived - **receive_count** (int, NOT NULL): Delivery attempt count - **expiration_time** (timestamptz): Message expiration - **consumed** (timestamptz): When message was processed - **delivered** (timestamptz): When processing completed - **last_sequence_number** (bigint): Sequence tracking - **UNIQUE CONSTRAINT** on (message_id, consumer_id): Deduplication key **Key Features**: - **Idempotency Guarantee**: Prevents duplicate email creation from retried events - **Composite Uniqueness**: Per-consumer deduplication - **Retry Tracking**: Monitors delivery attempts - **Lifecycle Timestamps**: Full message processing history #### `__EFMigrationsHistory` - Schema Version Control Standard Entity Framework Core migrations table with the following columns: - **migration_id** (varchar(150), PRIMARY KEY): Migration identifier - **product_version** (varchar(32), NOT NULL): EF Core version Tracks applied database migrations for schema evolution management. ## Data Flow & Integration ### Email Creation Flow **1. Order Event Received** - PlaceOrderCommand, CompleteOrderCommand, or CancelOrderCommand arrives via RabbitMQ - MassTransit checks inbox_state for duplicate detection - If new message, insert into inbox_state and proceed - If duplicate, skip processing **2. Email Composition** - Event data extracted (order ID, customer name, email, order details) - Email template rendered with dynamic data - MIME message constructed with HTML body - Outbox entity created with recipient info and content **3. Transactional Storage** - INSERT into outboxes table (email details) - INSERT into outbox_message table (event acknowledgment if needed) - Transaction COMMIT (atomic write) - Email now guaranteed to be sent eventually **4. Background Processing** - Background worker polls outboxes WHERE is_sent = false ORDER BY sequence_number - Email picked up for delivery - SMTP send attempted via MailKit to SendGrid or MailPit - On success: UPDATE outboxes SET is_sent = true, sent_at = NOW() - On failure: Email remains pending for retry ### Retry Strategy **Exponential Backoff**: - First retry: 5 minutes after failure - Second retry: 15 minutes after first retry - Third retry: 1 hour after second retry - Max retries: 5 attempts over 24 hours - After max retries: Email flagged for manual intervention **Failure Handling**: - Transient failures (network, SMTP server down): Automatic retry - Permanent failures (invalid email, blocked recipient): Manual review - Rate limiting: Throttle processing to avoid SendGrid limits ### Cleanup & Archival **Sent Email Cleanup**: - CleanUpSentEmailIntegrationEvent triggers periodic cleanup - Emails with is_sent = true and sent_at older than retention period are archived - Archived emails moved to cold storage (Azure Blob or data lake) - DELETE from outboxes table to manage database size **Retention Strategy**: - Active emails (unsent or recent): Kept in primary database - Sent emails (recent 90 days): Kept in primary database for quick access - Historical emails (90 days - 7 years): Archived to warm storage - Compliance records (7+ years): Compressed in cold storage ## Performance & Optimization ### Indexing Strategy **Critical Indexes**: - Primary keys (automatically indexed): outboxes(id), outbox_message(sequence_number), outbox_state(outbox_id), inbox_state(id) - Unique constraints (automatically indexed): inbox_state(message_id, consumer_id) - Recommended additional indexes: idx_outboxes_is_sent (on is_sent WHERE is_sent = false), idx_outboxes_sequence (on sequence_number), idx_outboxes_created_at (on created_at) **Performance Characteristics**: - **Email Lookup**: O(1) via primary key (id) - **Pending Email Query**: O(log n) via is_sent index - **Sequential Processing**: O(1) via sequence_number index - **Inbox Deduplication**: O(1) via unique constraint ### Query Patterns **High-Frequency Queries**: 1. **Get Pending Emails**: Select emails WHERE is_sent = false ORDER BY sequence_number LIMIT batch_size 2. **Check Duplicate Event**: Existence check in inbox_state by (message_id, consumer_id) 3. **Mark Email Sent**: UPDATE outboxes SET is_sent = true, sent_at = NOW() WHERE id = ? **Write Patterns**: - **Insert Email**: Single INSERT per order event - **Update Email Status**: Single UPDATE after successful send - **Bulk Cleanup**: Batch DELETE for old sent emails (daily maintenance) ### Database Sizing **Estimated Growth** (per 1,000 orders/day, assuming 1 email per order): - **outboxes**: ~500 KB/day (500 bytes/row × 1,000 rows) - **outbox_message**: ~1.5 MB/day (purged after 7 days) - **inbox_state**: ~500 KB/day (purged after 30 days) **Annual Storage** (with retention): - **outboxes**: ~180 MB/year (retained for 7 years = 1.26 GB) - **outbox_message**: ~10.5 MB (rolling 7-day window) - **inbox_state**: ~15 MB (rolling 30-day window) **Total Database Size** (7-year retention): ~1.5 GB + indexes (~3 GB total) ## Data Lifecycle & Retention ### Retention Policy - **outboxes**: 7 years (business and compliance requirements) - **outbox_message**: 7 days (after successful delivery) - **inbox_state**: 30 days (deduplication window) - **outbox_state**: Indefinite (small metadata table) ### Archival Strategy **Hot Storage** (Active Database): - Unsent emails (pending delivery) - Recently sent emails (last 90 days) - Active inbox/outbox message records **Warm Storage** (Archive Database or Blob): - Sent emails (90 days - 2 years) - Queryable for customer service - Indexed for fast retrieval **Cold Storage** (Data Lake): - Historical emails (2+ years) - Compressed archives for compliance - Rarely accessed, batch retrieval only ### Cleanup Jobs **Automated Maintenance**: - **Daily**: Purge old outbox_message records (sent more than 7 days ago) - **Weekly**: Purge old inbox_state records (consumed more than 30 days ago) - **Monthly**: Archive sent emails (sent more than 2 years ago) to cold storage - **Quarterly**: Vacuum database to reclaim space from deleted records ## Monitoring & Observability ### Key Performance Indicators **Email Metrics**: - **Pending Email Count**: Number of unsent emails in outbox - **Avg Send Latency**: Time from created_at to sent_at - **Send Success Rate**: Percentage of emails successfully delivered - **Send Failure Rate**: Percentage requiring retries or manual intervention - **Daily Email Volume**: Total emails created per day **Delivery Metrics**: - **SMTP Response Times**: Average time for email provider to accept - **Bounce Rate**: Percentage of emails rejected by recipient server - **Retry Count Distribution**: How many emails require 1, 2, 3+ retries - **Queue Depth**: Number of pending emails waiting for processing **Database Metrics**: - **Table Sizes**: Growth rate of outboxes table - **Query Performance**: p95/p99 latency for pending email queries - **Connection Pool Usage**: Active vs. available connections - **Disk Usage**: Current database size vs. allocated space ### Health Checks **Database Connectivity**: - Verify database is reachable with simple query - Check connection pool availability **Email Queue Health**: - Alert if pending email count exceeds 1,000 (indicates delivery issues) - Alert if oldest pending email is more than 1 hour old (processing stalled) **Processing Health**: - Monitor emails sent per minute (should match expected throughput) - Alert if no emails sent in last 15 minutes (worker may be down) ### Logging & Tracing **Structured Logging**: - Email creation logged with order_id and recipient email (masked) - Delivery attempts logged with SMTP response codes - Failures logged with error details and retry count - Cleanup operations logged with record counts **Distributed Tracing**: - OpenTelemetry spans for email processing - Correlation IDs link email to originating order event - End-to-end tracing from order event to email delivery ## Security & Compliance ### Data Classification - **Classification**: Internal - Contains customer PII (names and email addresses) - **Access Mode**: Read/Write - Notification Service only - **Residency**: East Asia region - Data sovereignty compliance - **Authoritative**: True - Single source of truth for notification history ### Security Measures **Access Control**: - Database user with least-privilege permissions - No direct external access (internal microservices only) - Service-level authentication via connection strings - Row-level security not required (single-tenant per service) **Data Protection**: - Encryption at rest (Azure PostgreSQL managed encryption) - Encryption in transit (TLS 1.2+) - Email addresses masked in logs (first 3 chars + domain) - No sensitive payment data stored **Email Security**: - SPF, DKIM, and DMARC configured for domain authentication - SMTP credentials stored in secure key vault (Azure Key Vault) - Email content sanitized to prevent injection attacks - Rate limiting to prevent abuse **Audit Trail**: - All email creation and delivery logged with timestamps - Complete history reconstructable from database records - Immutable audit trail via retention policy - Access logs retained for 90 days ### Compliance Considerations **GDPR / Privacy**: - Customer names and email addresses stored (PII) - Right to access: Query outboxes by email address - Right to erasure: Anonymize email addresses while retaining audit metadata - Right to portability: Export customer's email history **CAN-SPAM Act**: - Transactional emails exempt from unsubscribe requirements - Accurate sender information in all emails - Non-deceptive subject lines - Physical address included in email footer **Data Retention**: - 7-year retention for business and tax purposes - Automatic archival after 2 years - Secure deletion after retention period expires ## Disaster Recovery & High Availability ### Backup Strategy **Automated Backups** (Azure PostgreSQL Flexible Server): - **Point-in-Time Recovery (PITR)**: 30-day retention - **Backup Frequency**: Continuous transaction log streaming - **Recovery Objective**: RPO less than 5 minutes, RTO less than 1 hour - **Geo-Redundancy**: Backups replicated to secondary region **Manual Snapshots**: - Pre-deployment snapshots before schema changes - Retained for 90 days - Used for rollback scenarios ### High Availability **Azure PostgreSQL HA Configuration**: - **Availability Zone Redundancy**: Synchronous replica in different AZ - **Automatic Failover**: Sub-60-second failover time - **Read Replicas**: Optional for reporting queries - **Connection Pooling**: PgBouncer for efficient connection management ### Disaster Recovery Procedures **Failure Scenarios**: 1. **Database Connection Failure**: - Connection pool retry with exponential backoff - Circuit breaker trips after 5 consecutive failures - Graceful degradation: Queue emails in memory temporarily 2. **Email Provider Outage**: - Emails remain in outbox with is_sent = false - Automatic retry when provider recovers - Failover to backup SMTP provider if configured 3. **Complete Database Loss**: - Failover to geo-redundant replica (RTO approximately 1 hour) - Restore from latest backup snapshot - Unsent emails are preserved and automatically retried **Recovery Testing**: - Quarterly disaster recovery drills - Automated backup restoration tests - Chaos engineering experiments (random failures) ## Integration Points ### Upstream Event Sources **Finance Service**: - Receives PlaceOrderCommand when order is placed - Creates order confirmation email for customer **Ordering Service**: - Receives CompleteOrderCommand when order is fulfilled - Creates order completion email with tracking info **Ordering Service**: - Receives CancelOrderCommand when order is cancelled - Creates order cancellation email with refund info ### Downstream Email Providers **SendGrid** (Production): - Primary email delivery service - API integration via MailKit SMTP - Rate limits: 100 emails/second - Delivery tracking via webhooks **MailPit** (Development): - Local email testing server - SMTP server for development environments - Web UI for viewing sent emails ### Management Events **CleanUpSentEmailIntegrationEvent**: - Scheduled event for periodic cleanup - Triggers archival of old sent emails - Maintains database size within limits **ResendErrorEmailIntegrationEvent**: - Manual retry trigger for failed emails - Allows operations team to resend specific emails - Bypasses automatic retry limits --- id: OrderingCache name: Ordering Cache version: 1.0.0 container_type: cache technology: redis@8.2 authoritative: true access_mode: readWrite classification: internal retention: 2y residency: eastasia summary: Distributed cache for frequently accessed order and buyer data. owners: - nhanxnguyen --- ## Overview The Ordering Cache is a high-performance Redis-based distributed caching layer that sits between the Ordering Service and its primary data sources. Operating on Redis 8.2, this cache dramatically reduces database load and improves response times by storing frequently accessed order and buyer information in memory. In an e-commerce environment where the same orders and buyers are queried multiple times, the cache transforms expensive database queries into lightning-fast memory lookups, ensuring customers experience consistent sub-millisecond response times even during peak traffic. ## The Caching Strategy ### Why Cache? E-commerce order queries often exhibit temporal and spatial locality, where recently accessed orders and buyers are likely to be queried again soon. The Ordering Cache exploits this pattern by: - **Reducing Database Load**: Offloading 70-80% of read operations from PostgreSQL - **Improving Latency**: Response times drop from 50-100ms (database) to 1-5ms (cache) - **Enabling Scale**: Supporting 10x more concurrent users without database upgrades - **Cost Efficiency**: Reducing database instance size requirements by 50% - **High Availability**: Acting as a buffer during database maintenance or brief outages ### Caching Patterns We employ multiple caching strategies based on data characteristics: **Cache-Aside (Lazy Loading)** - Most common pattern for order and buyer data - Load data into cache only when requested - First request: cache miss → load from DB → store in cache - Subsequent requests: cache hit → return immediately **Write-Through** - For critical order updates - Write to database and cache simultaneously - Ensures cache is always consistent - Used for order status changes **Write-Behind (Write-Back)** - For high-frequency updates (view counts, clicks) - Write to cache immediately, queue database update - Reduces database write pressure - Periodic batch updates to database **Refresh-Ahead** - For predictable access patterns - Proactively refresh cache before expiration - Used for recent orders and active buyers --- id: OrderingDatabase name: Ordering Database version: 1.0.0 container_type: database technology: postgres@18 authoritative: true access_mode: readWrite classification: internal retention: 7y residency: eastasia summary: PostgreSQL database implementing event sourcing with Marten for order management, maintaining complete audit trails and supporting CQRS patterns. owners: - nhanxnguyen attachments: - url: https://drive.google.com/file/d/13k7UXdrFil-d_WXpg4JqG2FnefiXLmhr/view?usp=sharing title: ERD of the Ordering Database description: Learn more about the schema of the Ordering Database type: "diagrams" icon: FileTextIcon --- ## Overview The Ordering Database is the central persistence layer for the Ordering Service, implementing a hybrid architecture that combines traditional relational data storage with event sourcing through Marten. Built on PostgreSQL 18, this database maintains both the current state of orders and buyers alongside a complete immutable event history, enabling powerful audit capabilities, temporal queries, and event replay functionality. The database serves as the authoritative source for all order-related operations in the BookWorm e-commerce platform. ## Database Purpose & Role ### Hybrid Architecture Pattern The Ordering Database implements a sophisticated dual-storage approach: - **State Storage**: Traditional relational tables (orders, order_items, buyers) for efficient querying of current state - **Event Store**: Marten-managed event sourcing tables (mt_events, mt_streams) for complete audit history - **CQRS Support**: Separation enables independent optimization of read and write operations - **Projections**: Marten projections (mt_doc_ordersummaryview) provide optimized read models - **Temporal Queries**: Event store enables point-in-time reconstruction of any order state ### Order Lifecycle Management The database tracks orders through their complete lifecycle: - **Creation**: Orders start in "New" status when placed by customers - **Completion**: Successful fulfillment transitions orders to "Completed" status - **Cancellation**: Customer or system-initiated cancellations move orders to "Cancelled" status - **Soft Delete**: Maintains historical data while marking orders as deleted - **Audit Trail**: Every state change recorded as immutable domain event ### Event Sourcing with Marten Marten provides event sourcing capabilities on top of PostgreSQL: - **Event Append**: All domain events appended to immutable event log - **Stream Management**: Events grouped by aggregate root (order ID) - **Projections**: Automated read model updates from event streams - **Subscriptions**: Real-time event processing for integration events - **Snapshots**: Performance optimization for long event streams ## Schema Design ### Domain Tables #### `orders` - Order Aggregate State The primary table storing current order state with the following columns: - **id** (uuid, PRIMARY KEY, DEFAULT uuidv7()): Unique order identifier using UUID v7 for time-ordered IDs - **status** (smallint, NOT NULL): Current order status (0=New, 1=Cancelled, 2=Completed) - **note** (varchar(500)): Optional customer notes or special instructions - **buyer_id** (uuid, NOT NULL, FOREIGN KEY): Reference to buyer who placed the order - **is_deleted** (boolean, NOT NULL): Soft delete flag for data retention - **created_at** (timestamptz, NOT NULL, DEFAULT NOW() AT TIME ZONE 'UTC'): Order creation timestamp - **last_modified_at** (timestamptz, DEFAULT NOW() AT TIME ZONE 'UTC'): Last modification timestamp **Key Features**: - **UUID v7 IDs**: Time-ordered identifiers for efficient indexing and natural sorting - **Status Enum**: Simple integer enumeration for order states - **Soft Delete**: Preserves order history while marking as deleted - **Audit Timestamps**: Automatic tracking of creation and modification times - **Buyer Reference**: Foreign key relationship with CASCADE on delete **Status Lifecycle**: - New (0): Initial state when order is placed - Cancelled (1): Order cancelled before fulfillment - Completed (2): Order successfully fulfilled and delivered #### `order_items` - Order Line Items Stores individual items within each order with the following columns: - **id** (uuid, PRIMARY KEY, DEFAULT uuidv7()): Unique line item identifier - **quantity** (integer, NOT NULL): Number of units ordered (must be greater than 0) - **price** (numeric, NOT NULL): Unit price at time of order (must be greater than or equal to 0) - **book_id** (uuid, NOT NULL): Reference to product in catalog service - **order_id** (uuid, NOT NULL, FOREIGN KEY): Parent order reference with CASCADE on delete **Key Features**: - **Price Snapshot**: Captures price at order time (not current catalog price) - **Quantity Validation**: Business rules enforce positive quantities - **Cascade Delete**: Line items automatically deleted when parent order deleted - **Product Reference**: Links to catalog service via book_id (not enforced FK for microservice boundaries) **Business Rules**: - Each order must have at least one order item - Quantities must be positive integers - Prices must be non-negative decimals - Line items are immutable once order is placed #### `buyers` - Customer Information Manages buyer profiles and shipping addresses with the following columns: - **id** (uuid, PRIMARY KEY, DEFAULT uuidv7()): Unique buyer identifier - **name** (varchar(20), NOT NULL): Buyer's full name (limited to 20 characters) - **address_street** (varchar(50)): Street address for shipping - **address_city** (varchar(50)): City name - **address_province** (varchar(50)): Province or state name **Key Features**: - **Minimal Profile**: Stores only essential information for order fulfillment - **Optional Address**: Address fields nullable for flexible buyer creation - **Name Constraint**: 20-character limit ensures concise naming - **Geographic Data**: Enables location-based analytics and shipping logistics **Address Components**: - Street address for delivery location - City for regional analytics - Province for broader geographic insights - No postal code (simplified addressing) ### Event Sourcing Tables (Marten) #### `mt_events` - Event Store The core event store table managed by Marten with the following columns: - **seq_id** (bigint, PRIMARY KEY): Global sequence number for all events (auto-incrementing) - **id** (uuid, NOT NULL): Unique event identifier - **stream_id** (uuid, FOREIGN KEY): Reference to aggregate stream - **version** (bigint, NOT NULL): Event version within its stream (for optimistic concurrency) - **data** (jsonb, NOT NULL): Event payload stored as JSON - **type** (varchar(500), NOT NULL): Fully qualified event type name - **timestamp** (timestamptz, NOT NULL, DEFAULT NOW()): When event was recorded - **tenant_id** (varchar, DEFAULT '_DEFAULT_'): Multi-tenancy support - **mt_dotnet_type** (varchar): .NET type information for deserialization - **correlation_id** (varchar): Distributed tracing correlation - **causation_id** (varchar): Causal relationship tracking - **headers** (jsonb): Additional metadata - **is_archived** (boolean, NOT NULL, DEFAULT false): Archive flag for old events **Key Features**: - **Immutable Log**: Events never modified after creation - **Sequential Ordering**: Global sequence ensures total ordering - **Version Control**: Per-stream versioning prevents concurrent update conflicts - **JSONB Storage**: Efficient storage and querying of event data - **Metadata Rich**: Correlation and causation for distributed systems - **Archival Support**: Old events can be archived without deletion **Event Types**: - OrderPlacedEvent: When new order is created - OrderCompletedEvent: When order fulfillment succeeds - OrderCancelledEvent: When order is cancelled #### `mt_streams` - Event Stream Metadata Manages aggregate root streams with the following columns: - **id** (uuid, PRIMARY KEY): Stream identifier (matches aggregate root ID) - **type** (varchar): Aggregate type (e.g., "Order") - **version** (bigint): Current stream version (number of events) - **timestamp** (timestamptz, NOT NULL, DEFAULT NOW()): Last event timestamp - **snapshot** (jsonb): Snapshot data for performance optimization - **snapshot_version** (integer): Version at which snapshot was taken - **created** (timestamptz, NOT NULL, DEFAULT NOW()): Stream creation time - **tenant_id** (varchar, DEFAULT '_DEFAULT_'): Multi-tenancy identifier - **is_archived** (boolean, NOT NULL, DEFAULT false): Archive flag **Key Features**: - **Stream Tracking**: One stream per aggregate root instance - **Version Control**: Optimistic concurrency for aggregate updates - **Snapshot Support**: Performance optimization for long event histories - **Tenant Isolation**: Multi-tenant support for SaaS scenarios - **Archival**: Historical streams can be archived **Snapshot Strategy**: - Snapshots created every N events (configurable, typically 20-50) - Reduces replay time for aggregates with long histories - Snapshots optional, not required for correctness #### `mt_doc_ordersummaryview` - Order Summary Projection Marten-managed read model projection with the following columns: - **id** (uuid, PRIMARY KEY): Order identifier - **data** (jsonb, NOT NULL): Projected order summary data - **mt_last_modified** (timestamptz, DEFAULT transaction_timestamp()): Last projection update - **mt_dotnet_type** (varchar): .NET type for deserialization - **mt_version** (integer, NOT NULL, DEFAULT 0): Projection version **Key Features**: - **Automated Updates**: Marten automatically updates from event stream - **Optimized Queries**: Pre-computed data for fast reads - **JSONB Storage**: Flexible schema for evolving read models - **Version Tracking**: Detects out-of-sync projections **Projected Data**: - Order ID and status - Total price calculation - Creation and modification timestamps - Derived business metrics #### `mt_event_progression` - Projection State Tracking Tracks last processed event for projections with the following columns: - **name** (varchar, PRIMARY KEY): Projection name - **last_seq_id** (bigint): Last processed event sequence ID - **last_updated** (timestamptz, DEFAULT transaction_timestamp()): Last processing time **Key Features**: - **Checkpoint Tracking**: Enables resumable projection processing - **Multiple Projections**: Each projection tracks independently - **Failure Recovery**: Restart from last successful event #### `mt_doc_deadletterevent` - Failed Event Processing Stores events that failed projection processing with the following columns: - **id** (uuid, PRIMARY KEY): Dead letter event identifier - **data** (jsonb, NOT NULL): Failed event data - **mt_last_modified** (timestamptz, DEFAULT transaction_timestamp()): When failure occurred - **mt_version** (uuid, NOT NULL, DEFAULT random_uuid()): Version for concurrency - **mt_dotnet_type** (varchar): Event type information **Key Features**: - **Error Isolation**: Failed events don't block other processing - **Retry Support**: Can be replayed after fixing projection logic - **Debugging Aid**: Captures problematic events for analysis ### MassTransit Integration Tables #### `inbox_state` - Message Deduplication Tracks consumed integration events for idempotency with the following columns: - **id** (bigint, PRIMARY KEY IDENTITY): Internal identifier - **message_id** (uuid, NOT NULL): Incoming message identifier - **consumer_id** (uuid, NOT NULL): Consumer endpoint identifier - **lock_id** (uuid, NOT NULL): Processing lock - **row_version** (bytea): Optimistic concurrency token - **received** (timestamptz, NOT NULL): When message arrived - **receive_count** (int, NOT NULL): Delivery attempt count - **expiration_time** (timestamptz): Message expiration - **consumed** (timestamptz): When message was processed - **delivered** (timestamptz): When processing completed - **last_sequence_number** (bigint): Sequence tracking - **UNIQUE CONSTRAINT** on (message_id, consumer_id): Deduplication key **Key Features**: - **Exactly-Once Semantics**: Prevents duplicate command processing - **Composite Key**: Per-consumer deduplication - **Retry Tracking**: Monitors delivery attempts - **Processing State**: Complete lifecycle tracking #### `outbox_message` - Transactional Outbox Ensures reliable integration event publishing with the following columns: - **sequence_number** (bigint, PRIMARY KEY IDENTITY): Auto-incrementing sequence - **message_id** (uuid, NOT NULL): Unique message identifier - **message_type** (text, NOT NULL): Full message type name - **body** (text, NOT NULL): Serialized message content (JSON) - **content_type** (varchar(256), NOT NULL): Serialization format - **enqueue_time** (timestamptz): When message was stored - **sent_time** (timestamptz, NOT NULL): When message was published - **conversation_id** (uuid): Conversation tracking - **correlation_id** (uuid): Event correlation - **initiator_id** (uuid): Original message initiator - **request_id** (uuid): Request-response tracking - **source_address** (varchar(256)): Message source endpoint - **destination_address** (varchar(256)): Target endpoint - **response_address** (varchar(256)): Reply-to address - **fault_address** (varchar(256)): Error handling address - **expiration_time** (timestamptz): Message TTL - **headers** (text): Additional message headers (JSON) - **properties** (text): Custom message properties (JSON) - **inbox_message_id** (uuid): Related inbox message - **inbox_consumer_id** (uuid): Related inbox consumer - **outbox_id** (uuid): Related outbox state **Key Features**: - **Atomic Publishing**: Messages stored in same transaction as domain events - **Sequential Processing**: Ensures message ordering - **Complete Envelope**: Full routing and correlation metadata #### `outbox_state` - Outbox Processing State Manages outbox processing cursor with the following columns: - **outbox_id** (uuid, PRIMARY KEY): Unique outbox identifier - **lock_id** (uuid, NOT NULL): Distributed lock identifier - **row_version** (bytea): Optimistic concurrency token - **created** (timestamptz, NOT NULL): Outbox creation time - **delivered** (timestamptz): Last delivery time - **last_sequence_number** (bigint): Last processed message sequence **Key Features**: - **Processing Checkpoint**: Tracks last published message - **Distributed Locking**: Prevents concurrent processing - **Recovery Support**: Resume from last sequence after failures #### `__EFMigrationsHistory` - Schema Version Control Standard Entity Framework Core migrations table with the following columns: - **migration_id** (varchar(150), PRIMARY KEY): Migration identifier - **product_version** (varchar(32), NOT NULL): EF Core version Tracks applied database migrations for schema evolution management. ## Data Flow & Integration ### Order Creation Flow **1. Command Reception** - CreateOrderCommand received via HTTP API - Validated for business rules (buyer exists, items valid, prices correct) - Basket data retrieved from Basket Service via gRPC **2. Domain Event Generation** - Order aggregate created with New status - OrderPlacedEvent registered as domain event - Event includes order details, buyer info, and line items **3. Transactional Persistence** - INSERT into orders table (current state) - INSERT into order_items table (line items) - INSERT into mt_events table (OrderPlacedEvent) - INSERT into outbox_message table (UserCheckedOutEvent for Finance Service) - Transaction COMMIT (all atomic) **4. Event Processing** - Marten projections update mt_doc_ordersummaryview - Outbox worker publishes UserCheckedOutEvent to RabbitMQ - Finance Service begins saga orchestration ### Order Completion Flow **1. Command Execution** - CompleteOrderCommand received (typically from Finance Service) - Order retrieved with status validation (must be New) - Business rules enforced (only new orders can be completed) **2. State Transition** - Order status updated to Completed - OrderCompletedEvent registered - last_modified_at timestamp updated **3. Event Persistence** - UPDATE orders SET status=2, last_modified_at=NOW() - INSERT into mt_events (OrderCompletedEvent) - INSERT into outbox_message (OrderStatusChangedToCompleteEvent) - Transaction COMMIT **4. Integration** - Event published to message broker - Finance Service saga transitions to Completed state - Notification Service sends completion email to customer ### Order Cancellation Flow **1. Cancellation Request** - CancelOrderCommand received (from customer or system) - Order retrieved and validated (must be in New status) - Business logic prevents cancellation of completed orders **2. Domain Event** - Order status transitioned to Cancelled - OrderCancelledEvent registered with cancellation reason - Inventory reservations released (via integration events) **3. Database Updates** - UPDATE orders SET status=1, last_modified_at=NOW() - INSERT into mt_events (OrderCancelledEvent) - INSERT into outbox_message (OrderStatusChangedToCancelEvent) - Transaction COMMIT **4. Downstream Impact** - Finance Service saga transitions to Cancelled state - Notification Service sends cancellation confirmation - Basket Service may restore items (if configured) ### Event Sourcing Replay **Event Stream Reconstruction**: - Read all events for order ID from mt_events ORDER BY version - Apply events sequentially to reconstruct aggregate state - Validate current state matches orders table (consistency check) - Used for debugging, audit, and temporal queries **Projection Rebuilding**: - Truncate projection tables (mt_doc_ordersummaryview) - Replay all events from mt_events in order - Marten automatically updates projections - Used after projection schema changes ## Performance & Optimization ### Indexing Strategy **Critical Indexes**: - Primary keys (automatically indexed): orders(id), order_items(id), buyers(id), mt_events(seq_id, is_archived), mt_streams(id, is_archived) - Foreign keys (automatically indexed): order_items(order_id), orders(buyer_id), mt_events(stream_id, is_archived) - Unique constraints (automatically indexed): inbox_state(message_id, consumer_id) - Recommended indexes: orders(buyer_id), orders(created_at), orders(status), mt_events(stream_id), mt_events(type), mt_events(timestamp) **Performance Characteristics**: - **Order Lookup**: O(1) via primary key - **Buyer Orders**: O(log n) via buyer_id index - **Event Stream**: O(log n) via stream_id index - **Status Queries**: O(log n) via status index - **Time-Range Queries**: O(log n) via created_at index ### Query Patterns **High-Frequency Queries**: 1. **Get Order by ID**: SELECT from orders WHERE id = ? (with JOIN to order_items) 2. **List Buyer Orders**: SELECT from orders WHERE buyer_id = ? ORDER BY created_at DESC 3. **Get Event Stream**: SELECT from mt_events WHERE stream_id = ? ORDER BY version 4. **Poll Outbox**: SELECT from outbox_message WHERE sent_time IS NULL ORDER BY sequence_number 5. **Check Inbox**: SELECT from inbox_state WHERE message_id = ? AND consumer_id = ? **Write Patterns**: - **Create Order**: INSERT into orders, order_items (batch), mt_events, outbox_message - **Update Status**: UPDATE orders, INSERT into mt_events, INSERT into outbox_message - **Delete Order**: UPDATE orders SET is_deleted = true (soft delete) ### Database Sizing **Estimated Growth** (per 1,000 orders/day): - **orders**: ~150 KB/day (150 bytes/row × 1,000 rows) - **order_items**: ~600 KB/day (assuming avg 3 items/order, 200 bytes/row × 3,000 rows) - **buyers**: ~50 KB/day (assuming 30% new buyers, 150 bytes/row × 300 rows) - **mt_events**: ~1.5 MB/day (assuming avg 3 events/order, 500 bytes/row × 3,000 events) - **mt_streams**: ~100 KB/day (100 bytes/row × 1,000 streams) - **inbox_state**: ~500 KB/day (500 bytes/row × 1,000 messages) - **outbox_message**: ~1.5 MB/day (purged after 7 days) **Annual Storage** (with retention): - **Domain tables**: ~300 MB/year (orders + order_items + buyers) - **Event store**: ~550 MB/year (mt_events + mt_streams) - **MassTransit tables**: ~200 MB (rolling windows) - **Total**: ~1 GB/year + indexes (~2.5 GB total with 7-year retention) ### Marten Performance Optimizations **Snapshot Strategy**: - Snapshots created every 20 events (configurable) - Reduces event replay time for order aggregates - Snapshot overhead: ~10% storage increase - Performance gain: 80% faster aggregate loading for long streams **Projection Performance**: - Inline projections: Updated in same transaction (slower writes, fast reads) - Async projections: Updated by background process (fast writes, eventual consistency) - Current configuration: Inline for OrderSummaryView (strong consistency) **Connection Pooling**: - PgBouncer recommended for connection management - Default pool size: 100 connections - Typical active: 20-30 connections during peak load ## Data Lifecycle & Retention ### Retention Policy - **orders**: 7 years (regulatory and business compliance) - **order_items**: 7 years (tied to parent order) - **buyers**: Indefinite (customer master data) - **mt_events**: 7 years (complete audit trail) - **mt_streams**: 7 years (tied to events) - **inbox_state**: 30 days (deduplication window) - **outbox_message**: 7 days (after successful delivery) - **outbox_state**: Indefinite (small metadata) ### Archival Strategy **Hot Storage** (Active Database): - Active orders (New status) - Recent orders (last 90 days) - Active event streams - Current inbox/outbox records **Warm Storage** (Archive Database or Partition): - Completed/Cancelled orders (90 days - 2 years) - Associated event streams - Queryable for customer service and analytics **Cold Storage** (Data Lake): - Historical orders (2+ years) - Complete event history for compliance - Compressed and immutable archives - Rarely accessed, batch queries only ### Cleanup Jobs **Automated Maintenance**: - **Daily**: Purge old outbox_message records (sent more than 7 days ago) - **Weekly**: Purge old inbox_state records (consumed more than 30 days ago) - **Monthly**: Archive old orders and events (older than 2 years) to warm storage - **Quarterly**: Vacuum and analyze tables for optimal query planning - **Annual**: Archive to cold storage (older than 7 years, per retention policy) ### Soft Delete Management **Soft Delete Strategy**: - Orders marked is_deleted=true instead of physical deletion - Preserves referential integrity - Enables "undelete" functionality if needed - Audit trail remains intact **Cleanup Process**: - Soft-deleted orders retained for 90 days - After 90 days, eligible for archival - Physical deletion only after retention period expires ## Monitoring & Observability ### Key Performance Indicators **Order Metrics**: - **Order Volume**: Total orders created per day/hour - **Completion Rate**: Percentage of orders reaching Completed status - **Cancellation Rate**: Percentage of orders cancelled before completion - **Average Order Value**: Mean total price across all orders - **Order Processing Time**: Time from New to Completed status **Event Store Metrics**: - **Event Append Rate**: Events written per second - **Stream Size Distribution**: Number of events per aggregate - **Projection Lag**: Time delay between event append and projection update - **Event Replay Performance**: Time to reconstruct aggregate from events **Database Metrics**: - **Query Performance**: p95/p99 latency for common queries - **Connection Pool Utilization**: Active vs. available connections - **Table Sizes**: Growth rate monitoring for capacity planning - **Index Usage**: Identify unused or under-utilized indexes - **Disk Usage**: Current vs. allocated storage **Integration Metrics**: - **Outbox Backlog**: Count of unsent messages - **Inbox Duplicate Rate**: Percentage of rejected duplicate messages - **Event Publishing Latency**: Time from outbox insert to broker publish ### Health Checks **Database Connectivity**: - Verify PostgreSQL is reachable and accepting connections - Check connection pool availability **Event Store Health**: - Validate mt_events table is writable - Check for event append failures or conflicts - Monitor projection processing lag **Data Consistency**: - Verify orders table and mt_events table are synchronized - Check for orphaned order_items (missing parent order) - Validate projection data matches event stream **Outbox Processing**: - Alert if outbox backlog exceeds 1,000 messages - Monitor for stalled outbox workers (no messages sent in 15 minutes) ### Logging & Tracing **Structured Logging**: - Order creation/completion/cancellation logged with order ID - Event appends logged with stream ID and event type - Domain events include correlation IDs for distributed tracing - Database errors logged with query context **Distributed Tracing**: - OpenTelemetry spans for order operations - Correlation IDs link commands, events, and integration events - End-to-end tracing from HTTP request to message broker publish **Event Store Audit**: - Complete immutable history in mt_events - Every domain event captured with timestamp and metadata - Temporal queries enable point-in-time reconstruction ## Security & Compliance ### Data Classification - **Classification**: Internal - Contains customer PII and financial transaction data - **Access Mode**: Read/Write - Ordering Service only - **Residency**: East Asia region - Data sovereignty compliance - **Authoritative**: True - Single source of truth for order data ### Security Measures **Access Control**: - Database user with least-privilege permissions - No direct external access (internal microservices only) - Service-level authentication via connection strings - Row-level security not required (single-tenant per service) **Data Protection**: - Encryption at rest (Azure PostgreSQL managed encryption) - Encryption in transit (TLS 1.2+) - Customer PII limited to buyer name and address - No sensitive payment data stored (handled by Finance Service) **Event Store Security**: - Immutable event log prevents tampering - Append-only access pattern (events never modified) - Cryptographic hashing for event integrity (optional) - Access logs for audit compliance **Audit Trail**: - Complete order history in event store - Every state change captured as domain event - Correlation IDs link related events across services - Immutable audit trail for compliance and disputes ### Compliance Considerations **Financial Regulations**: - 7-year retention for order financial records - Complete audit trail via event sourcing - Transaction history reconstructable from events **GDPR / Privacy**: - Customer names and addresses stored (PII) - Right to access: Query orders and events by buyer ID - Right to erasure: Anonymize buyer data while retaining order metadata - Right to portability: Export order history in machine-readable format **Data Retention**: - 7-year retention for business and regulatory requirements - Automatic archival after 2 years to warm storage - Secure deletion after retention period expires ## Disaster Recovery & High Availability ### Backup Strategy **Automated Backups** (Azure PostgreSQL Flexible Server): - **Point-in-Time Recovery (PITR)**: 30-day retention - **Backup Frequency**: Continuous transaction log streaming - **Recovery Objectives**: RPO less than 5 minutes, RTO less than 1 hour - **Geo-Redundancy**: Backups replicated to secondary region **Event Store Backups**: - mt_events and mt_streams backed up together - Immutable event log ideal for incremental backups - Event replay capability provides additional resilience **Manual Snapshots**: - Pre-deployment snapshots before schema migrations - Retained for 90 days - Used for rollback scenarios ### High Availability **Azure PostgreSQL HA Configuration**: - **Availability Zone Redundancy**: Synchronous replica in different AZ - **Automatic Failover**: Sub-60-second failover time - **Read Replicas**: Optional for reporting and analytics queries - **Connection Pooling**: PgBouncer for efficient connection management **Marten Resilience**: - Event append idempotency prevents duplicate events - Optimistic concurrency prevents conflicting updates - Event replay enables recovery from projection failures ### Disaster Recovery Procedures **Failure Scenarios**: 1. **Database Connection Failure**: - Connection pool retry with exponential backoff - Circuit breaker trips after 5 consecutive failures - Graceful degradation: Return 503 Service Unavailable 2. **Event Store Corruption**: - Restore from PITR to point before corruption - Rebuild projections from restored event store - Validate consistency with downstream services 3. **Projection Failure**: - Failed events moved to mt_doc_deadletterevent - Fix projection logic - Replay dead letter events or rebuild projection from scratch 4. **Complete Database Loss**: - Failover to geo-redundant replica (RTO approximately 1 hour) - Restore from latest backup snapshot - Replay unacknowledged messages from message broker **Recovery Testing**: - Quarterly disaster recovery drills - Automated backup restoration tests - Event replay validation - Chaos engineering experiments (random failures) ## Integration with Marten ### Event Sourcing Configuration **Stream Management**: - One stream per order aggregate (stream_id matches order.id) - Events appended with optimistic concurrency (version checking) - Snapshots generated every 20 events for performance - Soft-deleted streams marked but events retained **Event Serialization**: - Events stored as JSONB for efficient querying - System.Text.Json for serialization - .NET type information stored for deserialization - Forward-compatible schema evolution **Projection Configuration**: - OrderSummaryView projection updated inline (same transaction) - Custom projections can be added for specific read models - Projection state tracked in mt_event_progression - Failed events captured in mt_doc_deadletterevent ### Domain Event Publishing **Event Flow**: 1. Domain event registered in aggregate root 2. Aggregate persisted via Marten (events appended to mt_events) 3. Marten's event subscription detects new events 4. Events transformed to integration events 5. Integration events published to outbox_message 6. Background worker publishes to RabbitMQ **Integration Event Mapping**: - OrderPlacedEvent maps to UserCheckedOutEvent - OrderCompletedEvent maps to OrderStatusChangedToCompleteEvent - OrderCancelledEvent maps to OrderStatusChangedToCancelEvent ### Temporal Queries **Point-in-Time Queries**: - Query order state at specific timestamp - Replay events up to target time - Useful for dispute resolution and debugging **Aggregate History**: - Retrieve complete event history for order - Visualize order lifecycle and state changes - Audit trail for compliance and investigations --- id: RatingDatabase name: Rating Database version: 1.0.0 container_type: database technology: postgres@18 authoritative: true access_mode: readWrite classification: internal retention: 2y residency: eastasia summary: Primary database for rating information. owners: - nhanxnguyen attachments: - url: https://drive.google.com/file/d/1NYJicm0yjl7o15nj2jLpggzr5prCXuAt/view?usp=sharing title: ERD of the Rating Database description: Learn more about the schema of the Rating Database type: "diagrams" icon: FileTextIcon --- ## Overview The Rating Database is a PostgreSQL 18-based storage system that captures and manages customer feedback for the BookWorm platform. This database serves as the foundation for the rating and review system, storing customer opinions, satisfaction scores, and detailed comments that help other shoppers make informed purchase decisions. By maintaining a comprehensive record of customer feedback, the system enables data-driven product improvements, quality monitoring, and customer satisfaction tracking across the entire book catalog. ## Purpose & Responsibility The Rating Database serves multiple critical business functions: - **Customer Voice**: Capturing authentic customer experiences and opinions - **Social Proof**: Providing credible feedback to help future customers make decisions - **Quality Monitoring**: Tracking satisfaction trends for continuous improvement - **Product Insights**: Identifying strengths and weaknesses in offerings - **Marketing Content**: Sourcing testimonials and success stories - **Customer Engagement**: Encouraging community participation and loyalty This database transforms customer opinions into actionable business intelligence, creating a feedback loop that drives product selection, customer service improvements, and overall platform quality. ### Feedbacks Table The core entity that represents individual customer feedback submissions. #### Column Breakdown **`id` (UUID, Primary Key)** - Unique identifier for each feedback entry - Generated using `uuidv7()` for time-ordered, globally unique IDs - Benefits: Chronologically sortable, distributed system friendly - Enables direct feedback linking and reference tracking - Example: `01914f5a-8c7e-7890-a123-456789abcdef` **`first_name` (VARCHAR(50), NOT NULL)** - Customer's first name for personalization - Required field to ensure accountability - Used for displaying reviewer identity (e.g., "John D.") - Helps build trust through authentic, attributable reviews - Indexed for customer lookup and analysis **`last_name` (VARCHAR(50), NOT NULL)** - Customer's last name for complete identification - Typically displayed with initial only for privacy (e.g., "Smith S.") - Combined with first_name for duplicate detection - Enables customer history tracking across multiple reviews - Part of reviewer identification in public displays **`comment` (VARCHAR(1000), NULLABLE)** - Detailed written feedback from the customer - Optional field allowing quick rating-only submissions - Maximum 1000 characters balances detail with readability - Contains valuable qualitative insights about products - Source for testimonials, issue identification, and sentiment analysis - NULL when customers provide rating without explanation **`rating` (INTEGER, NOT NULL)** - Numeric satisfaction score (typically 1-5 scale) - Required field representing quantitative satisfaction - Core metric for product quality assessment - Used for calculating averages and distributions - Enables filtering and sorting of products - Directly impacts product rankings and visibility **`created_at` (TIMESTAMP WITH TIME ZONE, NOT NULL)** - When the feedback was submitted - Stored with timezone for global customer base - Enables time-series analysis and trend tracking - Used for sorting (most recent first) - Helps identify seasonal satisfaction patterns - Critical for recency-weighted rating calculations **`last_modified_at` (TIMESTAMP WITH TIME ZONE, NULLABLE)** - Last time the feedback was updated or processed - Tracks moderation, response, or administrative actions - NULL for unprocessed feedback - Enables processing backlog monitoring - Used for calculating response time metrics - Supports audit trails and compliance requirements ## Rating Scale System The database supports a 5-star rating system, the industry standard for customer feedback: ### Star Rating Interpretation | Rating | Label | Customer Sentiment | Typical Usage | | ------ | --------- | --------------------------------------- | ------------------------------------ | | 5 | Excellent | Highly satisfied, exceeded expectations | Enthusiastic recommendations | | 4 | Good | Satisfied, met expectations | Positive but with minor reservations | | 3 | Average | Neutral, acceptable but unremarkable | Middle ground, lukewarm opinion | | 2 | Poor | Dissatisfied, below expectations | Significant issues noted | | 1 | Very Poor | Highly dissatisfied, unacceptable | Serious problems or disappointment | ### Rating Distribution Insights Typical healthy product distributions: - **5 Stars**: 40-50% (promoters, brand advocates) - **4 Stars**: 30-35% (satisfied customers) - **3 Stars**: 10-15% (neutral, fence-sitters) - **2 Stars**: 5-10% (disappointed but engaged) - **1 Star**: 5-10% (detractors, vocal critics) **Red Flags:** - Bimodal distribution (many 5s and 1s, few middle) suggests polarizing product - Excessive 5s without comments may indicate fake reviews - High concentration of 1-2 stars requires immediate investigation ## Key Features ### UUID v7 Benefits Using `uuidv7()` provides several advantages: - **Time-Ordered**: Feedbacks naturally sort by creation time - **Index Friendly**: Sequential nature improves B-tree performance - **Distributed Safe**: No coordination needed across service instances - **Globally Unique**: Safe for data merges and migrations - **Embedded Timestamp**: Can extract approximate creation time ### Comment Flexibility The optional comment field supports varied customer engagement levels: **Comment Present (65-75% typical):** - Detailed explanations of rating - Specific praise or criticism - Suggestions for improvement - Contextual information - Rich qualitative data **Rating Only (25-35% typical):** - Quick feedback from busy customers - Clear-cut experiences (obviously great or terrible) - Follow-up ratings from previous reviewers - Mobile users with limited time - Sufficient for quantitative analysis ### Temporal Tracking Both `created_at` and `last_modified_at` enable: **Time-Series Analysis:** - Daily, weekly, monthly satisfaction trends - Seasonal patterns identification - Impact of changes or updates - Response time monitoring **Recency Weighting:** - Recent feedback weighted more heavily - Identifies improving or declining quality - Reflects current product state - Deprecates outdated opinions over time ## Data Classification & Governance - **Classification**: Internal - Contains customer feedback and PII - **Access Mode**: Read/Write - Rating service has full control; Analytics has read-only - **Retention**: 2 years - Balances data utility with privacy regulations - **Residency**: East Asia region - Co-located with primary user base - **Authoritative**: True - Single source of truth for customer feedback ## Monitoring & Observability ### Key Performance Indicators **Database Health:** - Table size and growth rate - Query performance (p50, p95, p99 latency) - Index hit ratios - Connection pool utilization **Business Metrics:** - Daily feedback volume - Average rating trends - NPS score movement - Negative feedback rate - Comment engagement rate **Data Quality:** - Null comment ratio - Duplicate detection - Outlier identification - Processing backlog size ### Alerting Thresholds - Average rating drops below 3.5 for 7+ days - NPS score declines by more than 10 points - Negative feedback rate exceeds 20% - Unprocessed critical feedback (rating ≤ 2) older than 4 hours - Daily feedback volume drops by more than 50% (potential system issue) - Database size growth exceeds 30% month-over-month ## Security & Access Control ### Data Protection **Encryption:** - At rest: Transparent Data Encryption (TDE) - In transit: TLS 1.3 for all connections - Backups: Encrypted with AES-256 **Audit Logging:** - All data modifications logged - Access patterns monitored - Anomaly detection enabled - Compliance reporting automated **PII Handling:** - Minimize PII collection (name only, no email/phone) - Hash sensitive fields where possible - Support data export (GDPR Article 20) - Enable deletion (GDPR Article 17 - Right to be Forgotten) ## Backup & Disaster Recovery ### Backup Strategy **Automated Backups:** - Full backup: Daily at 02:00 UTC - Differential backup: Every 6 hours - Transaction log backup: Every 15 minutes - Retention: 30 days online, 2 years archived **Recovery Objectives:** - RPO (Recovery Point Objective): 15 minutes - RTO (Recovery Time Objective): 4 hours - Continuous transaction log archival - Cross-region backup replication ### High Availability **Replication:** - Primary database in East Asia region - Synchronous standby for automatic failover - Read replicas for analytics workload - Automatic failover in under 60 seconds **Disaster Recovery Testing:** - Monthly DR drill execution - Backup restoration verification - Failover testing procedures - Recovery playbook maintenance --- id: SchedulerDatabase name: Scheduler Database version: 1.0.0 container_type: database technology: postgres@18 authoritative: true access_mode: readWrite classification: internal retention: 2y residency: eastasia summary: Primary database for scheduling and job management. owners: - nhanxnguyen attachments: - url: https://drive.google.com/file/d/1NYJicm0yjl7o15nj2jLpggzr5prCXuAt/view?usp=sharing title: ERD of the Scheduler Database description: Learn more about the schema of the Scheduler Database type: "diagrams" icon: FileTextIcon --- ## Overview The Scheduler Database is a PostgreSQL 18-based persistent storage system that powers the job scheduling and task automation infrastructure for the BookWorm platform. This database manages both time-based (one-time and recurring) and cron-based scheduled tasks, ensuring reliable execution of background jobs, maintenance routines, batch processes, and automated workflows. By maintaining a comprehensive record of scheduled tasks, their execution history, and retry logic, the system provides a robust foundation for automated operations that keep the platform running smoothly 24/7. ## Purpose & Responsibility The Scheduler Database serves as the backbone for all automated and scheduled operations in the BookWorm ecosystem, enabling: - **Automated Task Execution**: Running background jobs at specified times or intervals - **Recurring Operations**: Managing periodic tasks like data cleanup, report generation, and cache warming - **Cron-Based Scheduling**: Supporting complex scheduling patterns using cron expressions - **Job Orchestration**: Coordinating batch jobs with dependencies and parent-child relationships - **Reliability & Resilience**: Handling failures with retry logic and exception tracking - **Execution Tracking**: Maintaining detailed history of job runs for audit and debugging - **Distributed Locking**: Preventing concurrent execution across multiple service instances - **Performance Monitoring**: Tracking execution times and job performance metrics This database transforms manual, time-sensitive operations into reliable, automated processes that improve operational efficiency and reduce human error. ## Schema Design The database employs a sophisticated schema designed around two primary scheduling paradigms: time-based triggers and cron-based patterns, all housed within a dedicated `ticker` schema for organizational clarity. ### Architecture Overview The scheduler system is built on two complementary scheduling models: **1. TimeTickers**: One-time or batch scheduled tasks - Execute at a specific point in time - Support batch operations with parent-child relationships - Ideal for one-off jobs or scheduled task chains - Examples: Order processing, report generation, data exports **2. CronTickers**: Recurring scheduled tasks based on cron expressions - Execute on repeating schedules (hourly, daily, weekly, etc.) - Generate CronTickerOccurrences for each scheduled run - Ideal for periodic maintenance and monitoring - Examples: Cache cleanup, analytics aggregation, health checks ### CronTickers Table Defines recurring scheduled tasks using cron expressions. #### Key Columns **`id` (UUID, Primary Key)** - Unique identifier for the cron schedule definition - Permanent reference across all occurrences - Links to CronTickerOccurrences for execution tracking **`expression` (TEXT)** - Cron expression defining the schedule pattern - Standard format: `minute hour day month day-of-week` - Examples: - `0 0 * * *` - Daily at midnight - `0 */2 * * *` - Every 2 hours - `0 9 * * MON-FRI` - Weekdays at 9 AM - `*/15 * * * *` - Every 15 minutes **`request` (BYTEA)** - Serialized request payload for the job - Binary format for efficient storage - Contains job parameters and configuration - Deserialized when creating occurrences **`retries` (INTEGER, NOT NULL)** - Maximum number of retry attempts on failure - Configurable per job type - Typical values: 3-5 retries for critical jobs - 0 for non-critical or idempotent operations **`retry_intervals` (INTEGER[])** - Array of wait times (in seconds) between retries - Example: `{60, 300, 900}` - wait 1min, 5min, 15min - Exponential backoff patterns common - Allows flexible retry strategies per job **`function` (TEXT)** - Name or identifier of the function to execute - Maps to actual code handlers in the scheduler service - Examples: `CleanupExpiredSessions`, `GenerateDailyReport`, `SyncInventory` **`description` (TEXT)** - Human-readable description of the job's purpose - Helps operators understand what the job does - Documentation embedded in the data - Examples: "Clean up expired shopping carts", "Generate daily sales report" **`init_identifier` (TEXT)** - Unique identifier for job initialization - Prevents duplicate job registration - Used during system startup to ensure idempotency - Typically matches configuration keys **`created_at` / `updated_at` (TIMESTAMP WITH TIME ZONE, NOT NULL)** - Audit trail for schedule lifecycle - Track when schedules are created or modified - Useful for troubleshooting and compliance - Timezone-aware for global operations ### CronTickerOccurrences Table Represents individual executions of cron-based schedules. #### Key Columns **`id` (UUID, Primary Key)** - Unique identifier for this specific occurrence - Links to execution logs and metrics - Used for distributed locking **`cron_ticker_id` (UUID, Foreign Key, NOT NULL)** - References the parent CronTicker definition - Establishes schedule-to-execution relationship - Cascade delete ensures cleanup - Indexed for efficient occurrence lookups **`status` (INTEGER, NOT NULL)** - Current state of the occurrence - Typical values: - 0: Pending (scheduled but not started) - 1: Locked (being executed) - 2: Completed (successful execution) - 3: Failed (execution failed) - 4: Cancelled (manually cancelled) **`lock_holder` (TEXT)** - Identifier of the service instance holding the execution lock - Prevents concurrent execution in distributed systems - Typically hostname or pod ID - NULL when not locked **`execution_time` (TIMESTAMP WITH TIME ZONE, NOT NULL)** - Scheduled time for this occurrence - Generated from cron expression - Used for determining when to run - May differ from actual executed_at **`locked_at` (TIMESTAMP WITH TIME ZONE)** - When the occurrence was locked for execution - Helps detect stale locks - Used for lock timeout calculations - NULL if never locked **`executed_at` (TIMESTAMP WITH TIME ZONE)** - Actual time when execution completed - Used for performance tracking - Differs from execution_time in case of delays - NULL if not yet executed **`exception` (TEXT)** - Error message if execution failed - Full stack trace for debugging - NULL on successful execution - Critical for troubleshooting failures **`elapsed_time` (BIGINT, NOT NULL)** - Execution duration in milliseconds - Performance metric for monitoring - Used for identifying slow jobs - 0 if not yet executed **`retry_count` (INTEGER, NOT NULL)** - Number of retry attempts made - Increments with each failed retry - Compared against CronTicker.retries limit - Resets to 0 on successful execution ### TimeTickers Table Manages one-time and batch scheduled tasks. #### Key Columns **`id` (UUID, Primary Key)** - Unique identifier for the scheduled task - Used for tracking and locking - Reference for batch parent-child relationships **`status` (INTEGER, NOT NULL)** - Current execution state (same values as CronTickerOccurrences) - Tracks lifecycle from pending to completed/failed **`lock_holder` (TEXT)** - Service instance executing this task - Distributed lock mechanism - Prevents concurrent execution **`request` (BYTEA)** - Serialized job payload - Contains all parameters needed for execution - Binary format for efficient storage **`execution_time` (TIMESTAMP WITH TIME ZONE, NOT NULL)** - When this task should execute - One-time execution timestamp - Jobs picked up when NOW() >= execution_time **`locked_at` / `executed_at` (TIMESTAMP WITH TIME ZONE)** - Lock acquisition and completion timestamps - Used for lock timeout detection - Performance monitoring **`exception` (TEXT)** - Error details if execution failed - Full stack trace for debugging - NULL on success **`elapsed_time` (BIGINT, NOT NULL)** - Execution duration in milliseconds - Performance tracking - SLA monitoring **`retries` (INTEGER, NOT NULL)** - Maximum retry attempts allowed - Configurable per task **`retry_count` (INTEGER, NOT NULL)** - Current retry attempt number - Increments on failure - Task abandoned when retry_count >= retries **`retry_intervals` (INTEGER[])** - Wait times between retries (seconds) - Exponential backoff support - Example: `{30, 60, 300}` - 30s, 1m, 5m **`batch_parent` (UUID, Foreign Key)** - References another TimeTicker as parent - Creates task hierarchies - Enables dependent job execution - NULL for standalone tasks **`batch_run_condition` (INTEGER)** - Defines execution condition in batch: - 0: Run only if parent succeeds - 1: Run regardless of parent status - 2: Run only if parent fails - Enables sophisticated workflow orchestration **`function` (TEXT)** - Handler function name - Maps to actual code implementation - Example: `ProcessOrder`, `GenerateInvoice` **`description` (TEXT)** - Human-readable task description - Operational documentation - Helps understand job purpose **`init_identifier` (TEXT)** - Unique initialization key - Prevents duplicate task creation - Idempotency guarantee **`created_at` / `updated_at` (TIMESTAMP WITH TIME ZONE, NOT NULL)** - Task lifecycle tracking - Audit trail - Troubleshooting aid ## Key Features ### Distributed Locking The database implements pessimistic locking to prevent concurrent execution: **Lock Acquisition:** ```sql UPDATE ticker."TimeTickers" SET status = 1, -- Locked lock_holder = 'service-instance-123', locked_at = NOW() WHERE id = $1 AND status = 0 -- Pending AND execution_time <= NOW() AND (lock_holder IS NULL OR locked_at < NOW() - INTERVAL '5 minutes') RETURNING *; ``` **Benefits:** - Prevents duplicate execution in multi-instance deployments - Handles crashed instances via lock timeout - No external coordination service needed - Database-native locking mechanism ### Retry Logic with Exponential Backoff Failed jobs automatically retry with configurable intervals: **Retry Strategy:** ``` Attempt 1: Immediate execution Attempt 2: Wait 60 seconds (1 minute) Attempt 3: Wait 300 seconds (5 minutes) Attempt 4: Wait 900 seconds (15 minutes) Final: Mark as failed, alert operations team ``` **Implementation:** - `retry_count` tracks current attempt - `retry_intervals` array defines wait times - Next execution_time = NOW() + retry_intervals[retry_count] - Automatic abandonment after max retries ### Batch Job Orchestration TimeTickers support parent-child relationships for complex workflows: **Example Workflow:** ``` Parent: GenerateMonthlyReport ├── Child 1: ExtractSalesData (run if parent succeeds) ├── Child 2: ExtractInventoryData (run if parent succeeds) ├── Child 3: CompileReport (run if all children succeed) └── Child 4: SendFailureAlert (run only if parent fails) ``` **Batch Conditions:** - Sequential execution based on parent status - Parallel execution for independent children - Conditional execution based on outcomes - Dependency resolution and workflow orchestration ### Cron Expression Scheduling CronTickers support standard cron syntax: **Common Patterns:** ``` */5 * * * * - Every 5 minutes 0 * * * * - Every hour at minute 0 0 0 * * * - Daily at midnight 0 2 * * * - Daily at 2 AM 0 9 * * MON-FRI - Weekdays at 9 AM 0 0 1 * * - Monthly on the 1st at midnight 0 0 * * SUN - Every Sunday at midnight 0 */6 * * * - Every 6 hours */15 9-17 * * MON-FRI - Every 15 minutes during business hours ``` ### Execution History & Audit Trail Complete tracking of all job executions: **Tracked Metrics:** - Execution time vs scheduled time (latency) - Duration of execution (performance) - Success/failure outcomes - Error messages and stack traces - Retry attempts and outcomes - Lock holder information ## Data Classification & Governance - **Classification**: Internal - Operational data for system automation - **Access Mode**: Read/Write - Scheduler service has full control; Monitoring has read-only - **Retention**: 2 years - Balances audit requirements with storage costs - **Residency**: East Asia region - Co-located with primary services - **Authoritative**: True - Single source of truth for scheduled tasks ### Data Retention Policy **Active Jobs:** - CronTickers: Retained indefinitely while active - TimeTickers: Retained for 90 days after completion - Occurrences: Keep last 1000 per CronTicker **Historical Data:** - Archive completed occurrences older than 90 days - Compress and move to cold storage - Maintain summary statistics for long-term trends - Failed jobs retained longer for root cause analysis ## Integration Points The Scheduler Database integrates with: - **Scheduler Service**: Primary consumer for job management - **Catalog Service**: Scheduled product synchronization and cache updates - **Finance Service**: Periodic invoice generation and payment processing - **Notification Service**: Scheduled notification campaigns - **Analytics Service**: Regular data aggregation and report generation - **Monitoring Service**: Performance metrics and health checks - **Message Bus**: Publishes job completion events ## Use Cases ### Operational Automation **Data Maintenance:** - Clean up expired sessions (every hour) - Archive old orders (daily at 2 AM) - Vacuum database tables (weekly) - Rotate log files (daily at midnight) **Cache Management:** - Warm popular product caches (every 30 minutes) - Invalidate stale data (hourly) - Pre-generate search indexes (every 6 hours) ### Business Process Automation **Financial Operations:** - Generate daily sales reports (daily at 9 AM) - Process pending payments (every 15 minutes) - Calculate commission (monthly on 1st) - Send payment reminders (daily at 10 AM) **Customer Engagement:** - Send abandoned cart emails (hourly) - Weekly newsletter (Mondays at 8 AM) - Birthday wishes (daily at 9 AM) - Review request emails (3 days after delivery) ### System Monitoring **Health Checks:** - Database connectivity tests (every 5 minutes) - API endpoint health checks (every minute) - Certificate expiration monitoring (daily) - Disk space monitoring (every 15 minutes) **Analytics Processing:** - Aggregate hourly metrics (hourly at minute 5) - Calculate daily statistics (daily at 1 AM) - Generate weekly reports (Mondays at 7 AM) - Update search indexes (every 30 minutes) ## Monitoring & Observability ### Key Metrics **Job Execution Metrics:** - Jobs executed per hour/day - Average execution time - Success vs failure rate - Retry rate - Jobs pending execution **Performance Metrics:** - Scheduled time vs actual execution time (latency) - Lock contention incidents - Stale lock detection count - Database query performance **Reliability Metrics:** - Failed jobs requiring attention - Jobs abandoned after max retries - Exception rate by job type - SLA compliance (on-time execution rate) --- id: "CreateOrder" name: "User Creates Order" version: "1.0.0" summary: "End-to-end flow for creating and processing customer orders in BookWorm" badges: - content: Order Lifecycle textColor: green backgroundColor: green icon: ShoppingCartIcon - content: Checkout textColor: blue backgroundColor: blue icon: CreditCardIcon owners: - full-stack - nhanxnguyen steps: - id: "create_order_initiated" title: "User Creates Order" summary: "User creates an order" actor: name: "User" next_step: id: "create_order_request" label: "Initiate order" - id: "create_order_request" title: "Make Order Request" message: id: "CreateOrderCommand" version: "1.0.0" next_step: id: "ordering_service" label: "Proceed to checkout" - id: "ordering_service" title: "Ordering Service" service: id: "OrderingService" version: "1.0.0" next_steps: - id: "user_checked_out_event" label: "Publish event" - id: "user_checked_out_event" title: "Publish User Checked Out Event" message: id: "UserCheckedOutEvent" version: "1.0.0" next_step: id: "finance_service" label: "Subscribe event" - id: "finance_service" title: "Finance Service" service: id: "FinanceService" version: "1.0.0" next_steps: - id: "place_order_command" label: "Publish event" - id: "send_basked_delete_failed" label: "Publish event when subscribe basked deleted failed" - id: "send_basked_delete_complete" label: "Publish event when subscribe basked deleted complete" - id: "place_order_command" title: "Reverse Basket" message: id: "PlaceOrderCommand" version: "1.0.0" next_step: id: "basket_service" label: "Subscribe event" - id: "basket_service" title: "Basket Service" service: id: "BasketService" version: "1.0.0" next_steps: - id: "check_status" label: "Publish event" - id: "check_status" title: "Check Basket Reverse Status" custom: title: "Check Basket Reverse Status" color: "purple" icon: "CheckCircleIcon" type: "Decision" height: 6 summary: "Check if the basket is deleted" next_steps: - id: "basket_deleted_failed" label: "Publish event when failed" - id: "basket_deleted_complete" label: "Publish event when complete" - id: "basket_deleted_failed" title: "Publish Basket Deleted Failed Event" message: id: "BasketDeletedFailedEvent" version: "1.0.0" next_step: id: "finance_service" label: "Subscribe event" - id: "basket_deleted_complete" title: "Publish Basket Deleted Complete Event" message: id: "BasketDeletedCompletedEvent" version: "1.0.0" next_step: id: "finance_service" label: "Subscribe event" - id: "send_basked_delete_failed" title: "Publish Basket Delete Command Failed" message: id: "DeleteBasketFailedCommand" version: "1.0.0" next_step: id: "ordering_service" label: "Subscribe event & Rollback order" - id: "send_basked_delete_complete" title: "Publish Basket Complete Command Failed" message: id: "DeleteBasketCompleteCommand" version: "1.0.0" next_steps: - id: "ordering_service" label: "Subscribe event" - id: "notification_service" label: "Subscribe event" - id: "notification_service" title: "Notification Service" service: id: "NotificationService" version: "1.0.0" --- ## Overview The Create Order flow represents the core business process of BookWorm, handling the complete journey from customer checkout to order fulfillment. This flow implements the **Saga pattern** to ensure data consistency across distributed services. ## 🎯 Business Context ### Objectives 1. **Seamless Checkout**: Minimize friction in the purchase process 2. **Payment Security**: Ensure PCI compliance and fraud prevention 3. **Inventory Accuracy**: Prevent overselling through proper reservation 4. **Customer Communication**: Keep customers informed throughout ### Success Criteria - ✅ Order creation within 5 seconds - ✅ 99.9% success rate for valid orders - ✅ Zero duplicate charges - ✅ Confirmation email within 1 minute ## 🔄 Detailed Flow ## 📋 Business Rules ### Order Validation Rules | Rule | Description | Action on Violation | | ----------------------- | ----------------------------- | -------------------------- | | **Minimum Order Value** | Orders must be >= $10 | Reject with error message | | **Maximum Items** | Max 50 unique items per order | Split into multiple orders | | **Stock Availability** | All items must be in stock | Suggest alternatives | | **Payment Method** | Must have valid payment | Redirect to payment setup | | **Shipping Address** | Must be deliverable region | Show supported regions | ### Compensation Rules When failures occur, the system automatically compensates: 1. **Payment Failed**: - Release inventory reservations - Restore basket contents - Notify customer 2. **Basket Clear Failed**: - Refund payment - Cancel order - Alert support team 3. **Notification Failed**: - Retry with backoff - Log for manual follow-up - Order still completes ## 📈 SLA Requirements ### Service Level Agreement | Component | Availability | Response Time | Error Rate | | -------------------- | ------------ | ------------- | ---------- | | **API Gateway** | 99.99% | < 50ms | < 0.01% | | **Ordering Service** | 99.9% | < 200ms | < 0.1% | | **Finance Service** | 99.95% | < 500ms | < 0.05% | | **Basket Service** | 99.9% | < 100ms | < 0.1% | | **Overall Flow** | 99.9% | < 5s | < 0.1% |