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:

Key Characteristics

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

  1. Preparation (Week 1)
    • S3 configuration
    • Folder structure
    • Base templates
  2. Development (Week 2-3)
    • Analysis scripts
    • Report templates
    • Local testing
  3. Deployment (Week 4)
    • Lambda configuration
    • Automation
    • Documentation

Code Examples

  1. 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')
    
  2. 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

Best Practices

  1. Version all code
  2. Test locally before deployment
  3. Monitor costs
  4. Document processes
  5. Regular data backup

Resources

Key Considerations

  1. Limit page size
  2. Optimize queries
  3. Handle errors
  4. Secure access
  5. Maintain templates

Would you like me to detail any specific parts further or add more examples?