---
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% |