Getting Started with Book Store
This guide will help you set up and run the complete Book Store application (frontend + backend) on your local machine using .NET Aspire.
Prerequisites
Required Software
- .NET 10 SDK - Download
- Includes C# 14 with latest language features (collection expressions, primary constructors, etc.)
- .NET Aspire workload - Install after .NET SDK:
dotnet workload install aspire - Docker Desktop - Download
- Git - Download
Recommended Tools
- Visual Studio 2024 or VS Code with C# extension
- Postman or curl for API testing
- pgAdmin (included in Docker setup) for database inspection
Installation
1. Clone the Repository
git clone <repository-url>
cd BookStore
2. Verify Docker is Running
docker --version
docker ps
Make sure Docker Desktop is running and healthy.
3. Restore Dependencies
dotnet restore
4. Run the Application with Aspire
aspire run
This will:
- Start the .NET Aspire orchestrator
- Launch PostgreSQL and PgAdmin containers via Docker
- Start the backend API service
- Start the Blazor web frontend
- Open the Aspire dashboard in your browser
Tip
For a deep dive into how Aspire orchestrates the solution locally, see the Aspire Orchestration Guide.
Accessing the Application
Aspire Dashboard
The Aspire dashboard opens automatically or visit:
https://localhost:17161/login?t=<token>
From the dashboard you can:
- View all services: Frontend, API, PostgreSQL, PgAdmin
- Check resource health: CPU, memory, status
- Access logs: Real-time logs for each service
- Find service URLs: Click on any service to get its endpoint
Web Frontend (Blazor)
- In the Aspire dashboard, click on web (BookStore.Web)
- Click the HTTP endpoint URL (e.g.,
http://localhost:5001) - Browse the book catalog, search for books, and view details
The frontend provides:
- Book Catalog: Browse and search all available books
- Book Details: View comprehensive information about each book
- Responsive Design: Works on desktop and mobile devices
API Documentation (Scalar)
- In the Aspire dashboard, click on apiservice
- Copy the HTTP endpoint URL (e.g.,
http://localhost:5000) - Navigate to
http://localhost:5000/scalar/v1
You'll see interactive API documentation where you can:
- Browse all endpoints (public and admin)
- Test API calls directly from the browser
- View request/response schemas
- See code examples in multiple languages
Database (PgAdmin)
- In the Aspire dashboard, click on pgadmin
- Login with credentials (shown in Aspire dashboard)
- Connect to the PostgreSQL server
- Explore the event store and projections:
mt_events- All domain events (event sourcing)mt_streams- Event streams per aggregatebook_search_projection- Book search read modelauthor_projection,category_projection,publisher_projection
First API Calls
1. Create a Publisher
curl -X POST http://localhost:5000/api/admin/publishers \
-H "Content-Type: application/json" \
-H "X-Correlation-ID: getting-started-001" \
-d '{
"name": "O'\''Reilly Media"
}'
Response:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"correlationId": "getting-started-001"
}
2. Create an Author
curl -X POST http://localhost:5000/api/admin/authors \
-H "Content-Type: application/json" \
-H "X-Correlation-ID: getting-started-001" \
-d '{
"name": "Martin Fowler",
"biography": "Software development expert and author"
}'
3. Create a Category
curl -X POST http://localhost:5000/api/admin/categories \
-H "Content-Type: application/json" \
-H "X-Correlation-ID: getting-started-001" \
-d '{
"name": "Software Architecture",
"description": "Books about software design and architecture",
"translations": {
"en": {
"name": "Software Architecture",
"description": "Books about software design and architecture"
},
"pt": {
"name": "Arquitetura de Software",
"description": "Livros sobre design e arquitetura de software"
}
}
}'
4. Create a Book
curl -X POST http://localhost:5000/api/admin/books \
-H "Content-Type: application/json" \
-H "X-Correlation-ID: getting-started-001" \
-d '{
"title": "Patterns of Enterprise Application Architecture",
"isbn": "978-0321127426",
"description": "Classic book on enterprise application patterns",
"publicationDate": "2002-11-15",
"publisherId": "550e8400-e29b-41d4-a716-446655440000",
"authorIds": ["<author-id-from-step-2>"],
"categoryIds": ["<category-id-from-step-3>"]
}'
5. Search for Books
curl "http://localhost:5000/api/books/search?q=patterns"
6. Get Categories in Portuguese
curl -H "Accept-Language: pt-PT" http://localhost:5000/api/categories
Exploring the Event Store
View Events in PostgreSQL
-- Connect to the database via PgAdmin
-- View all events
SELECT
id,
seq_id,
type,
timestamp,
correlation_id,
causation_id
FROM mt_events
ORDER BY timestamp DESC
LIMIT 10;
-- View events for a specific correlation ID
SELECT * FROM mt_events
WHERE correlation_id = 'getting-started-001'
ORDER BY timestamp;
-- View a specific stream
SELECT * FROM mt_streams
WHERE id = '<book-id>';
Project Structure
BookStore/
├── src/
│ ├── BookStore.ApiService/ # Backend API
│ │ ├── Aggregates/ # Domain aggregates (event sourcing)
│ │ ├── Events/ # Domain events
│ │ ├── Commands/ # Command definitions
│ │ ├── Handlers/ # Wolverine command handlers
│ │ ├── Projections/ # Read models (CQRS)
│ │ ├── Endpoints/ # API endpoints
│ │ │ ├── Admin/ # Admin CRUD endpoints
│ │ │ ├── BookEndpoints.cs # Public book endpoints
│ │ │ └── ... # Other public endpoints
│ │ ├── Infrastructure/ # Cross-cutting concerns
│ │ └── Program.cs # API entry point
│ │
│ ├── BookStore.Web/ # Blazor Frontend
│ │ ├── Components/ # Blazor components
│ │ │ ├── Pages/ # Page components
│ │ │ └── Layout/ # Layout components
│ │ ├── Services/ # API client (Refit)
│ │ ├── Models/ # DTOs and view models
│ │ └── Program.cs # Frontend entry point
│ │
│ ├── BookStore.AppHost/ # Aspire Orchestration
│ │ └── Program.cs # Service configuration
│ │
│ ├── BookStore.ServiceDefaults/ # Shared Configuration
│ │ └── Extensions.cs # OpenTelemetry, logging, health checks
│ │
│ └── BookStore.Tests/ # Unit Tests
│ ├── Handlers/ # Handler tests
│ └── JsonSerializationTests.cs
│
└── docs/ # Documentation
├── getting-started.md # This guide
├── architecture.md # System design
├── wolverine-guide.md # Command/handler pattern
├── time-standards.md # JSON and time standards
└── ... # Other guides
Testing
The project uses TUnit, a modern testing framework for .NET with built-in code coverage and source-generated tests.
Running Tests
# Run all tests
dotnet test
# Run tests for specific project
dotnet test --project src/ApiService/BookStore.ApiService.Tests/BookStore.ApiService.Tests.csproj
# Alternative: Run tests directly
dotnet run --project src/ApiService/BookStore.ApiService.Tests/BookStore.ApiService.Tests.csproj
Test Structure
- Handler Tests - Test Wolverine command handlers with mocked dependencies
- JSON Serialization Tests - Verify API serialization standards (ISO 8601, camelCase, etc.)
- Integration Tests - Test the full application stack with Aspire.Hosting.Testing
All tests use TUnit's fluent assertion syntax:
_ = await Assert.That(result).IsNotNull();
_ = await Assert.That(actual).IsEqualTo(expected);
_ = await Assert.That(collection).Contains(item);
Note
TUnit provides built-in code coverage without additional packages. Tests run in parallel by default for improved performance.
Development Workflow
1. Make Code Changes
Edit files in src/ApiService/BookStore.ApiService/
2. Hot Reload
The application supports hot reload. Changes to code will automatically rebuild and restart.
3. Test Changes
Use Scalar UI or curl to test your changes:
# Test endpoint
curl http://localhost:5000/api/books/search?q=test
# Check logs in Aspire dashboard
4. View Events
Check PgAdmin to see events being stored:
SELECT * FROM mt_events ORDER BY timestamp DESC LIMIT 5;
Common Tasks
Rebuild Projections
curl -X POST http://localhost:5000/api/admin/projections/rebuild
Check Projection Status
curl http://localhost:5000/api/admin/projections/status
Health Check
curl http://localhost:5000/health
Troubleshooting
Docker Not Running
Error: "Container runtime 'docker' was found but appears to be unhealthy"
Solution:
- Open Docker Desktop
- Wait for it to fully start
- Run
aspire runagain
Port Already in Use
Error: "Address already in use"
Solution:
# Find process using port
lsof -i :5000
# Kill the process
kill -9 <PID>
Database Connection Issues
Error: "Could not connect to PostgreSQL"
Solution:
- Check Docker is running
- Check Aspire dashboard for PostgreSQL status
- Verify connection string in
appsettings.json
Build Errors
# Clean and rebuild
dotnet clean
dotnet restore
dotnet build
Next Steps
- Architecture Overview - Understand the system design
- Testing Guide - Learn about testing with TUnit
- Event Sourcing Guide - Learn about event sourcing
- ETag Support - Implement optimistic concurrency
Getting Help
- Check the API Reference for endpoint details
- Review Architecture for design patterns
- Explore the Scalar UI for interactive documentation
- Check Aspire dashboard logs for debugging