A full-stack product catalog app with a .NET 8 Web API and Angular 18. Focused on clean architecture, clear trade-offs, and a working search endpoint.
- .NET 8 SDK
- Node.js 20.x
- Docker Desktop
- Angular CLI 18 (
npm install -g @angular/cli@18)
cp .env.example .env
docker-compose up -d- Frontend: http://localhost:4200
- API Swagger: http://localhost:5000/swagger/index.html
1) Start SQL Server
# Replace <YourPassword> with a strong password (8+ chars, upper/lower/digit/symbol)
docker run -e "ACCEPT_EULA=Y" -e "MSSQL_SA_PASSWORD=<YourPassword>" \
-p 1433:1433 --name productcatalog-sqlserver \
-d mcr.microsoft.com/mssql/server:2022-CU15-ubuntu-22.042) Configure the API
cd ProductCatalog/src/ProductCatalog.Api
cp appsettings.Development.json.example appsettings.Development.json3) Run the API
dotnet run --urls "http://localhost:5000"4) Run the Frontend
cd ProductCatalogUI
npm install
ng serve- Clean Architecture: Api (presentation), Core (domain), Infrastructure (data/services)
- Dependency flow: Api -> Core <- Infrastructure
- Thin controllers, service layer for business logic, repositories for data access
- Products: Id, Name, Description, Price, CategoryId, StockQuantity, CreatedDate, IsActive
- Categories: Id, Name, Description, IsActive
- Backend: .NET 8
- ORM: EF Core 8
- Database: SQL Server 2022
- Frontend: Angular 18
- Containerization: Docker
- Controllers only handle HTTP concerns.
- Services orchestrate business logic.
- Repositories isolate data access.
- Interfaces live in Core; implementations in Infrastructure.
- AsNoTracking for read operations.
- Projection to DTOs to avoid over-fetching and N+1 queries.
- Search uses raw SQL for a true single-query implementation.
- Search is the most practical for the UI.
- Raw SQL with CTE + UNION ALL returns TotalCount even when a page is empty.
- LIKE escaping for %, _, [, \ to treat them literally.
- Stable pagination with secondary sort by Id.
- Chosen for testability and separation of concerns.
- Trade-off: extra abstraction layer.
- IX_Product_CategoryId: category filtering / FK joins
- IX_Product_IsActive: active-only filtering
- IX_Product_Price: price range filtering
- IX_Product_Name: default sort and search
- IX_Product_CreatedDate: date sorting
Products
- GET /api/products
- GET /api/products/{id}
- GET /api/products/search
- POST /api/products
- PUT /api/products/{id}
- DELETE /api/products/{id}
Categories
- GET /api/categories
- POST /api/categories
Health
- GET /health/live
- GET /health/ready
# All tests
cd ProductCatalog
dotnet test
# Unit tests only
dotnet test tests/ProductCatalog.Tests
# Integration tests only (requires Docker)
dotnet test tests/ProductCatalog.Tests.Integration- Add auth/authorization (JWT + roles).
- Improve database resilience (retry policies + read replicas).
- Single-tenant app with moderate data size (not millions of rows).
- Soft delete preferred for auditability.
- Raw SQL used only for search to satisfy the single-query requirement.
- Seeding runs only in Development to avoid production startup issues.
Angular 18 app provides a product list view with basic routing and error handling. See ProductCatalogUI/ for details.