Book Store
Full-stack online book store application with event-sourced backend API and Blazor frontend, orchestrated by .NET Aspire.
Overview
A complete book store management system featuring:
- Backend API: Event-sourced ASP.NET Core Minimal APIs with Marten and PostgreSQL
- Frontend: Blazor web application for browsing and managing books
- Orchestration: .NET Aspire for local development, deployment, and observability
- Database: PostgreSQL with event store and read model projections
- Modern Stack: .NET 10 with C# 14 (latest language features)
๐ Quick Start
# Prerequisites: .NET 10 SDK, Aspire CLI, Docker Desktop
# Install Aspire CLI: Follow instructions at https://aspire.dev/get-started/install-cli/
# Clone and run
git clone <repository-url>
cd BookStore
dotnet restore
aspire run
The Aspire dashboard opens automatically, providing access to:
- Web Frontend - Blazor application for browsing books
- API Service - Backend API with Scalar documentation at
/scalar/v1 - PostgreSQL - Event store and read model database
- PgAdmin - Database management interface
โจ Features
Frontend (Blazor Web)
- Book Catalog with search and filtering
- Book Details with comprehensive information
- Real-time Updates with SignalR notifications
- Optimistic UI for instant feedback with eventual consistency
- Responsive Design for desktop and mobile
- Type-safe API Client with Refit
- Resilience with Polly (retry and circuit breaker)
Backend API
- Event Sourcing with Marten and PostgreSQL
- CQRS with async projections for optimized reads
- Real-time Notifications with SignalR (Wolverine integration)
- Multi-language Support for categories (en, pt, es, fr, de)
- Full-text Search with PostgreSQL trigrams and unaccent
- Optimistic Concurrency with ETags
- Distributed Tracing with correlation/causation IDs
- API Versioning (header-based, v1.0)
- Soft Deletion - Logical deletes with restore capability
Architecture Enforcement
The project includes a custom Roslyn Analyzer (BookStore.ApiService.Analyzers) that enforces Event Sourcing, CQRS, and DDD patterns:
- โ Events must be immutable record types
- โ Commands follow CQRS conventions
- โ Aggregates use proper Marten Apply methods
- โ Handlers follow Wolverine conventions
- โ Consistent namespace organization
See Analyzer Rules Documentation for details.
- Native OpenAPI with Scalar UI
- Structured Logging with correlation IDs
Infrastructure (.NET Aspire)
- Service Orchestration for local development
- Service Discovery between frontend and backend
- OpenTelemetry integration for observability
- Container Management for PostgreSQL and PgAdmin
- Dashboard for monitoring all services
๐ Project Structure
BookStore/
โโโ src/
โ โโโ BookStore.ApiService/ # Backend API with event sourcing
โ โ โโโ Aggregates/ # Domain aggregates
โ โ โโโ Events/ # Domain events
โ โ โโโ Commands/ # Command definitions
โ โ โโโ Handlers/ # Wolverine command handlers
โ โ โโโ Projections/ # Read model projections
โ โ โโโ Endpoints/ # API endpoints
โ โ โโโ Infrastructure/ # Cross-cutting concerns
โ โ
โ โโโ BookStore.Web/ # Blazor frontend
โ โ โโโ Components/ # Blazor components
โ โ โโโ Services/ # API client (Refit)
โ โ โโโ Models/ # DTOs and view models
โ โ
โ โโโ BookStore.AppHost/ # Aspire orchestration
โ โ โโโ Program.cs # Service configuration
โ โ
โ โ โโโ Extensions.cs # OpenTelemetry, health checks
โ โ
โ โโโ BookStore.Shared/ # Shared domain models & DTOs
โ โ โโโ BookStore.Shared/ # Shared library
โ โ โโโ BookStore.Shared.Tests/# Unit tests for shared code
โ โ
โ โโโ BookStore.Tests/ # Integration tests
โ
โโโ docs/ # Documentation
โ โโโ getting-started.md # Setup guide
โ โโโ architecture.md # System design
โ โโโ wolverine-guide.md # Command/handler pattern
โ โโโ time-standards.md # JSON and time standards
โ โโโ etag-guide.md # ETag usage
โ โโโ correlation-causation-guide.md
โ
โโโ BookStore.slnx # Solution file (new .slnx format)
โโโ README.md # This file
๐ Documentation
- Getting Started - Setup and first steps
- Architecture Overview - System design and patterns
- Aspire Orchestration Guide - Service orchestration and local development
- Aspire Deployment Guide - Deploy to Azure and Kubernetes
- Production Scaling Guide - Scale applications and databases in production
- Configuration Guide - Options pattern and validation
- Performance Guide - GC optimization and performance tuning
- Logging Guide - Structured logging with source-generated log messages
- Testing Guide - Testing with TUnit, assertions, and best practices
- Wolverine Integration - Command/handler pattern with Wolverine
- API Conventions - Time handling and JSON serialization standards
- ETag Support - Optimistic concurrency and caching
- Correlation & Causation IDs - Distributed tracing
- Caching Guide - Hybrid caching with Redis and localization support
- Real-time Notifications - SignalR integration and optimistic updates
- Contributing Guidelines - How to contribute to this project
๐ง Technology Stack
Frontend
- Blazor Web - Interactive web UI with Server rendering
- SignalR Client - Real-time notifications
- Refit - Type-safe HTTP client
- Polly - Resilience and transient fault handling
Backend
- ASP.NET Core 10 - Minimal APIs
- C# 14 - Latest language features (collection expressions, primary constructors, etc.)
- Marten 8.17 - Event store and document DB
- Wolverine 5.9 - Mediator, message bus, and SignalR integration
- PostgreSQL 16 - Database with pg_trgm and unaccent extensions
Infrastructure
- .NET Aspire - Orchestration and observability
- OpenTelemetry - Distributed tracing and metrics
- Scalar - API documentation UI
- Docker - Container runtime
- TUnit - Modern testing framework with built-in code coverage
- Roslynator.Analyzers 4.15.0 - Enhanced code analysis
๐ API Endpoints
Public Endpoints
GET /api/books/search- Search books with paginationGET /api/books/{id}- Get book by ID (with ETag)GET /api/authors- List authorsGET /api/categories- List categories (localized)GET /api/publishers- List publishers
Admin Endpoints
POST /api/admin/books- Create bookPUT /api/admin/books/{id}- Update book (with If-Match)DELETE /api/admin/books/{id}- Soft delete bookPOST /api/admin/books/{id}/restore- Restore book- Similar CRUD for authors, categories, publishers
POST /api/admin/projections/rebuild- Rebuild projections
๐ Localization Example
# Get categories in Portuguese
curl -H "Accept-Language: pt-PT" http://localhost:5000/api/categories
# Create category with translations
curl -X POST http://localhost:5000/api/admin/categories \
-H "Content-Type: application/json" \
-d '{
"name": "Software Architecture",
"translations": {
"en": {"name": "Software Architecture"},
"pt": {"name": "Arquitetura de Software"}
}
}'
๐ Event Sourcing Example
# All operations create events in the event store
# Create a book
curl -X POST http://localhost:5000/api/admin/books \
-H "X-Correlation-ID: workflow-123" \
-d '{"title": "Clean Code", ...}'
# โ BookAdded event stored
# Update the book
curl -X PUT http://localhost:5000/api/admin/books/{id} \
-H "X-Correlation-ID: workflow-123" \
-H "If-Match: \"1\"" \
-d '{"title": "Clean Code (Updated)", ...}'
# โ BookUpdated event stored
# View all events for this workflow
SELECT * FROM mt_events
WHERE correlation_id = 'workflow-123';
๐ก๏ธ Optimistic Concurrency with ETags
# Get book (receives ETag)
curl -i http://localhost:5000/api/books/{id}
# ETag: "5"
# Update with concurrency check
curl -X PUT http://localhost:5000/api/admin/books/{id} \
-H "If-Match: \"5\"" \
-d '{"title": "Updated Title", ...}'
# Success โ ETag: "6"
# Concurrent update fails
curl -X PUT http://localhost:5000/api/admin/books/{id} \
-H "If-Match: \"5\"" \
-d '{"title": "Another Update", ...}'
# Error: 412 Precondition Failed
๐ Monitoring
- Health Checks:
/health - Aspire Dashboard:
https://localhost:17161 - Scalar API Docs:
/scalar/v1 - OpenAPI Spec:
/openapi/v1.json
๐งช Testing
The project uses TUnit, a modern testing framework with built-in code coverage and parallel execution.
# Run all tests
dotnet test
# Run tests for specific project
dotnet test --project src/BookStore.Tests/BookStore.Tests.csproj
# Alternative: Run tests directly
dotnet run --project src/BookStore.Tests/BookStore.Tests.csproj
Note
TUnit uses Microsoft.Testing.Platform on .NET 10+. The global.json file configures the test runner automatically.
๐ License
This project is licensed under the MIT License - see the LICENSE file for details.
Copyright (c) 2025 Antao Almada
๐ค Contributing
Contributions are welcome! Please read our Contributing Guidelines for details on:
- How to report issues
- How to suggest features
- Development setup and workflow
- Coding standards and best practices
- Pull request process
By contributing, you agree that your contributions will be licensed under the MIT License.