The coffeeshop apps on .NET Aspire
Notice: This is just a demo of how can we build and deploy the microservices approach. In the reality, the boundary should be defined by bounded-context concepts of Domaim-driven Design, and totally based on the business domain, and might not be so fine-grained services like this demo, so you use it with care.
- Built on .NET 8.0 LTS
- Microservices architectural style
- Follows Vertical Sliding principles
- Domain Driven Design building blocks
- CQRS with MediatR and Fluent Validations
- Shift-left Observability with .NET Aspire (OpenTelemetry built-in)
- Custom OpenTelemetry for MediatR and FluentValidation handlers
- Custom OpenTelemetry for MassTransit on consumers
- Enrich .NET 8 global loggings
- OpenAPI supports
- Mapperly for generating object mappings
- API Versioning
- Integration test with .NET Aspire and Wiremock.NET
- Run it on GitHub Actions and output code coverage
- Response Caching - Distributed Caching with Redis
- Dapr integration
- JWT & Authentication with Keycloak
C4Context
title System Context diagram for CoffeeShop Application
Boundary(b0, "Boundary1") {
Person(customer, "Customers", "Customers of the coffeeshop.")
Boundary(b1, "Application", "boundary") {
System(SystemA, "CoffeeShop app", "Allows customers to submit and view their orders.")
}
Boundary(b2, "Infrastructure", "boundary") {
SystemDb(SystemD, "Database", "A system of the coffeeshop app.")
SystemQueue(SystemQ, "Message Queue", "A system of the coffeeshop app.")
}
}
Rel(customer, SystemA, "Uses")
Rel(SystemA, SystemD, "Uses")
Rel(SystemA, SystemQ, "Uses")
C4Container
title Container diagram for CoffeeShop Application
Person(customer, "Customers", "Customers of the coffeeshop.")
Container_Boundary(c1, "CoffeeShop Application") {
Container(reverse_proxy, "Gateway", "C#, .NET 8, YARP", "The reverse proxy/API gateway of the coffeeshop app.")
Container(counter_api, "Counter APIs", "C#, .NET 8, MassTransit", "The counter service.")
Container(barista_api, "Barista APIs", "C#, .NET 8, MassTransit", "The barista service.")
Container(kitchen_api, "Kitchen APIs", "C#, .NET 8, MassTransit", "The kitchen service.")
Container(order_summary, "Order Summary", "C#, .NET 8, Marten", "The order summary service.")
Container(product_api, "Product APIs", "C#, .NET 8", "The product service.")
Boundary(b1, "Docker containers", "boundary") {
ContainerDb(database, "Database", "Postgres", "Stores orders, audit logs, etc.")
ContainerQueue(message_broker, "Message Broker", "RabbitMQ", "Asynchronous communication between counter, barista, kitchen, and order-summary")
}
}
Rel(customer, reverse_proxy, "Uses", "HTTPS")
Rel(reverse_proxy, product_api, "Proxies", "HTTP")
Rel(reverse_proxy, counter_api, "Proxies", "HTTP")
Rel(order_summary, database, "Uses", "TCP")
Rel(counter_api, product_api, "Calls", "HTTP")
Rel(counter_api, message_broker, "Publishes", "TCP")
Rel(counter_api, message_broker, "Publishes", "TCP")
Rel_Back(barista_api, message_broker, "Subscribes", "TCP")
Rel_Back(kitchen_api, message_broker, "Subscribes", "TCP")
Rel_Back(order_summary, message_broker, "Subscribes", "TCP")
If you run on Windows 11
:
> cargo install just
# https://cheatography.com/linux-china/cheat-sheets/justfile/
> dotnet build coffeeshop-aspire.sln
> dotnet run --project app-host/app-host.csproj
# http://localhost:5019
dotnet run --project app-host\CoffeeShop.AppHost.csproj `
--publisher manifest `
--output-path ../aspire-manifest.json
dotnet tool install -g aspirate --prerelease
> just run
On Windows 11 - WSL2 Ubuntu 22 integrated, we can use Podman Desktop
to replace Docker for Desktop
, and run .NET Aspire
normally. Check this blog post -> https://dev.to/thangchung/net-8-integration-tests-on-podman-desktop-windows-11-wsl2-ubuntu-23-4hpo
> touch .env
> make run
# http://localhost:5019
dotnet publish "/workspaces/coffeeshop-aspire/app-host/../product-api/CoffeeShop.ProductApi.csproj" -p:PublishProfile="DefaultContainer" -p:PublishSingleFile="true"
-p:PublishTrimmed="false" --self-contained "true" --verbosity "quiet" --nologo -r "linux-x64" -p:ContainerRegistry="k3d-myregistry.localhost:12345" -p:ContainerRepository="product-api" -p:ContainerImageTag="latest"