The Reporting Layer: A SaaS Story of Patience and OLAP
A reporting layer is the part of the system that answers questions about data in aggregate: how many users did X last month, what is the revenue trend by cohort, which customers are approaching a usage limit. It is separate from the transactional layer because the query shapes are different and the two layers have different scaling needs. Most SaaS products bolt reporting onto the OLTP database until it hurts, then migrate under pressure.
Written by Yashveer Singh, founder of Yashveer Labs.
What you actually need to know
- Reporting queries and transactional queries have different shapes. They should not share the same database under the same load.
- A read replica buys time. A proper analytical store (data warehouse or column-store) is the destination.
- Materialized views and scheduled aggregation are the right tools for most SaaS reporting use cases.
- Customer-facing reports and internal dashboards can share the same analytical infrastructure.
- In my experience, the teams that plan the reporting layer early spend two weeks building it. The teams that ignore it spend six weeks fixing performance incidents caused by it.
| Option | Best for | Scale ceiling | Latency | Operational tax |
|---|---|---|---|---|
| OLTP read replica | Simple reports, early stage | Moderate query complexity | Near real-time | Low |
| BigQuery | Large volumes, event data | Essentially unlimited | Minutes to near real-time | Low (managed) |
| Snowflake | Enterprise analytics, complex queries | Essentially unlimited | Minutes | Low (managed) |
| ClickHouse (self-hosted) | High-frequency event data | Very high | Sub-second | Medium |
| Redshift | AWS-native teams | High | Minutes | Medium |
| DuckDB (embedded) | Small-scale, developer tools | Moderate | Sub-second | Very low |
The core argument
The reporting layer is the part of the system that answers "how many" and "what changed." It is separate from the part that answers "what is the current state." The difference matters because aggregate queries and point-in-time reads want different things from their database. Row-level reads want indexes on primary keys and foreign keys. Aggregate queries want column-oriented storage, partition pruning, and full table scans on specific columns. Optimizing for one makes the other worse.
Most SaaS teams ignore this for as long as possible. The early product has a few reports that run fast because the database is small. The reports get slower as the database grows. The team adds indexes. The indexes slow down writes. The team adds a read replica. The read replica delays reporting queries but removes them from the write path. Eventually the reports are still slow, the read replica is under heavy load, and the team is facing a real migration.
The teams that plan ahead add an analytical store when the signs appear, not after the incident. They use change data capture or scheduled ETL to keep the analytical store in sync. They route reporting queries to it. The production database stays fast. The reporting layer scales independently.
This is not a complex architecture. The complexity is in doing it before it is painful rather than after. That requires a founder or a senior engineer who has seen this story before and knows how it ends.
The layers of a reporting system
The data store
The source of truth for reporting queries. For most B2B SaaS at growth stage, a managed data warehouse is the right call. BigQuery and Snowflake are the dominant options. Both are serverless, meaning you pay for queries not for uptime. Both handle the scale of most SaaS products without any operational work.
For products with high-frequency event data (millions of events per day), a column-store like ClickHouse is worth considering. It is faster for event-pattern queries and cheaper at volume. The operational overhead of self-hosted ClickHouse is real, though ClickHouse Cloud removes most of it.
The sync layer
Data has to get from the production database to the analytical store. Change data capture is the production-grade solution. Tools like Debezium, Airbyte, or Fivetran stream changes in near-real time. ETL batch jobs are simpler to set up and good enough when a few hours of lag is acceptable.
The sync layer is where teams make decisions they regret. Running ad hoc scripts to sync data works until the production database schema changes and the script silently starts missing rows. The sync layer needs to be schema-aware and tested.
The query and visualization layer
Metabase, Redash, Grafana, and Looker all sit at this layer. They connect to the analytical store and provide the interface for building reports, dashboards, and customer-facing views. For internal dashboards, Metabase is the most common choice at growth stage. For customer-facing reports, a custom interface that queries the analytical store through a controlled API is more appropriate.
How much does it cost
| Component | Setup time | Monthly cost at moderate scale |
|---|---|---|
| Postgres read replica | Hours | 20 to 100 USD on managed Postgres |
| BigQuery | Half a day | 5 to 50 USD for most early-stage queries |
| Snowflake (starter) | Half a day | 25 to 200 USD |
| Fivetran or Airbyte (sync) | One to two days | 100 to 500 USD |
| Metabase (self-hosted) | Half a day | Engineering cost only |
| ClickHouse Cloud | One day | 50 to 300 USD |
What the reporting layer must have
- Physical separation from the OLTP database under real reporting load.
- A reliable sync mechanism with schema change handling.
- Materialized views or pre-aggregated tables for the most common queries.
- Access control so customers see only their own data.
- A customer-facing export surface for B2B products.
- A retention policy matching the analytical needs and cost constraints.
- Monitoring on sync lag so you know when the analytical store is stale.
Expert opinion
Reporting is the feature that customers ask for in year two and wish had been built in year one. The technical problem is not hard. The hard part is understanding early enough that your OLTP database is the wrong place to run it. The teams that figure that out before the first performance incident have a much smoother path than the ones who figure it out during one.
>
Yashveer Singh, founder of Yashveer Labs
How this played out on a real project
A B2B SaaS client had a reporting page that ran five complex aggregate queries on a read replica of their production Postgres database. When the product was small, the page loaded in two seconds. At forty thousand customers it took forty-five seconds. At times of day when customers were actively using the product, the read replica was under enough load that reporting queries timed out.
We spent three weeks adding BigQuery as the analytical layer. We set up Fivetran to sync the relevant tables from Postgres into BigQuery nightly. We rewrote the five reporting queries to run against BigQuery instead of the read replica. The reporting page load time dropped to under three seconds at any scale. The production database was no longer affected by reporting load.
The second phase was the customer-facing data export. Enterprise customers had been asking for CSV exports of their historical data. With BigQuery in place, the export query was straightforward. We added an export endpoint that queries BigQuery with the customer's tenant ID as a filter and streams the result as a CSV. The first enterprise deal that closed after shipping the export cited it explicitly in the evaluation notes. For more on the data layer, see why your saas should treat its database like a product and the read heavy workload strategies that move the needle.
Common mistakes teams make
- Running all reporting queries on the production OLTP database. This degrades performance for active users.
- Treating the read replica as a permanent solution. It delays the problem, not eliminates it.
- No sync layer monitoring. The analytical store silently falls behind and reports show stale data.
- No customer access control on the reporting layer. A query bug exposes cross-tenant data.
- Building customer-facing reports without a controlled API. Direct database access from the UI is a security risk.
- Skipping materialized views. Expensive aggregate queries recalculate on every page load.
- No retention policy. The analytical store grows without bound and costs compound.
- Building a custom ETL script instead of using a managed sync tool. Schema changes break it silently.
A six-week plan
- Week one. Audit all reporting queries. Identify which ones are slow or growing. Identify which tables they run against.
- Week two. Set up a managed data warehouse (BigQuery is the easiest starting point). Configure sync for the highest-traffic reporting tables.
- Week three. Migrate the slowest reporting queries to the warehouse. Validate results match the original.
- Week four. Add materialized views or pre-aggregated tables for the most common aggregate patterns.
- Week five. Add customer-facing export if this is a B2B product. Wire access control by tenant.
- Week six. Add sync lag monitoring and alerting. Document the architecture for the team.
For the time-series variant of this problem, time series data in saas when to pull in timescaledb or influxdb covers the specialized case. For the write-side implications, the write heavy workload a different set of tradeoffs is the companion read.
Frequently asked
The person who wrote this
Yashveer Singh wrote this. Class 12, Commerce track, full stack developer. The categories do not align, which is the point. The work runs in production. Everything else is paperwork. If the project on your plate is the one this article describes, you can reach me through the contact page or through Instagram. I will read it. I will reply. That is the standard.
Posts that line up with this one.
- SaaS Architecture and Scaling
Idempotency in API Design: Why It Matters More Than You Think
An idempotent API is one that handles repeated requests gracefully. Building it in from the start is far cheaper than retrofitting it after your first double-charge incident.
- SaaS Architecture and Scaling
Internal Admin Tools: Build vs Buy vs Retool
Every SaaS needs internal tools. The question is whether to build them, buy a platform like Retool, or use a lighter alternative. Here is the decision framework that saves engineering hours without creating tool debt.
- SaaS Architecture and Scaling
Job Failure Recovery: How Good SaaS Companies Sleep at Night
Every background job will fail eventually. The companies that sleep at night are the ones that built failure recovery into the system from day one, not as an afterthought when something broke in production.
- SaaS Architecture and Scaling
Monolith vs Microservices: Why Most Startups Get It Wrong
Microservices are the architecture that works at Netflix and fails at early-stage startups. Here is why the monolith is the right default, when microservices become rational, and how to make the transition without breaking everything.