A project by Dylan

BridgeSphere

Real estate networking platform MVP, contracted to architect whole platform and infrastructure. Primary full-stack engineer from graduate hire to 1.0 launch. (All visuals contain demo data)

BridgeSphere project preview
Project still · BridgeSphere

Role

Primary full-stack engineer

Released

Stack

  • React
  • TypeScript
  • ASP.NET Core 8
  • PostgreSQL
  • Mapbox
  • Azure

See it

The Scenario

A product owner approached me after his real-estate networking project had stalled. The previous developer had left five weeks in with only some wireframes and a few technical documents completed, I came on as the sole engineer on a contract basis. I spent over one year building the application from the ground up: architecture, every line of production code, the roadmap, and day-to-day iteration directly with the founder.

The Role

As a recent grad, this was the deep end: I was responsible for the whole platform.

  • Architecture design and system diagramming
  • Frontend (React SPA) and backend (ASP.NET Core API)
  • Database schema, indexing, geospatial queries, and vector search
  • Matching pipeline design and optimization
  • Authentication, security, and role-based access control
  • Infrastructure, CI/CD, and production operations
  • Requirements gathering, sprint planning, and client demos
  • Direct collaboration with the founder on product direction

What I Built

A monorepo: a React SPA on Azure Static Web Apps, an ASP.NET Core 8 API on App Service, PostgreSQL with PostGIS and pgvector, Mapbox for maps, a FastAPI embedding sidecar, and an Azure Functions image pipeline. Environments promote through Dev, QA, and Prod.

Architecture diagram of the BridgeSphere system

React SPA → App Service API → PostgreSQL/PostGIS/pgvector, with Mapbox, SendGrid, a FastAPI embedding service, and an Azure Functions image pipeline as satellite services, all shipped through a Dev → QA → Prod pipeline.

Matching

Problem:

I had built a working version of our networking feature. In the beginning, I put most of the location filtering in-memory in the C# layer. This meant that match requests recomputed too much per call: geo candidates, scoring, and embedding similarity. All at once. Under realistic profile volumes, lists felt slow, and repeated dashboard loads made it worse. A full recompute on every request was the wrong default.

BridgeSphere hybrid match pipeline: criteria, geo filter, scorers, semantic embeddings, then rank and cache
Public view of the match pipeline. Weights and score breakdowns stay off the page under NDA.

Constraints

Keeping the data fresh was critical for user trust and establishing real-time data for agents to use throughout their day. Agents, Clients, and Brokerages may change service zones and preferences often enough that a stale list will harm trust and user experience. Caching the data without a clearly defined invalidation story was showing incorrect matches, based on stale data. Prior to implementing PostGIS, there was a N+1 problem in the code, where all eligble matches within a large radius were called at once. Then scorers and rank/cache would run across this massive data set. I used the haversine formula for all matches within 300km of the primary location of the user, these bugs were causing fetch requests to take up to 20 seconds to match with 50+ paginated agents.

Approach

I treated matching as a pipeline with an expensive tail, and so it was best to optimize the tail-end of the operation:

  • Ran geo filtering early by moving calculations from C# level to PostGIS in PostgreSQL. This caused later stages to work on a much smaller set of data.
  • Ran scoring and semantic similarity only on that filtered set.
  • Cached ranked lists in memory, keyed by requester and match direction.
  • Invalidated the cache on profile and location writes, so edits surfaced on the next meaningful read.
  • Added stricter rate limiting on match endpoints in production so retries and abuse couldn’t amplify cost.

The embedding service ran as a separate FastAPI sidecar rather than inside the .NET API. That kept the main service focused, but it also meant cold starts and timeouts were real failure modes I had to design around in the match path.

Result

Match lists became usable for everyday dashboard traffic, and production held up under normal load. Latency is still an honest constraint at scale: better async refresh and smarter caching are the next levers if the user base grows.

What I’d do next

Move more of the heavy recompute off the request path entirely (queue or background refresh), tighten cache keys around the specific fields that actually change scores, and add load testing (k6) as a release gate on match endpoints.

Hard Parts (there were many)

  • Learning how to handle a multi-role surface. One codebase for clients, agents, and brokerages. I had to catch myself everytime I started repeating myself for each role. The process of organizing the app into 3 unique roles and ensuring there was no crossover ended up being weeks and weeks of research, trial and error, and coffee. Shared primitives (auth, maps, cards, save/unsave) had to stay consistent while each role’s path felt complete.

  • Mapping was a core feature, not simply decoration. The networking feature required a smart location system that could filter out matches based on location. This needed to be both customizable and reliable. I spent hours reading the MapBox documentation.
    Agents/Brokerages draw service zones; clients mark preferred areas. Geography actively drives who shows up in a match list, not just where a pin sits on a map.

  • API iteration and maintainability during requirement evolution. I chose to use the “Strangler Fig Pattern” to slowly migrate toward a cleaner v3 API surface while keeping v2 write paths alive, so production never broke mid-migration.

Tradeoffs

Decision Tradeoff Justification
Hybrid matching + cache Latency vs data freshness Full recompute on every request was expensive and slow
Dual v2/v3 APIs Complexity vs Continuity I determined it wasn’t feasible to re-write the entire API at once.
Separate embedding sidecar Extra ops Kept the .NET API focused and RESTful.

Reflection

Besides having to learn on the go every day, the biggest lesson I took was that production itself is a feature. Knowing what not to expose in the UI, secrets management, branch promotion, DNS, and ops were just as critical as anything else.

I went from limited professional experience to owning and engineering a full-stack, multi-environment system end-to-end. This includes all the unglamorous work that separates a basic demo from a real, bonafide product. I learned so much throughout the process about myself, my problem-solving, what makes good project management, and much much more. I made many mistakes and the app is certainly far from perfect, though the fact that the entire app works is pretty impressive.