Where The Heart Is - MVP Plan
Project Overview
A transparent mortgage calculator that reveals the hidden financial impact of homebuying decisions. Unlike typical calculators that focus on monthly payment affordability, this tool shows exactly how much interest you'll pay over a loan's lifetime, how a 1% rate difference changes your total cost, and how PMI affects your bottom line.
Core Value: Answer "what does this house actually cost me over 30 years?" instead of "can I afford the monthly payment?"
Original Project: Mike-Bros/WhereTheHeartIs
Tech Stack
| Component | Technology | Purpose |
|---|---|---|
| Backend | Go + Gin | REST API server |
| Database | PostgreSQL | Single database for calculations |
| Cache | Valkey (optional) | Performance optimization |
| Frontend | React + Next.js + TypeScript | Modern web UI |
| UI Components | Shadcn/ui + Tailwind | Component library |
| Dev Environment | Docker Compose | Local development |
Key Libraries:
- Backend:
gin-gonic/gin,jackc/pgx/v5,valkey-io/valkey-go - Frontend: TanStack Query, React Hook Form, Zod, Recharts
Core Features
| Feature | Description | Notes |
|---|---|---|
| Mortgage Calculator | Input home details, get transparent cost breakdown | Real-time calculation as you type |
| "The Hidden Truth" | Prominently show total interest, PMI costs, rate comparisons | What typical calculators hide |
| Real-time Updates | Results recalculate as you type (500ms debounce) | No page refresh needed |
| Pin & Compare | Save calculation snapshots for side-by-side comparison | Client-side state only (no persistence) |
| Rate Range Visualization | Chart showing monthly payment across interest rate range | Interactive chart with tooltips |
| Extra Payment Scenarios | Model impact of additional monthly/yearly payments | Shows years saved and interest savings |
Out of Scope for MVP:
- User authentication
- Saved calculations (persistence)
- Event streaming (Kafka)
- Multiple services (microservices)
- Analytics tracking
Architecture
graph TB
subgraph Frontend
NX[Next.js<br/>TypeScript + Shadcn]
end
subgraph Backend
API[Go API<br/>Gin Router]
end
subgraph Data Layer
PG[(PostgreSQL<br/>Calculations)]
CACHE[(Valkey<br/>Optional Cache)]
end
NX -->|REST API| API
API -->|Read/Write| PG
API -->|Cache| CACHE
API Endpoints:
POST /api/v1/calculate- Calculate mortgage paymentGET /api/v1/calculations/:id- Get calculation by IDGET /health- Health check
Data Model
Calculation Inputs:
| Field | Type | Validation | Default |
|---|---|---|---|
| Home Price | Currency | ≥ $1 | - |
| Down Payment | Percentage | 0-100% | 20% |
| Loan Term | Years | ≥ 1 | 30 |
| Interest Rate | Percentage | ≥ 0% | 3.5% |
| Cash Savings | Currency | ≥ $0 | - |
| Moving Costs | Currency | ≥ $0 | $2,000 |
| Closing Costs | Percentage | 0-100% | 3% |
| PMI Rate | Percentage | 0.3-1.5% | 0.5% (if < 20% down) |
| Extra Monthly Payment | Currency | ≥ $0 | $0 |
| Extra Yearly Payment | Currency | ≥ $0 | $0 |
Calculation Outputs:
| Output | Description | Display Type |
|---|---|---|
| Monthly Payment | Principal + Interest | Metric (card) |
| Monthly PMI | PMI payment (if down payment < 20%) | Metric (card) |
| Total Monthly Payment | Payment + PMI | Metric (prominent) |
| Total Interest Paid | Lifetime interest cost | Metric (prominent) |
| Interest at Rate -1% | Savings with better rate | Comparison metric |
| Total PMI Cost | Lifetime PMI cost | Metric (highlighted if >$0) |
| PMI End Date | When PMI stops (~10 years) | Metric (date) |
| Down Payment Amount | Cash for down payment | Metric (card) |
| Loan Amount | Amount borrowed | Metric (card) |
| Closing Costs | Upfront closing costs | Metric (card) |
| Remaining Cash | Cash left after all costs | Metric (card) |
| Payoff Time (with extra payments) | Years to fully pay off loan | Comparison metric |
| Interest Saved (with extra payments) | Savings vs. standard payment schedule | Comparison metric (prominent) |
| Equivalent Rate | Effective interest rate with extra payments | Comparison metric |
| Payment Schedule | Month-by-month breakdown of principal, interest, balance | Chart (line/table) |
| Rate Comparison | Monthly payment across interest rate range | Chart (line) |
Mortgage Formulas
Monthly Payment (M):
M = P * [r(1 + r)^n] / [(1 + r)^n - 1]
Where:
P = Principal loan amount (home price - down payment)
r = Monthly interest rate (annual rate / 12 / 100)
n = Total number of payments (loan term in years * 12)
PMI Calculation:
Monthly PMI = (Loan amount × PMI rate) / 12
PMI applies when: Down payment % < 20%
PMI ends when: Loan balance reaches 78% of original home value (typically ~10 years)
Total PMI paid = Monthly PMI × Months until PMI ends
Derived Calculations:
- Down payment = Home price × (Down payment % / 100)
- Loan amount = Home price - Down payment
- Closing costs = Home price × (Closing cost % / 100)
- Total monthly payment = Monthly payment + Monthly PMI
- Total payment = (Monthly payment × Number of payments) + Total PMI paid
- Total interest = Total payment - Loan amount - Total PMI paid
- Remaining cash = Cash savings - Down payment - Closing costs - Moving cost
Extra Payment Calculations (The True Impact):
When extra payments are made:
1. Calculate standard amortization schedule
2. Apply extra payments each period:
- Extra monthly payment added to principal each month
- Extra yearly payment added once per year (typically month 12)
3. Recalculate remaining balance after each payment
4. Loan is paid off when balance reaches $0
5. Interest saved = Standard total interest - Actual total interest paid
6. Years saved = Standard term - Actual payoff time
Equivalent Rate: The interest rate that would give the same monthly
payment without extra payments (reverse calculation)
Note on PMI with Extra Payments:
- PMI ends when loan-to-value ratio reaches 78% (based on current balance)
- Extra payments accelerate reaching this threshold
- Must recalculate PMI end date in amortization schedule
Comparison Calculations (The Hidden Truth):
- Interest saved with 1% lower rate = Current total interest - (total interest at rate - 1%)
- PMI impact = Total PMI paid (shown prominently if applicable)
- Extra payment impact = Interest saved + Years saved (shown prominently if extra payments > 0)
Implementation Milestones
Milestone 1: Project Setup
- Initialize Go project with Gin router structure
- Initialize Next.js project with TypeScript
- Set up Docker Compose (PostgreSQL, Valkey, backend, frontend)
- Configure environment variables and verify local setup
Milestone 2: Backend - Calculation API
- Implement standard mortgage calculation logic with unit tests
- Implement PMI calculation (when it applies, when it ends)
- Implement extra payment amortization schedule calculation
- Create
/api/v1/calculateendpoint with input validation - Add database schema and migration for calculations table
- Implement Valkey caching layer (optional)
- Test API endpoints with curl/Postman (including extra payment scenarios)
Milestone 3: Frontend - Calculator UI
- Create calculator form with all input fields (including extra payments)
- Implement real-time calculation with 500ms debounce
- Create result display component showing "The Hidden Truth"
- Display extra payment impact (years saved, interest saved) when applicable
- Add input validation and error handling
- Connect to backend API with TanStack Query
Milestone 4: Pin & Compare Feature
- Implement client-side state management for pinned results
- Create pinned results display component (side-by-side cards)
- Add "Pin Result" and remove functionality
- Implement responsive layout for comparison view
Milestone 5: Visualization & Polish
- Create rate range chart with Recharts
- Improve mobile responsiveness
- Add loading states and skeletons
- Polish UI with consistent styling
- End-to-end testing of user workflows
Project Structure
where-the-heart-is/ # Monorepo root
├── backend/
│ ├── cmd/
│ │ └── api/
│ │ └── main.go # Application entry point
│ ├── internal/
│ │ ├── handler/ # HTTP handlers (Gin)
│ │ │ ├── calculate.go
│ │ │ ├── health.go
│ │ │ └── middleware/ # CORS, logging
│ │ ├── service/ # Business logic
│ │ │ ├── calculator.go # Mortgage calculation
│ │ │ └── cache.go # Cache management
│ │ ├── repository/ # Data access
│ │ │ ├── postgres.go
│ │ │ └── valkey.go
│ │ ├── model/ # Domain models
│ │ │ └── calculation.go
│ │ └── config/ # Configuration
│ │ └── config.go
│ ├── migrations/ # SQL migrations
│ ├── go.mod
│ └── Dockerfile
├── frontend/
│ ├── app/ # Next.js App Router
│ │ ├── layout.tsx
│ │ └── page.tsx # Calculator page
│ ├── components/
│ │ ├── calculator/
│ │ │ ├── calculator-form.tsx
│ │ │ ├── result-card.tsx
│ │ │ └── pinned-results.tsx
│ │ └── ui/ # Shadcn components
│ ├── lib/
│ │ ├── api/
│ │ │ ├── client.ts # Axios instance
│ │ │ └── calculator.ts # API calls
│ │ ├── hooks/
│ │ │ ├── use-calculator.ts
│ │ │ └── use-debounce.ts
│ │ ├── schemas/
│ │ │ └── calculation.ts # Zod schemas
│ │ └── utils/
│ │ └── formatters.ts # Currency formatting
│ ├── package.json
│ └── next.config.js
├── docker-compose.yml # Local development
├── Makefile # Common tasks (migrate, seed, test)
└── README.md
Success Criteria
MVP is complete when:
- Calculator accepts all required inputs with real-time validation
- Mortgage calculations are mathematically correct (verified by unit tests)
- Results update in real-time without page refresh (500ms debounce)
- Users can pin and compare up to 5 scenarios side-by-side
- Rate range visualization chart works correctly
- UI is clean, intuitive, and mobile-responsive
- Application runs locally via Docker Compose
Key Differentiators:
- The "Hidden Truth" section prominently shows total interest paid, PMI costs, and rate comparison savings
- Extra payment modeling shows the true impact of paying extra because most calculators don't do this well or at all
- All hidden costs and savings are transparent and easy to compare
Document Version: 1.0 (MVP Scope)
Last Updated: 2025-10-17
Status: Ready for Implementation