Flavio Espinoza
When I was at Vivint Solar, we were gearing up for our IPO, and Blackstone and Goldman Sachs basically took over our floor to vet the company. A key part of their due diligence relied on our sales, installation, power generation, and revenue analytics. The problem was our legacy reporting system. It pulled from multiple relational SQL databases and Salesforce, took over an hour to run a daily sales report, and just spit out a static HTML page, with no ability to export a CSV.
Our VP of Engineering made me the team lead of a three-person team of junior developers to completely re-architect this bottleneck. We built the new system from scratch using Angular on the front-end and Node for the authentication layer, microservices, and APIs. Our system interfaced with a new Elasticsearch data store -- a high-speed indexed cache the backend team built, fed by a JDBC architecture that continuously translated our relational data into near-real-time, searchable JSON.
The speed of Elasticsearch was unbelievable. It turned an hour-long wait for a query response into near real-time. This allowed us to build a dynamic, interactive pivot-table dashboard on the front-end. Executives and auditors could instantly slice and dice the data.
You could start at a macro level, like filtering the whole country down to just a single state, and then drill down into highly specific buckets. You could filter by sun hours, estimated kilowatt production, system sizes, or individual sales managers. You could instantly see which accounts were producing power versus those still waiting for final inspection from local building departments.
You could save any report and then copy the URL to the clipboard and send it via email. Anyone authorized and logged in to the platform would automatically see the report that was sent to them. You could also export any report to a CSV.
It became a definitive tool that told the company -- and our investors -- exactly how many installs we had and how much power was being generated across every Vivint Solar installation.
The Architecture
Mercury was a real-time sales-and-installation analytics platform for Vivint Solar -- and the first thing to understand is that Vivint Solar was a sales company, not a solar company and not a tech company. The numbers that mattered were installs, kilowatts generated, and revenue. It is a two-tier app: an Angular single-page front end -- the pivot dashboard, the sales leaderboard, and the saved reports my team built -- talking to a high-speed Elasticsearch store that the back-end/database team kept current from the company's relational SQL databases and Salesforce. The sections below follow the data: from ingestion, up through the faceted pivot dashboard and the leaderboard, to the save-and-share layer that turned a live pivot into a permanent emailable report, and the identity layer that scoped every view to the right person.
The Elasticsearch Data Store and JDBC Ingestion — The speed layer -- and, to be clear about ownership, the part the back-end/database team built, not my team. It is the foundation everything I built sat on.
- A.Indexed cache, not the system of record -- Elasticsearch held a denormalized, query-optimized copy of the data, so aggregations over the full dataset returned in near real time where the relational databases could not. The legacy report was slow precisely because it queried the relational sources live.
- B.JDBC ingestion -- a JDBC architecture continuously translated relational rows (and Salesforce data) into JSON documents and indexed them, so the store stayed current without anyone re-running an hour-long report.
- C.Incremental, not full reloads -- after a brutal first full population, the pipeline moved to small, frequent incremental syncs (down to roughly every five minutes), which is what made the dashboard feel live.
The Pivot Dashboard and Faceted Analytics — The front door, and the thing executives actually used. An Angular single-page app that turned the Elasticsearch store into an interactive pivot table: click to filter, and every chart recomputes instantly.
- A.No designer on the team -- there was no UX/UI designer, so the entire interface was mine. I designed the whole thing: the layout, the interaction model, and the color scheme. I owned how Mercury looked and felt, not just how it worked.
- B.Facets as buckets -- every dimension is a clickable "bucket" button; click one and it pins to the left rail and fires a fresh Elasticsearch query, so a user composes a view (country to state to city, by office, by sales manager) one click at a time, with no page reloads.
- C.The analytics dimensions -- installs broken down by sun hours, system size, estimated production, module count, customer age, and rate per kWh, each rendered as a D3 donut plus a histogram, filterable by state, city, and office.
- D.Operational truth -- a user could see at a glance which accounts were producing power versus which were still waiting on a final inspection from a local building department -- the difference between booked and billable.
- E.Custom D3, not a chart library -- the donuts, histograms, and rank tables were built from scratch in D3 so they could all redraw together off a single query result.
The Sales Leaderboard — Vivint Solar's sales force was door-to-door, and the leaderboard made performance a live scoreboard.
- A.Reps ranked "baseball-card" style -- each card showed active deals, installs, kW installed, and installs scheduled, ranked across the org.
- B.Drillable like everything else -- the same facet model applied, so a regional or district manager could filter the board down to their own people.
- C.Part of the IPO story -- showing not just totals but the depth and consistency of the sales engine was exactly the kind of thing the company put in front of investors.
Save and Share Reports — The win I am proudest of, and the one that shows how I think. The new (non-technical) CTO loved Mercury but wanted to save a pivot and send it to someone. At first that meant a developer hand-building each custom report and redeploying -- and deploys were painful back then (no CI/CD; some days we shipped three times just for this).
- A.The insight -- a pivot is not a screenshot, it is a JSON object. Every click was just building an Elasticsearch query in JSON. So "save this report" did not need a new deploy; it needed to persist that JSON. I put it to our VP of Engineering: "Couldn't we just save this on a MongoDB database?" His reply was, "I can't believe I didn't think of that."
- B.The build -- we added a Save button: name the report, write its query JSON to MongoDB, done. No redeploy, no developer in the loop. The thing that used to require a deploy now took a click.
- C.Share by link -- copy a saved report's link from a dropdown and email it; an authorized recipient opens Mercury straight to that report. The route carries no query parameters, so the platform resolves the saved query server-side instead of exposing it in the URL. Dallin, my lead developer, built that resolution layer.
- D.Why it is the story -- I am not a computer-science graduate; I trained as a building architect. The value I add is seeing the structure under a problem -- here, that "save this report" was really "persist this JSON" -- and that reframing turned a redeploy-every-time chore into a one-click feature.
Identity and Access Control — Every view in Mercury is scoped to the person looking at it. Getting that right was the difference between a sales tool and a data leak.
- A.Session-based auth -- login ran on Express and Passport.js (company email and password), with the authenticated session -- not the URL -- deciding what a user could see.
- B.Lessons learned from our parent company -- a backend developer who came over from Vivint Inc told us about how an internal user could reach records they were not entitled to by manipulating an identifier in the URL. We made sure ours was secure: identity is derived server-side from the session, and sensitive identifiers never ride in the URL.
- C.Scoped impersonation for support -- a deliberate, authorization-gated support tool let an admin view a specific rep's data (pick the rep from a dropdown, under your own login) to debug "my Hawaii sales are not showing up" issues for reps working across several states. It is a support feature run under the admin's own identity, not a way to become another user.
What this project shows — Mercury is the project where I learned that my edge is not the credential. I did not come up through a computer-science program -- I trained as a building architect -- and what I bring is the ability to see the structure under a problem and act on it. A partner at my first architecture firm once wrote that I "consider the broad ramifications of a project even while working on the smallest detail," and that is exactly the move that turned "save this report" into "persist this JSON," and an hour-long static report into a live, shareable analytics platform that helped take a company public. I led a small team of junior developers, taught myself Node in a weekend to do it, and owned the surface that executives, auditors, and investors actually touched.