Fractional Property Investment Advisor - UAE Real Estate
A production-grade .NET 10 backend that helps fractional property investors answer "What should I do next, and why?"
Given an investor's goals, risk appetite, time horizon, and existing holdings, NestPulse scores available properties across 5 weighted factors, recommends optimal allocations, projects returns over 1 to 10 years, and flags when a portfolio is ready for a full mortgage.
Built to demonstrate senior backend engineering capabilities in the proptech and fintech space; clean architecture, SOLID principles, RESTful API design, and Azure-ready infrastructure.
Fractional property investment platforms let investors own portions of UAE properties from as little as AED 500. As a portfolio grows across multiple properties, most platforms show investors what they have — but not what they should do next.
NestPulse is the backend engine that fills that gap. It analyses an investor's current holdings and available capital, scores all available properties for suitability, and recommends the optimal next move - with full reasoning broken down by factor.
- NestPulse.Domain → Entities, Value Objects, Enums, Repository Interfaces
- NestPulse.Application → DTOs, Service Interfaces, Scoring Engine, Calculators, Validators
- NestPulse.Infrastructure → Repositories, Cache, External API Client, Enrichment Service, DI
- NestPulse.API → Controllers, Middleware, Auth, Program.cs
- NestPulse.Infrastructure.Tests → Unit tests - PropertyEnrichmentServiceTest
- NestPulse.Application.Tests → Unit tests - Scoring, Calculator, Recommendation, Portfolio, Property, Services
Dependency flow is strictly inward - Domain has zero external dependencies. Infrastructure implements Domain interfaces - the Application layer never knows what's behind them.
| Decision | Approach | Why |
|---|---|---|
| Architecture | Clean Architecture | Separation of concerns, independently testable layers |
| Repository pattern | IPropertyRepository interface |
Swap seed data ↔ live API with zero application changes |
| Cache abstraction | ICacheService interface |
Swap in-memory ↔ Redis via config, no code changes |
| Validation | Custom ValidationException |
Collects ALL field errors before throwing - never stops at first failure |
| Error handling | GlobalExceptionMiddleware |
Consistent JSON error responses, structured logging, no stack trace leakage |
| Authentication | JWT Bearer | Stateless, Azure AD B2C ready - swap DemoUsers for B2C with config change only |
| Logging | Serilog | Structured logs to console + rolling daily files, request logging middleware |
| External API | Provider-agnostic naming | PropertyApiClient, PropertyRepository - External URL lives in config only |
| Enrichment | PropertyEnrichmentService |
Transforms raw API listings into domain entities using Dubai rental index benchmarks |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| POST | /api/v1/auth/token |
None | Get JWT bearer token |
| GET | /api/v1/properties |
Bearer | All available properties |
| POST | /api/v1/portfolio/analyse |
Bearer | Analyse existing portfolio |
| POST | /api/v1/portfolio/recommend |
Bearer | Get next investment recommendation |
Swagger UI available at / when running locally.
Each property is scored out of 100 across 5 weighted factors:
| Factor | Weight | What it measures |
|---|---|---|
| Yield Alignment | 35% | Does the yield match the investor's income/growth goal? |
| Risk Tag Match | 25% | Does the property risk tier fit the investor's appetite? |
| Time Horizon Fit | 20% | Does the holding period suit when returns materialise? |
| Funding Availability | 10% | Is there enough funding left to invest meaningfully? |
| Diversification Benefit | 10% | Does this property add a new area to the portfolio? |
Seed data (default): 10 properties modelled on publicly available UAE listings with yield figures derived from the Dubai rental index.
Live API (optional): External property listings API returning real UAE for-sale
properties. Enriched with area-level yield benchmarks via PropertyEnrichmentService.
To switch - one config change, zero code changes:
// appsettings.json
"UseExternalPropertyApi": true,
"PropertyApi": {
"BaseUrl": "https://your-api-url",
"ApiKey": "your-key",
"ApiHost": "your-host"
}| Data | Cache Key | TTL |
|---|---|---|
| Property listings | properties:all |
1 hour |
| Recommendation results | Hash of input profile | 15 minutes |
Currently running in-memory cache via InMemoryCacheService.
RedisCacheService is fully implemented and ready - swap registration in DependencyInjection.cs
by setting ConnectionStrings:Redis in config. Zero application layer changes required.
JWT Bearer token authentication. Demo credentials for testing:
| Password | |
|---|---|
| demo@nestpulse.com | demo123 |
| investor@nestpulse.com | invest123 |
Production path: Replace DemoUsers with Azure AD B2C - three config lines,
zero controller changes. The [Authorize] attributes stay exactly as they are.
git clone https://github.com/yourusername/nestpulse.git
cd nestpulse
dotnet restore
dotnet run --project NestPulse.APINavigate to https://localhost:{port} - Swagger UI loads at root.
Get a token first:
POST /api/v1/auth/token
{
"email": "demo@nestpulse.com",
"password": "demo123"
}Click Authorize in Swagger, paste the token, then call any endpoint.
dotnet testTest coverage includes:
| Test Class | Coverage |
|---|---|
PortfolioServiceTests |
Analyse portfolio |
PropertyServiceTests |
Analyse get property, with and without cache |
SuitabilityScorerServiceTests |
All 5 scoring components, all combinations |
PortfolioCalculatorServiceTests |
Blended yield, diversification, projections, crossover, allocation |
PropertyEnrichmentServiceTests |
Area extraction, yield lookup, risk tag derivation, full enrichment |
RecommendationServiceTests |
End-to-end recommendation flow with mocked dependencies |
PropertyEnrichmentServiceTests |
Test all enrichment methods |
POST /api/v1/portfolio/recommend
Authorization: Bearer {token}
{
"profile": {
"existingHoldings": [
{
"propertyId": "prop-001",
"propertyName": "Studio in Bayz Tower",
"location": "Business Bay, Dubai",
"area": "Business Bay",
"amountInvested": 5000,
"grossYieldPercent": 7.72,
"oneYearReturnPercent": 10.61
}
],
"availableAmount": 5000,
"goal": 1,
"riskAppetite": 2,
"timeHorizon": 3
}
}Enum values:
| Field | Values |
|---|---|
goal |
1=PassiveIncome, 2=CapitalGrowth, 3=Both |
riskAppetite |
1=Conservative, 2=Balanced, 3=Aggressive |
timeHorizon |
1=OneYear, 3=ThreeYears, 5=FiveYears, 10=TenYears |
- Clean Architecture scaffold
- Domain entities and value objects
- Suitability scoring engine
- Portfolio analysis and projections
- Mortgage crossover logic
- External property API integration
- Property enrichment service
- JWT authentication
- Global exception handling
- Structured logging with Serilog
- In-memory cache with Redis interface ready
- Docker + docker-compose
- Azure App Service deployment
- Azure Key Vault integration
- Azure AD B2C authentication
- React frontend
| Layer | Technology |
|---|---|
| Runtime | .NET 8, ASP.NET Core Web API |
| Architecture | Clean Architecture, SOLID, DI |
| Testing | xUnit, Moq, FluentAssertions |
| Logging | Serilog — Console + rolling file sinks |
| Auth | JWT Bearer — Azure AD B2C ready |
| Caching | In-memory — Redis interface implemented |
| API Docs | Swagger / Swashbuckle |
| External Data | Provider-agnostic property listings API |