Static Data Architecture Guide for Small Businesses
For Companies < $2M Revenue
Executive Summary
This guide proposes a lightweight and cost-effective data architecture based on static report generation. The approach focuses on:
- Minimal cost (< $50/month)
- Near-zero maintenance
- Serverless approach
- Open-source tools
- Simple interface for end users
Key Characteristics
- Limited technical resources
- Need for simplicity and easy maintenance
- Preference for managed services
- Strong budget constraints
- Small data volumes (<50GB)
- Small team (<5 people)
Recommended Solutions
1. Storage & Configuration
| Category | Tool | Features | Best For |
|———-|——|———–|———-|
| Storage | AWS S3 | • Object storage
• Static hosting
• Minimal cost | • Data storage
• Page hosting
• Configuration |
| Format | Parquet | • Efficient compression
• Column format
• DuckDB support | • Data storage
• Efficient querying
• Archiving |
| Config | YAML | • Readable
• Simple structure
• Versionable | • Report config
• Parameters
• Templates |
YAML Configuration Example:
# config/reports/sales.yaml
title: "Sales Report"
description: "Monthly sales analysis"
update_frequency: "daily"
data_source: "s3://bucket/sales.parquet"
charts:
- name: "Sales Evolution"
type: "line"
query: "SELECT date, SUM(amount) as sales FROM sales GROUP BY 1"
params:
x: "date"
y: "sales"
title: "Monthly Sales"
2. Processing & Analysis
| Category | Tool | Features | Best For |
|———-|——|———–|———-|
| Database | DuckDB | • In-memory querying
• Parquet support
• Python integration | • Data analysis
• Aggregations
• Transformations |
| Processing | pandas | • Data manipulation
• Easy integration
• Multiple formats | • Data preparation
• Calculations
• Export |
| Charts | Plotly | • Interactive
• HTML export
• Customizable | • Visualizations
• Charts
• Dashboards |
DuckDB Query Example:
import duckdb
def analyze_sales(start_date):
query = """
SELECT
date_trunc('month', date) as month,
sum(amount) as revenue,
count(distinct customer_id) as customers,
sum(amount)/count(distinct customer_id) as avg_revenue_per_customer
FROM read_parquet('s3://bucket/sales.parquet')
WHERE date >= ?
GROUP BY 1
ORDER BY 1
"""
return duckdb.query(query, [start_date]).df()
3. Report Generation
| Category | Tool | Features | Best For |
|———-|——|———–|———-|
| Templates | Jinja2 | • Template inheritance
• Macros
• Filters | • HTML structure
• Reusability
• Logic |
| Style | TailwindCSS | • Utility CSS
• No build
• Responsive | • Page styling
• Components
• Layout |
| Deployment | AWS Lambda | • Serverless
• Minimal cost
• Automated | • Generation
• Publishing
• Updates |
Jinja Template Example:
{# templates/components/metric_card.html #}
<div class="bg-white rounded-lg shadow p-4">
<h3 class="text-lg font-semibold text-gray-700"></h3>
<p class="text-3xl font-bold text-blue-600"></p>
</div>
Architecture
graph TD
A[Parquet/S3 Data] -->|DuckDB| B[Analysis]
C[Jinja Templates] -->|Lambda| D[HTML Generation]
B -->|Plotly| D
D -->|S3| E[Static Pages]
F[EventBridge] -->|Trigger| G[Lambda]
G -->|Update| E
Implementation Guide
- Preparation (Week 1)
- S3 configuration
- Folder structure
- Base templates
- Development (Week 2-3)
- Analysis scripts
- Report templates
- Local testing
- Deployment (Week 4)
- Lambda configuration
- Automation
- Documentation
Code Examples
- Main Lambda Script:
def lambda_handler(event, context): generator = ReportGenerator() # Generate index index_html = generator.generate_report('index.html') upload_to_s3(index_html, 'index.html') # Generate reports reports = get_report_configs() for report in reports: html = generator.generate_report( f'reports/{report.template}', params=report.params ) upload_to_s3(html, f'reports/{report.id}.html') - Generator Class:
class ReportGenerator: def __init__(self): self.template_loader = jinja2.FileSystemLoader('templates') self.template_env = jinja2.Environment(loader=self.template_loader) self.db = duckdb.connect(":memory:") def generate_report(self, template_name, params=None): template = self.template_env.get_template(template_name) data = self.get_report_data(params) return template.render(data=data, params=params)
Estimated Costs
- S3: ~$1-2/month
- Lambda: free (under free tier)
- Transfer: ~$1-2/month Total: ~$5/month
Best Practices
- Version all code
- Test locally before deployment
- Monitor costs
- Document processes
- Regular data backup
Resources
Key Considerations
- Limit page size
- Optimize queries
- Handle errors
- Secure access
- Maintain templates
Would you like me to detail any specific parts further or add more examples?