Book Store

CI CodeQL

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

๐Ÿ”ง 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 pagination
  • GET /api/books/{id} - Get book by ID (with ETag)
  • GET /api/authors - List authors
  • GET /api/categories - List categories (localized)
  • GET /api/publishers - List publishers

Admin Endpoints

  • POST /api/admin/books - Create book
  • PUT /api/admin/books/{id} - Update book (with If-Match)
  • DELETE /api/admin/books/{id} - Soft delete book
  • POST /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.