A ferry booking management API built with ASP.NET Core 10.0 and .NET Aspire. Passengers create bookings on multi-stop ferry routes, operators view manifests, and the system enforces per-leg capacity constraints.
The API models a real ferry route between Bergen, Stavanger, Hirtshals, and Kristiansand. Each departure leg maintains independent capacity — a passenger boarding in Bergen and alighting in Stavanger only occupies a seat on that leg, freeing capacity for subsequent legs. The system seeds departures across both directions of the route.
Built with observability as a first-class concern: OpenTelemetry provides distributed tracing with custom spans, application-specific metrics (occupancy gauge, booking duration histogram), and structured logging. .NET Aspire orchestrates local development with a dashboard that surfaces all telemetry signals, health status, and resource links in one place.
- Features
- Getting Started
- Configuration
- Architecture
- Docker Compose
- Health Checks
- Development
- Contributing
- License
API
- Multi-stop routes with independent per-leg capacity
- List departures with real-time available capacity
- Create bookings with passenger count validation and capacity enforcement
- View departure manifests with full passenger lists
- Cancel bookings and automatically free capacity
Observability
- OpenTelemetry tracing with auto-instrumentation (ASP.NET Core, EF Core, Npgsql) and custom spans per operation
- Custom metrics: departure occupancy gauge (0.0–1.0 per leg) and booking duration histogram
- Structured logging
- OTLP export (gRPC) to any compatible collector when configured
Developer Experience
- .NET Aspire orchestration with dashboard for traces, metrics, logs, and health monitoring
- Interactive API documentation via Scalar at
/docs - Docker Compose for one-command startup with PostgreSQL and Aspire Dashboard
- .NET 10 SDK
- .NET Aspire tool
Install the Aspire tool:
dotnet tool install aspire.cli [--global]git clone https://github.com/glinklater/Bookings.git
cd Bookings
dotnet restore Bookings.slnx
dotnet build Bookings.slnxStart the application via the Aspire AppHost (preferred for local development):
dotnet run --project Bookings.AppHostOr run the API standalone:
dotnet run --project Bookings.Api| URL | Purpose |
|---|---|
| http://localhost:8080 | API |
| http://localhost:8080/docs | API documentation (Scalar) |
| http://localhost:8081/healthz | Health checks |
| Aspire Dashboard | Auto-assigned port (see console output) |
The API runs on two ports configured via Kestrel in appsettings.json:
| Port | Purpose |
|---|---|
| 8080 | API traffic and documentation |
| 8081 | Health checks and metrics (host-restricted) |
| Variable | Description |
|---|---|
OTEL_EXPORTER_OTLP_ENDPOINT |
OpenTelemetry collector endpoint. When set, enables export of traces, metrics, and logs via OTLP. |
OTEL_EXPORTER_OTLP_TRACES_PROTOCOL |
OTLP transport protocol (e.g. grpc). |
OTEL_SERVICE_NAME |
Service name reported to the telemetry collector. |
ASPNETCORE_FORWARDEDHEADERS_ENABLED |
Set to true when running behind a reverse proxy. |
This repository contains three projects:
- Bookings.Api — ASP.NET Core minimal API. Entry point is
Program.cs; service registration lives inStartupConfiguration.csusing C# 13 extension blocks. - Bookings.AppHost — .NET Aspire orchestrator for local development. Wires the API with PostgreSQL, runs migrations, and configures the dashboard.
- Bookings.Tests — NUnit 4 test project with unit and integration tests.
Key patterns:
- Minimal APIs
- Structured Logging via
LoggerMessagefor zero-allocation log calls - OpenAPI v3.1 document (
v1alpha1)
Run the API and Aspire Dashboard together:
docker compose up --build| Service | URL |
|---|---|
| API Documentation | http://localhost:8080/docs |
| Aspire Dashboard | http://localhost:18888 |
The Aspire Dashboard receives OpenTelemetry data from the API, providing distributed traces, metrics, and structured logs.
Health endpoints are served on port 8081 (host-restricted, not exposed to API consumers):
| Endpoint | Purpose | What it checks |
|---|---|---|
/healthz |
All checks | Self + database connectivity |
/healthz/alive |
Liveness | Self check only (fast, no I/O) |
/healthz/ready |
Readiness | EF Core database connectivity |
These endpoints are designed for Kubernetes probe configuration:
livenessProbe:
httpGet:
path: /healthz/alive
port: 8081
initialDelaySeconds: 5
periodSeconds: 10
readinessProbe:
httpGet:
path: /healthz/ready
port: 8081
initialDelaySeconds: 10
periodSeconds: 5Start the application via the Aspire AppHost:
dotnet run --project Bookings.AppHostThis launches the API and opens the Aspire Dashboard, which provides:
- Distributed tracing — end-to-end request traces with automatic instrumentation for HTTP clients and ASP.NET Core
- Metrics — runtime, HTTP, and custom application metrics collected via OpenTelemetry
- Structured logs — formatted log entries with scopes, searchable in the dashboard
- Health monitoring — live health status from the API's
/healthzendpoint
When running the Aspire AppHost locally, you can retrieve the dashboard access token from the console output.
To run tests:
dotnet test Bookings.TestsPRs accepted. All contributions must pass all linters, style requirements, and static code analysis to be considered.
Small note: If editing the README, please conform to the standard-readme specification.
Copyright © 2026 Greg Linklater
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.