Aloha Market is a .NET 9 microservices backend for a marketplace platform. The solution uses .NET Aspire for orchestration, Kafka for asynchronous integration, and YARP as the API gateway.
The system is designed around independent business services and data ownership:
- User profiles, roles, and JWT-based authentication with Keycloak
- Marketplace listings with media upload, search, and moderation workflows
- Category and location validation with seed data
- Subscription plans and user plan provisioning
- VNPay and Momo payment flows with callbacks
- Real-time chat and notifications with SignalR
- Event-driven service communication over Kafka
The diagram below shows a reference deployment topology for the platform.
- Azure Front Door + WAF protects the public entry point before traffic reaches the YARP gateway.
- Azure Container Registry and Azure Container Apps host the gateway, Keycloak, and microservices.
- Azure Database for PostgreSQL stores User, Post, Category, and Plan service data.
- Azure Cosmos DB stores Location and Payment service data.
- Azure Event Hubs Kafka endpoint provides managed event streaming for service-to-service integration.
- Azure Key Vault, Application Insights, Log Analytics, Azure SignalR, and Private Endpoints provide secrets management, observability, realtime messaging, and network isolation.
stateDiagram-v2
[*] --> Draft: Create Post
state "Authentication" as Auth {
NotLoggedIn --> LoggedIn : Login/Register
LoggedIn --> NotLoggedIn : Logout
}
state "Post States" as Post {
Draft --> Pending : Submit Post
Pending --> Created : Approve Post
Created --> Draft : Edit Post
Created --> Inactive : Deactivate Post
Inactive --> Created : Activate Post
state "Post Visibility" as Visibility {
Active --> Hidden : Hide Post
Hidden --> Active : Show Post
}
}
state "Plan Subscription" as Plan {
NoPlan --> ActivePlan : Subscribe
ActivePlan --> ExpiredPlan : Time Expires
ExpiredPlan --> ActivePlan : Renew Plan
state ActivePlan {
HasPosts --> NoMorePosts : Reach Max Posts
HasPushes --> NoPushes : Use All Pushes
}
}
state "Category Management" as Category {
state "Category Hierarchy" as CH {
Root --> SubCategory : Add SubCategory
SubCategory --> SubCategory : Add Nested Category
}
}
state "Location Structure" as Location {
Province --> District : Select District
District --> Ward : Select Ward
Ward --> [*] : Complete Location
}
state "Event Publishing" as Events {
Produced --> Consumed : Kafka Message Flow
Consumed --> Handled : Process Event
Handled --> [*] : Complete Event
}
| Service | Project | Responsibilities | Data store | Integrations |
|---|---|---|---|---|
| API Gateway | Aloha.ApiGateway |
Reverse proxy, routing, CORS, auth header forwarding, request logging | None | YARP, Keycloak JWT |
| User Service | Aloha.UserService |
User profiles, avatars, admin status, seller info | PostgreSQL | Cloudinary, Kafka |
| Post Service | Aloha.MicroService.Post |
Listings, filtering, search, status changes, reports, moderation | PostgreSQL | Cloudinary, Kafka |
| Category Service | Aloha.CategoryService |
Category CRUD, hierarchy path, seed data | PostgreSQL | Kafka |
| Location Service | Aloha.LocationService |
Province, district, and ward lookup data | MongoDB | Kafka |
| Plan Service | Aloha.MicroService.Plan |
Subscription plans, user plans, admin reporting | PostgreSQL | Kafka, Keycloak roles |
| Payment Service | Aloha.MicroService.Payment |
Payment records, VNPay and Momo flows, callbacks | MongoDB | Kafka |
| Notification Service | Aloha.NotificationService |
Conversations, messages, SignalR hub, chat notifications | MongoDB | Kafka, SignalR |
Aloha.EventBus: integration event abstractions and MediatR-based dispatchAloha.EventBus.Kafka: Kafka producer and consumer wiring using Aspire.Confluent.KafkaAloha.EventBus.Models: typed contracts for posts, plans, payments, chat, and validation eventsAloha.Security: Keycloak JWT authentication extensions and claims helpersAloha.Shared: response helpers, validation attributes, converters, middleware, and common utilitiesAloha.ServiceDefaults: OpenTelemetry, health checks, service discovery, Cloudinary, and shared dependency registrationAloha.AppHost: Aspire AppHost that wires projects and Kafka topics
Each service publishes to a Kafka topic derived from its Aspire project name. AppHost assigns EVENT_PUBLISHING_TOPICS and EVENT_CONSUMING_TOPICS for each service and creates the required topics during startup.
sequenceDiagram
participant Service as Service
participant Publisher as EventPublisher
participant Kafka as Kafka
participant Consumer as EventHandler
Service->>Publisher: Publish integration event
Publisher->>Kafka: Send to topic
Kafka->>Consumer: Consume event
Consumer->>Consumer: Process event
| Publishing service topic | Consuming topics configured in AppHost |
|---|---|
Aloha-MicroService-User |
Aloha-MicroService-Post, Aloha-MicroService-Location, Aloha-NotificationService |
Aloha-MicroService-Post |
Aloha-MicroService-User, Aloha-MicroService-Location, Aloha-MicroService-Plan, Aloha-MicroService-Category, Aloha-NotificationService |
Aloha-MicroService-Plan |
Aloha-MicroService-User, Aloha-MicroService-Post, Aloha-MicroService-Payment |
Aloha-MicroService-Location |
Aloha-MicroService-Post |
Aloha-MicroService-Category |
Aloha-MicroService-Post |
Aloha-MicroService-Payment |
Aloha-MicroService-Plan |
Aloha-NotificationService |
Aloha-MicroService-User, Aloha-MicroService-Post |
| Service | Store |
|---|---|
| API Gateway | None |
| User Service | PostgreSQL |
| Post Service | PostgreSQL |
| Category Service | PostgreSQL |
| Plan Service | PostgreSQL |
| Location Service | MongoDB |
| Payment Service | MongoDB |
| Notification Service | MongoDB |
- .NET 9 / ASP.NET Core
- .NET Aspire
- Apache Kafka
- YARP Reverse Proxy
- PostgreSQL + EF Core + Npgsql
- MongoDB
- Keycloak JWT authentication
- OpenTelemetry
- SignalR
- Cloudinary + ImageSharp
- Docker
Aspire.Hosting.AppHostAspire.Hosting.KafkaAspire.Hosting.MongoDBAspire.Hosting.PostgreSQLAspire.Hosting.Azure.SignalRAspire.Hosting.RabbitMQAspire.Confluent.KafkaMediatRAutoMapperMicrosoft.EntityFrameworkCoreNpgsql.EntityFrameworkCore.PostgreSQLMongoDB.DriverMicrosoft.AspNetCore.Authentication.JwtBearerYarp.ReverseProxySwashbuckle.AspNetCoreOpenTelemetry.*AspNetCore.HealthChecks.NpgSqlCloudinaryDotNetSixLabors.ImageSharpdotenv.net
EVENT_PUBLISHING_TOPICSEVENT_CONSUMING_TOPICS- Connection string named
kafkawhen running outside Aspire
ReverseProxyroutes and clusters in configuration
Authentication:AuthorityAuthentication:Audience
CLOUDINARY_CLOUDNAMECLOUDINARY_APIKEYCLOUDINARY_APISECRET
ConnectionStrings:SupabaseConnectionConnectionStrings:DefaultConnectionConnectionStrings:PostgresConnection
- Location service:
MongoSettings:ConnectionString,MongoSettings:DatabaseName - Payment service:
MongoSettings:ConnectionString,MongoSettings:DatabaseName,MongoSettings:CollectionName - Notification service:
MongoDbSettings:ConnectionString,MongoDbSettings:DatabaseName
- VNPay:
Vnpay:Version,Vnpay:Command,Vnpay:TmnCode,Vnpay:CurrCode,Vnpay:Locale,Vnpay:BaseUrl,Vnpay:HashSecret - Payment callback:
PaymentCallBack:ReturnUrl,TimeZoneId - Momo:
MomoAPI:PartnerCode,MomoAPI:AccessKey,MomoAPI:SecretKey,MomoAPI:MomoApiUrl,MomoAPI:ReturnUrl,MomoAPI:NotifyUrl,MomoAPI:RequestType - Frontend redirects:
FrontendRedirect:SuccessUrl,FrontendRedirect:FailedUrl
OTEL_EXPORTER_OTLP_ENDPOINT
- .NET 9 SDK
- Docker Desktop
- PostgreSQL and MongoDB instances
- Keycloak
- Cloudinary account
dotnet run --project Aloha/Aloha.AppHostdotnet run --project Aloha.UserServiceProvide the required Kafka, database, and authentication settings when running a single service outside AppHost.
docker compose up -dEach service exposes Swagger UI in development. The UI is hosted at the service root (/) and OpenAPI is available at /openapi/v1.json.
Aloha.Aspire.sln: solution entry pointAloha/Aloha.AppHost: Aspire AppHost wiring, project references, topic creationAloha.ApiGateway: YARP reverse proxy and auth forwardingAloha.UserServiceAloha.MicroService.PostAloha.CategoryServiceAloha.LocationServiceAloha.MicroService.PlanAloha.MicroService.PaymentAloha.NotificationServiceAloha.EventBus,Aloha.EventBus.Kafka,Aloha.EventBus.Models: event bus and contractsAloha.Security,Aloha.Shared,Aloha/Aloha.ServiceDefaults: shared cross-cutting componentsdocker-compose.yml: local PostgreSQL and pgAdmin for development
- Create a new project following the existing service structure.
- Register the service in
Aloha.AppHost. - Configure Kafka topics and service references.
- Add the service-specific datastore and integration settings.
Example:
builder.AddProjectWithPostfix<Your_Service>()
.SetupKafka<Your_Service>(kafka)
.WithReference(otherServices);- Use integration events for cross-service communication.
- Keep database ownership inside each service.
- Use migrations for schema changes.
- Keep sensitive values in environment variables or secret stores.
- Apply least-privilege access to infrastructure and service dependencies.
- Fork the repository.
- Create a feature branch.
- Commit your changes.
- Push the branch.
- Open a pull request.
This project is licensed under the Unlicense License. See LICENSE.txt.
