A reusable, client-agnostic inventory management platform built on ASP.NET Core 8. Designed to serve multiple business domains through arbitrary product metadata, dynamic category hierarchies, and a full inventory transaction ledger with undo support.
- .NET 8 SDK or later
No database installation required. SQLite is embedded and the database file is created automatically on first run.
# Clone and navigate to the API project
cd src/FlexStock.WebApi
# Run — database is created and seeded automatically
dotnet run
# Open Swagger UI
# http://localhost:5000/swaggerThe first run seeds a small demo dataset so the API is immediately explorable.
FlexStock/
├── src/
│ ├── FlexStock.Domain/ # Entities, enums, domain exceptions
│ ├── FlexStock.Application/ # Use cases (CQRS handlers via MediatR)
│ ├── FlexStock.Infrastructure/ # EF Core + SQLite, repository implementations
│ └── FlexStock.WebApi/ # ASP.NET Core controllers, Program.cs
└── tests/
├── FlexStock.Application.Tests/
└── FlexStock.Infrastructure.Tests/
Full interactive documentation is available at /swagger when the server is running.
| Method | Endpoint | Description |
|---|---|---|
POST |
/api/products |
Create a product with metadata and categories |
GET |
/api/products/search |
Search products by metadata and/or category |
GET |
/api/products/{id} |
Get a single product by ID |
Create a product:
curl -X POST http://localhost:5000/api/products \
-H "Content-Type: application/json" \
-d '{
"name": "iPhone 15 Pro",
"metadata": {
"brand": "Apple",
"color": "Titanium",
"sku": "APPL-IP15P-256-TI"
},
"categories": [
"Electronics/Phones/Smartphones",
"Apple/iPhones"
]
}'Search products:
# By metadata (multiple filters = AND)
GET /api/products/search?metadata.brand=Apple&metadata.color=Titanium
# By category (prefix match — returns product and all sub-categories)
GET /api/products/search?category=Electronics/Phones
# Combined
GET /api/products/search?metadata.brand=Apple&category=Electronics/Phones| Method | Endpoint | Description |
|---|---|---|
POST |
/api/inventory/transactions |
Add or remove inventory (single or batch) |
DELETE |
/api/inventory/transactions/{id} |
Void (undo) a transaction |
GET |
/api/inventory/{productId}/count |
Get current stock for a product |
GET |
/api/inventory/count |
Get stock across products filtered by metadata |
GET |
/api/inventory/transactions |
List transactions (filterable) |
Adjust inventory (batch-capable):
curl -X POST http://localhost:5000/api/inventory/transactions \
-H "Content-Type: application/json" \
-d '{
"adjustments": [
{ "productId": "3fa85f64-...", "type": "Add", "quantity": 100 },
{ "productId": "9bc12a44-...", "type": "Remove", "quantity": 10, "notes": "Sold" }
]
}'Void a transaction (undo):
curl -X DELETE http://localhost:5000/api/inventory/transactions/7d1e4c2a-...Get inventory count by metadata:
GET /api/inventory/count?metadata.brand=Apple# All tests
dotnet test
# Specific project
dotnet test tests/FlexStock.Application.Tests
dotnet test tests/FlexStock.Infrastructure.Testssrc/FlexStock.WebApi/appsettings.json:
{
"ConnectionStrings": {
"DefaultConnection": "Data Source=flexstock.db"
},
"SeedDemoData": true
}Change DefaultConnection to any valid SQLite path. Set SeedDemoData to false to start with an empty database.
See ARCHITECTURE.md for full design documentation including:
- Architecture Decision Records (ADRs)
- Entity Relationship Diagram
- Full API contract with request/response examples
- Data model rationale
- Trade-off analysis