A bus trip booking platform: ASP.NET Core (.NET 10) + PostgreSQL backend and an Angular + Tailwind customer web app. Built as a Clean Architecture modular monolith — see ARCHITECTURE.md for the layering and the principles we hold to.
src/
TransportPlatform.Domain entities, value objects, domain events (no dependencies)
TransportPlatform.Application use-case handlers + abstractions
TransportPlatform.Infrastructure EF Core/Postgres, Identity/JWT, payment gateway
TransportPlatform.Api minimal-API endpoints, middleware, background workers
tests/
TransportPlatform.UnitTests domain + application + architecture (layering) tests
TransportPlatform.IntegrationTests full API against a throwaway Postgres (Testcontainers)
web/customer Angular 20 + Tailwind customer SPA (see its own README)
-
.NET 10 SDK and Docker (the integration tests spin up Postgres via Testcontainers).
-
A local PostgreSQL for running the API. The dev connection string is in
src/TransportPlatform.Api/appsettings.json. Quick start with Docker:docker run --name tpx-pg -e POSTGRES_DB=transport -e POSTGRES_USER=transport \ -e POSTGRES_PASSWORD=change_me_local_only -p 5432:5432 -d postgres:17-alpine
dotnet build # 0 warnings (warnings are errors)
dotnet test # unit + integration (Docker must be running)
dotnet ef database update \
--project src/TransportPlatform.Infrastructure \
--startup-project src/TransportPlatform.Api # apply migrations
dotnet run --project src/TransportPlatform.Api --launch-profile http # API on :5278Then run the customer app — see web/customer/README.md (npm install
&& npm start, served on :4200 and proxying /api to the API).
The repo path contains spaces, which breaks the EF CLI. If
dotnet eferrors, run it from a junction without spaces, e.g.mklink /J C:\tmp\tpx "<repo path>"then work fromC:\tmp\tpx.
docker compose up --build # postgres -> migrations -> API -> web (nginx)Web app on http://localhost:8080, API on http://localhost:5278. The compose Postgres is published on host port 5433 (so it never clashes with a PostgreSQL already on 5432). Containers, CI/CD, production config, and cloud deploy targets are documented in DEPLOYMENT.md.
Browse the database in your browser — two options:
- Prisma Studio (npm-based, works even when Docker image pulls fail) —
tools/db-studio→ http://localhost:5555. See tools/db-studio/README.md. - Adminer (containerized, opt-in):
docker compose up -d adminer→ http://localhost:8081 (System: PostgreSQL, Server:postgres, User/DB:transport, Password: yourPOSTGRES_PASSWORD).
On first run in Development the API seeds demo data so you can click through every role
immediately — sign in with password Demo!Passw0rd:
| Role | |
|---|---|
| Admin | admin@tpx.local |
| Vendor | vendor@tpx.local |
| Customer | customer@tpx.local |
It also creates an active Demo Lines company with a bus and three upcoming Damascus → Latakia trips, so customer search returns results out of the box. The seeder is idempotent and never runs outside Development (and is disabled during tests).
The Dependency Rule is enforced automatically by
tests/TransportPlatform.UnitTests/Architecture/LayeringTests.cs — the build fails if an inner
layer takes a dependency on an outer one. Add features as new handlers/implementations (Open/
Closed), depend on abstractions, and keep inner layers free of outer ones. Details in
ARCHITECTURE.md.
../ARCHITECTURE.md · ../SYSTEM_MAP.md · ../STACK_RECOMMENDATION.md