A production-grade .NET 8 Web API built with Clean Architecture principles for product catalog management and order processing with concurrency-safe stock management.
- Product Catalog Management: Full CRUD operations for products
- Order Processing: Place orders with multiple products
- Stock Management: Concurrency-safe stock updates to prevent overselling
- Clean Architecture: Separation of concerns with Domain, Application, Infrastructure, and API layers
- Database Transactions: Atomic operations for data integrity
- API Documentation: Swagger/OpenAPI integration
- Docker Support: Containerized application with PostgreSQL
- .NET 8 - Latest LTS version
- ASP.NET Core Web API - Web API framework
- Entity Framework Core 8 - ORM with PostgreSQL provider
- PostgreSQL - Relational database
- MediatR - CQRS pattern implementation
- FluentValidation - Input validation
- Docker - Containerization
- Swagger/OpenAPI - API documentation
The solution follows Clean Architecture principles with the following layers:
stackbuld/
├── src/ # Source code
│ ├── Stackbuld.ProductOrdering.Domain/ # Domain entities and business logic
│ ├── Stackbuld.ProductOrdering.Application/ # Use cases, DTOs, and interfaces
│ ├── Stackbuld.ProductOrdering.Infrastructure/ # Data access and external services
│ └── Stackbuld.ProductOrdering.Api/ # Web API controllers and configuration
├── tests/ # Unit and integration tests
├── docker-compose.yml # Development environment
├── docker-compose.prod.yml # Production environment
├── docker-compose.override.yml # Local development overrides
├── Dockerfile # API container
├── Dockerfile.migrations # Database migrations container
├── env.example # Environment variables template
├── .gitignore # Git ignore rules
└── README.md # This file
- Entities: Product, Order, OrderItem
- Exceptions: Custom business exceptions
- Business Logic: Stock management, order validation
- Commands/Queries: CQRS pattern with MediatR
- DTOs: Data transfer objects
- Interfaces: Repository contracts
- Validators: FluentValidation rules
- DbContext: Entity Framework configuration
- Repositories: Data access implementations
- Unit of Work: Transaction management
- Controllers: RESTful endpoints
- Filters: Global exception handling
- Configuration: Dependency injection setup
- .NET 8 SDK
- Docker and Docker Compose
- PostgreSQL (if running locally without Docker)
- Make (optional, for convenience commands)
-
Clone the repository
git clone <repository-url> cd stackbuld
-
Quick setup (using Make)
make setup
Or manual setup:
# Copy environment file cp env.example .env # Start services docker-compose up -d # Run migrations docker-compose up migrations
-
Access the API
- API: http://localhost:5001
- Swagger UI: http://localhost:5001/swagger
- pgAdmin: http://localhost:5050 (admin@stackbuld.com / admin123)
# Start development environment
make dev
# View logs
make logs-api
# Check health
make health
# Stop services
make docker-stop
# See all available commands
make help-
Start PostgreSQL
# Using Docker docker run --name postgres -e POSTGRES_PASSWORD=postgres -p 5432:5432 -d postgres:15-alpine -
Update connection string in
appsettings.Development.json -
Restore packages
dotnet restore
-
Run database migrations
cd src/Stackbuld.ProductOrdering.Api dotnet ef database update -
Run the application
dotnet run
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/products |
Get all products |
| GET | /api/products/{id} |
Get product by ID |
| POST | /api/products |
Create new product |
| PUT | /api/products/{id} |
Update product |
| DELETE | /api/products/{id} |
Delete product |
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/orders |
Get all orders |
| GET | /api/orders/{id} |
Get order by ID |
| POST | /api/orders |
Place new order |
The API implements several mechanisms to prevent overselling:
- Database Transactions: All order operations are wrapped in transactions
- Atomic Stock Updates: Uses raw SQL for atomic stock quantity updates
- Optimistic Concurrency: Stock validation before and during order processing
- Rollback on Failure: Automatic rollback if any product has insufficient stock
UPDATE "Products"
SET "StockQuantity" = "StockQuantity" - @quantity, "UpdatedAt" = @timestamp
WHERE "Id" = @productId AND "StockQuantity" >= @quantitycurl -X POST "http://localhost:5000/api/products" \
-H "Content-Type: application/json" \
-d '{
"name": "Laptop",
"description": "High-performance laptop",
"price": 999.99,
"stockQuantity": 10
}'curl -X POST "http://localhost:5000/api/orders" \
-H "Content-Type: application/json" \
-d '{
"items": [
{
"productId": "product-guid-here",
"quantity": 2
}
]
}'dotnet testThe solution includes a concurrency test that simulates multiple orders competing for the same stock to verify the system prevents overselling.
The docker-compose.yml includes:
- PostgreSQL 15: Database service
- API Service: .NET 8 Web API
- Health Checks: Database connectivity verification
- Volume Persistence: Database data persistence
- Single Database: All operations use a single PostgreSQL database
- No Authentication: API endpoints are publicly accessible (for demo purposes)
- Synchronous Processing: Orders are processed synchronously
- No Caching: No caching layer implemented
- No Logging: Basic logging configuration only
ASPNETCORE_ENVIRONMENT: Development/ProductionConnectionStrings__DefaultConnection: Database connection string
- Provider: PostgreSQL
- Migrations: Entity Framework Core migrations
- Connection Pooling: Default EF Core connection pooling
This project is licensed under the MIT License.
- Fork the repository
- Create a feature branch
- Commit your changes
- Push to the branch
- Create a Pull Request
For questions or support, please open an issue in the repository.