# RentalHunt

A rental marketplace that connects property listings, applications, conversations, viewings, and paid access.

Development snapshot · September 2026

**Key idea:** The key boundary is a database commit: the application is recorded now; email and other provider work can finish later.

**Implementation:** Next.js · FastAPI · PostgreSQL · Firebase Auth · Redis

## The problem

A renter’s expression of interest needs to reach the right landlord, open the right conversation, and leave a clear record of what happens next. A lost response or failed email should not turn that into a half-created application.

## The approach

The web client sends an authenticated request to a shared application service. The service validates the current listing and saves the related product records together. External follow-up is processed after the database commit.

## Product state first. Follow-up work second.

![Product state first. Follow-up work second.](../diagrams/rentalhunt-overview.svg)

This view follows the core application path. PostgreSQL holds authoritative product state, Firebase supplies identity, and a separate outbox worker processes committed follow-up work. Redis supports selected entitlement reads; it is not the source of purchased access.

[Editable Mermaid source](../diagrams/rentalhunt-overview.mmd)

### Website and identity

The Next.js website organizes listing and application features. Firebase identifies the signed-in person; the backend resolves their product profile and checks whether they can perform the action.

### Shared domain service

FastAPI routes delegate to a shared application primitive used by manual, quick, and automated application paths. It checks current state under database locks instead of relying on what the browser previously displayed.

### Product database and outbox

The application, listing conversation, optional initial message, and follow-up intent are written within the same transaction. A tenant/listing uniqueness constraint backs up the duplicate check.

### Connected applications

Admin, sales, marketing, and a mobile prototype belong to the same RentalHunt system. Operator tooling combines backend commands with some direct product-data access; sales and communications also maintain their own operational records. They are not separate marketplace products.

## The relationship behind the duplicate boundary

![The relationship behind the duplicate boundary](../diagrams/rentalhunt-data.svg)

A renter profile can submit many applications, and a listing can receive many applications. The application table permits one row for each renter/listing pair. Conversations and follow-up events are coordinated by the application service.

[Editable Mermaid source](../diagrams/rentalhunt-data.mmd)

## From “apply” to a landlord notification

![From “apply” to a landlord notification](../diagrams/rentalhunt-sequence.svg)

The synchronous path confirms the application and conversation. A separate polling worker performs follow-up after commit. An optional viewing invitation depends on the listing’s current stage.

[Editable Mermaid source](../diagrams/rentalhunt-sequence.mmd)

### 1. Send the request

The website keeps a pending application intent and sends the listing, message, and consent alongside the current identity token. It preserves enough context to handle an uncertain response.

### 2. Check current authority

The backend verifies identity, resolves the renter profile, and checks the current listing and eligibility. It locks the relevant records before making the application decision.

### 3. Commit the related records

The service creates the application and ensures its listing conversation, adds an initial message when supplied, and records follow-up work. In the viewing stage, it can also create an invitation. These changes share one database transaction.

### 4. Confirm or reconcile

A successful reply contains application and conversation identifiers. After a timeout or duplicate conflict, the browser re-reads authenticated listing state to determine whether the application already exists before offering another attempt.

### 5. Deliver after commit

An independent worker polls committed outbox records, claims due work, and creates notifications or requests email delivery. Recording the follow-up intent is distinct from successful provider delivery.

## Implementation details

### A timeout does not tell the whole story

The server may have committed even when the browser did not receive its reply. The client therefore treats an ambiguous response as something to reconcile with server state. Database uniqueness still provides a final boundary if concurrent requests arrive.

### Provider acceptance is a separate state

External notification delivery cannot be part of the product database transaction. The worker tracks attempts and outcomes, and ambiguous provider responses require review and reconciliation rather than blind resending. This narrows duplicate risk without claiming exactly-once delivery.

### Payment success and activated access differ

Checkout saves a pending purchase before making a synchronous Stripe request. Signed provider events arrive independently and activate the local purchase. The website checks RentalHunt’s own purchase state before announcing that access is ready.

### Caching follows the access boundary

Selected feature checks use Redis with scoped keys and expiry tied to the purchase. Fresh reads can bypass the cache. The database remains authoritative; this case study does not claim that listing search is served from Redis.

### Background work has more than one lane

The general application outbox is a separate database-polling process. Billing has its own effect and reconciliation workers, while other scheduled assistance uses Celery. These responsibilities should not be collapsed into a single generic queue.

## Engineering decisions and tradeoffs

### Commit the application graph together

Related product records and follow-up intent are saved through one shared transaction, with database constraints for the important uniqueness boundary.

**Tradeoff:** The local workflow can stay consistent even when email is unavailable. Delivery becomes deferred work that needs workers, retry states, and operational visibility.

### Recover state after uncertain delivery

The browser reconciles application state after ambiguous responses, while checkout and provider work keep durable identities for repeated attempts.

**Tradeoff:** The system can distinguish some retries from new work, but it needs more state transitions and a review path for outcomes it cannot prove.

### Cache selected reads, retain database authority

Entitlement checks use scoped, expiring cache entries and support a fresh database path.

**Tradeoff:** This can reduce repeated reads, but cache invalidation and purchase expiry still need careful handling. It does not remove the database from the access decision.

## What the implementation demonstrates

- The inspected code joins application creation, conversation setup, and deferred follow-up in one product workflow.
- Source tests cover rollback, duplicate submissions, uncertain client outcomes, and provider-event handling. Those tests were inspected for this case study, not executed against a live RentalHunt environment.

## Current limitations

- The reviewed development trees include recent work; this snapshot does not establish which features are currently deployed.
- The mobile prototype does not yet demonstrate full contract parity with the current web/backend application flow.
- External delivery and payment activation depend on worker processes and provider configuration that source review alone cannot verify.

## Potential improvements

These are proposals, not implemented features or measured outcomes.

- Add release and contribution annotations tying each engineering story to the version that shipped.
- Validate mobile/API contracts and rehearse worker failure recovery in an isolated integration environment.

---

This case study describes an inspected source snapshot. It does not establish current deployment, adoption, provider operation, or individual ownership. Metrics and historical comparisons are included only when supported by evidence.
