A web application that transforms paper-based rental property expense tracking into organized, tax-ready Schedule E reports.
- Frontend: Angular 21 + @ngrx/signals + Angular Material
- Backend: .NET 10 + ASP.NET Core + Clean Architecture + CQRS/MediatR
- Database: PostgreSQL 16 + EF Core 10
- API Client Generation: NSwag
- .NET 10 SDK
- Node.js 22 LTS
- Docker Desktop
- Angular CLI (
npm install -g @angular/cli)
# Start PostgreSQL and MailHog
docker compose up -d db mailhogcd backend
dotnet restore
dotnet ef database update --project src/PropertyManager.Infrastructure --startup-project src/PropertyManager.Api
dotnet run --project src/PropertyManager.ApiAPI will be available at: http://localhost:5292 Swagger UI: http://localhost:5292/swagger
cd frontend
npm install
ng serveWeb app will be available at: http://localhost:4200
| Service | URL |
|---|---|
| Web App | http://localhost:4200 |
| API | http://localhost:5292 |
| Swagger | http://localhost:5292/swagger |
| MailHog | http://localhost:8025 |
| PostgreSQL | localhost:5432 |
property-manager/
├── backend/
│ ├── src/
│ │ ├── PropertyManager.Domain/ # Entities, value objects, interfaces
│ │ ├── PropertyManager.Application/ # Commands, queries, handlers
│ │ ├── PropertyManager.Infrastructure/# EF Core, persistence, identity
│ │ └── PropertyManager.Api/ # Controllers, middleware
│ ├── tests/
│ │ ├── PropertyManager.Domain.Tests/
│ │ ├── PropertyManager.Application.Tests/
│ │ └── PropertyManager.Api.Tests/
│ ├── Dockerfile
│ └── PropertyManager.sln
├── frontend/
│ ├── src/
│ │ ├── app/
│ │ │ ├── core/ # Auth, API services
│ │ │ ├── shared/ # Reusable components
│ │ │ └── features/ # Feature modules
│ │ ├── styles/
│ │ └── assets/
│ ├── e2e/
│ ├── Dockerfile
│ └── angular.json
├── postman/
│ ├── PropertyManager.postman_collection.json
│ └── environments/
├── docker-compose.yml
└── README.md
cd frontend
npm run generate-api# Backend tests
cd backend
dotnet test
# Frontend unit tests
cd frontend
npm test
# Frontend E2E tests (requires full stack running)
npm run test:e2eE2E tests use Playwright and require the full stack running (API, database, MailHog).
# Start infrastructure
docker compose up -d db mailhog
# Start backend (in another terminal)
cd backend
dotnet run --project src/PropertyManager.Api
# Run E2E tests (in another terminal)
cd frontend
npm run test:e2e
# Run with UI mode (for debugging)
npm run test:e2e:ui
# View HTML report after test run
npm run test:e2e:reportfrontend/e2e/
fixtures/ # Custom Playwright fixtures
helpers/ # MailHog API, auth helpers
pages/ # Page Object Model classes
tests/ # Test specifications
E2E tests run automatically in CI on pull requests after unit tests pass. Failed tests produce HTML reports and trace files uploaded as GitHub artifacts.
cd backend
# Create migration
dotnet ef migrations add <MigrationName> --project src/PropertyManager.Infrastructure --startup-project src/PropertyManager.Api
# Apply migrations
dotnet ef database update --project src/PropertyManager.Infrastructure --startup-project src/PropertyManager.ApiThis project uses GitHub Actions for continuous integration and deployment:
-
CI Pipeline (
.github/workflows/ci.yml): Triggered on pull requests tomain- Builds and tests backend (.NET 10)
- Builds and tests frontend (Angular with Vitest)
- Verifies Docker image builds
-
CD Pipeline (
.github/workflows/cd.yml): Triggered on push tomain- Runs CI checks first
- Builds and pushes Docker images to GitHub Container Registry
- Deploys to Render via webhook
-
Using Render Blueprint (Recommended):
- Click "New" → "Blueprint" in Render dashboard
- Connect your GitHub repository
- Render will automatically detect
render.yamland create services
-
Manual Setup:
- Create a Web Service for the API (Docker,
./backend) - Create a Static Site for the frontend (
./frontend) - Create a PostgreSQL database
- Configure environment variables (see below)
- Create a Web Service for the API (Docker,
| Variable | Description | Required |
|---|---|---|
ConnectionStrings__Default |
PostgreSQL connection string | Yes |
Jwt__Secret |
256-bit secret for JWT signing | Yes |
Jwt__Issuer |
JWT issuer URL | Yes |
Jwt__Audience |
JWT audience URL | Yes |
Jwt__ExpiryMinutes |
Token expiry (default: 60) | No |
Email__Provider |
Smtp or SendGrid |
Yes |
Email__SmtpHost |
SMTP server host | If using SMTP |
Email__SmtpPort |
SMTP server port | If using SMTP |
Email__FromAddress |
Sender email address | Yes |
Email__FromName |
Sender display name | No |
AWS__AccessKeyId |
AWS IAM access key for S3 | For file storage |
AWS__SecretAccessKey |
AWS IAM secret key | For file storage |
AWS__BucketName |
S3 bucket name | For file storage |
AWS__Region |
AWS region (default: us-east-1) | No |
AWS__PresignedUrlExpiryMinutes |
URL expiry (default: 60) | No |
ASPNETCORE_ENVIRONMENT |
Production |
Yes |
The application uses AWS S3 for storing:
- Receipt images
- Property photos (with auto-generated thumbnails)
- Generated tax reports (PDF/ZIP)
S3 Configuration: Create an S3 bucket and IAM user with these permissions:
s3:PutObject,s3:GetObject,s3:DeleteObject
Local Development: S3 is optional for local development. When AWS credentials are not configured, the system uses NoOp implementations that log operations but don't persist files.
- Basic Health:
GET /api/v1/health- Returns 200 if API is running - Readiness:
GET /api/v1/health/ready- Returns 200 if database is connected
In production, EF Core migrations run automatically on application startup. If migrations fail, the application will not start (fail-safe behavior).
-
Via Render Dashboard:
- Navigate to your service
- Click "Rollback" and select a previous deploy
-
Via Git:
- Revert the commit causing issues
- Push to
mainbranch - CD pipeline will deploy the reverted version
# Test with production-like settings
docker compose -f docker-compose.yml -f docker-compose.prod.yml up --build- Database connection failed: Check
ConnectionStrings__Defaultformat - JWT validation failed: Ensure
Jwt__Secretis at least 32 characters - CORS errors: Verify
Jwt__Audiencematches your frontend URL - Email not sending: Check SMTP credentials and port settings
- Render: Navigate to your service → "Logs" tab
- Local Docker:
docker compose logs -f api
Private - All rights reserved.