Developer Guide 1: Architecture & Structure
Architecture
Chapter 1: Understanding how sparQ works under the hood.
Now that you've built your first app, let's step back and understand how sparQ is structured. This knowledge will help you make better decisions as you build more complex applications.
Design Philosophy
sparQ is built on a foundation that prioritizes modularity and extensibility. The goal is a system that can grow and scale over time while remaining simple, intuitive, and efficient.
Everything in sparQ—including the core system itself—is a module. This means:
A sparQ module is NOT a Python module. When we say "module" in sparQ, we mean a self-contained feature unit—a folder with a specific structure. This is different from Python's concept of a module (any .py file).
- Consistency - Once you understand one module, you understand them all
- Isolation - Modules don't interfere with each other
- Extensibility - Add functionality without touching core code
- Simplicity - Each module does one thing well
Architectural Principles
Seven key pillars guide sparQ's architecture:
| Principle | What It Means |
|---|---|
| Modular Structure | Every component functions as an independent module with its own models, views, and controllers |
| Dynamic Loading | Only essential modules are required; additional features load on demand |
| Lightweight Database | SQLite as the primary data store for simplicity and portability |
| ORM Layer | SQLAlchemy handles all database interactions with clean Python code |
| Server-Side Rendering | Jinja2 templates with HTMX for dynamic updates without JavaScript complexity |
| Containerized Deployment | Docker-based deployment for consistent environments |
| Internationalization | JSON-based translation system enables global functionality |
The Big Picture
sparQ is a modular business platform built on Flask. Think of it as a foundation that handles all the common stuff—authentication, database, email, payments—so you can focus on building your specific business logic.
Here's what makes up a sparQ installation:
+-------------------------------------------------------------+
| Flask Application |
| (app.py) |
+-------------------------------------------------------------+
| |
| +-----------+ +-----------+ +---------------------+ |
| | Module | | Database | | i18n | |
| | Loader | |(SQLAlchemy)| | (JSON files) | |
| | (pluggy) | | SQLite | | | |
| +-----+-----+ +-----------+ +---------------------+ |
| | |
| v |
| +-----------------------------------------------------+ |
| | Modules | |
| | +-------+ +-------+ +-------+ +-------+ | |
| | | Core | | Team | |Dashbrd| | Apps | | |
| | +-------+ +-------+ +-------+ +-------+ | |
| +-----------------------------------------------------+ |
| |
+-------------------------------------------------------------+
Required Modules
Three modules are essential for sparQ to run:
| Module | Purpose |
|---|---|
| Core | Authentication, user administration, base templates, and foundational services |
| Dashboard (Home) | Business dashboard, pipeline view, and central landing page after login |
| Team | Employee management with roles, departments, and organizational structure |
Everything else—Sales, Billing, Service, and your custom apps—is optional and loads only when needed.
Technology Stack
sparQ uses a carefully chosen set of technologies:
| Layer | Technology | Why |
|---|---|---|
| Web Framework | Flask | Lightweight, flexible, extensive ecosystem |
| Database | SQLite | Zero configuration, portable, surprisingly powerful |
| ORM | SQLAlchemy | Python's most mature database toolkit |
| Templates | Jinja2 | Fast, secure, Flask's native templating |
| Interactivity | HTMX + Alpine.js | Dynamic UIs without JavaScript complexity |
| Styling | Bootstrap 5 | Responsive, well-documented, customizable |
| Plugin System | Pluggy | Python's standard for extensibility (used by pytest) |
| AI Integration | OpenAI / Anthropic | LLM-powered assistant with tool calling |
System Services
sparQ provides a set of system services that any module can use. These live in the system/ directory:
| Service | What It Does | Example Use |
|---|---|---|
| db | Database connection and ORM | from system.db import db |
| auth | Authentication decorators | @login_required |
| Send emails | EmailService.send() |
|
| Generate PDF documents | PDFService.generate() |
|
| i18n | Translations | _('Hello') |
| payments | Stripe integration | PaymentService.charge() |
| background | Background task processing | BackgroundTask.run() |
| marketplace | Install/manage apps | MarketplaceService.install() |
| ai | AI tools and LLM integration | from system.ai import Tool |
You'll use these services throughout your apps. For example, to require login on a route:
from system.auth import login_required
@bp.route('/dashboard')
@login_required
def dashboard():
return render_template('my_app/dashboard.html')
Request Lifecycle
When a user clicks something in your app, here's what happens:
1. Request arrives at Flask
↓
2. URL routing matches a controller
↓
3. Auth middleware checks permissions
↓
4. Your controller runs
↓
5. Controller calls models/services
↓
6. Template renders HTML
↓
7. Response sent to browser
Let's trace a real example. When a user visits /hello/ from your Hello sparQ app:
- Flask sees the URL matches
@bp.route('/')in yourmain.pycontroller - Your
index()function runs - It calls
render_template('hello/desktop/index.html') - Jinja2 processes the template
- HTML is sent back to the browser
The Frontend Stack
sparQ uses a server-rendered approach rather than a Single Page Application (SPA). This means:
- Jinja2 - Templates render HTML on the server
- HTMX - Makes pages feel dynamic without writing JavaScript
- Alpine.js - Handles small UI interactions (dropdowns, modals)
- Bootstrap 5 - Provides the CSS framework
This stack is simpler than React/Vue and perfect for business applications where SEO matters and you don't need complex client-side state.
Database Layer
sparQ uses SQLite as its primary database and SQLAlchemy as its ORM (Object-Relational Mapper). This lets you work with Python objects instead of writing raw SQL:
# Instead of: SELECT * FROM tasks WHERE user_id = 1
tasks = Task.query.filter_by(user_id=1).all()
# Instead of: INSERT INTO tasks (title, user_id) VALUES ('Buy milk', 1)
task = Task(title='Buy milk', user_id=1)
db.session.add(task)
db.session.commit()
SQLite might seem simple, but it's incredibly capable. It handles millions of records, supports concurrent reads, and requires zero configuration. For most business applications, it's more than enough.
The SDK
The sdk/ folder contains developer tooling for building apps:
# Create a new app with auto-generated mappid
cd sdk
make app name=myapp
# Package for marketplace
make release name=myapp
The SDK handles scaffolding, generates unique marketplace IDs (mappid), and packages apps for distribution.
The Module System
Everything in sparQ is a module. There are three types:
| Type | Location | Purpose |
|---|---|---|
| Base Modules | modules/base/ |
Core functionality shipped with sparQ |
| Apps | data/modules/apps/ |
Your custom applications |
| Plugins | data/addons/ |
Extensions that enhance other modules |
When sparQ starts, it automatically discovers and loads modules in this order:
- Core - Authentication, base templates
- Dashboard - Business dashboard (Home)
- Team - Employee management
- Other base modules - Alphabetically
- Your apps - Alphabetically
Modules can define dependencies on other modules, ensuring they load in the correct order.
We'll dive deeper into modules in the next chapter.
Key Takeaways
- sparQ prioritizes modularity—everything is a self-contained module
- Core, Dashboard, and Team modules are required; everything else is optional
- Flask + SQLite + SQLAlchemy provide the foundation
- Server-rendered HTML with HTMX gives you dynamic UIs without JavaScript complexity
- System services (auth, db, email, etc.) are available to all modules
- Modules are automatically discovered and loaded at startup
Modules & Apps
Chapter 2: How sparQ organizes code into modules.
In the previous chapter, you learned that sparQ is built on modules. Now let's understand exactly what modules are, where they live, and how they work together.
What is a Module?
A sparQ module is NOT a Python module. When we say "module" in sparQ, we mean a self-contained feature unit—a folder with a specific structure. This is different from Python's concept of a module (any .py file).
A module is a self-contained piece of functionality with its own models, controllers, views, and translations. Your Hello sparQ app from the tutorial? That's a module.
Every module follows the same structure:
This consistent structure means once you understand one module, you understand them all.
Module Types
sparQ has two types of modules, determined by where they live:
| Type | Location | Purpose |
|---|---|---|
| Base Modules | modules/base/ |
Core sparQ functionality (Sales, Billing, Team, etc.) |
| Apps | data/modules/apps/ |
Your custom applications and marketplace installs |
The folder determines the type. Put your code in data/modules/apps/ and sparQ automatically treats it as an app.
Base Modules vs Apps
The key difference is how they appear in the UI:
Base Modules
- Part of the main sparQ experience
- Show up in the left sidebar navigation
- Examples: Dashboard, Sales, Service, Billing
Apps
- Launched from the Launch Pad
- Can be pinned to the main navigation for quick access
- Can have their own sidebar navigation
- Examples: Notes, Weather, your custom apps
Users can pin frequently-used apps to the main navigation, making them as accessible as base modules.
Where Your Apps Live
When you run the SDK scaffolding command:
cd sdk
make app name=myapp
It creates your app in:
data/modules/apps/myapp/
This location is important because:
- Persists across updates - Your code won't be overwritten when sparQ updates
- Auto-discovered - sparQ finds and loads it automatically
- Portable - You can zip it up and share it on the marketplace
The Directory Structure Explained
Let's look at each part of a module:
__manifest__.py
The identity card of your app. Contains name, version, mappid, and configuration:
# __manifest__.py
manifest = {
# Required fields
"name": "MyApp", # Display name (PascalCase)
"version": "1.0", # Semantic version
"mappid": "x7k2m9", # Marketplace app ID (auto-generated)
"main_route": "/myapp", # URL slug (used in /m/{mappid}/{main_route})
"type": "App", # "App" (visible) or "System" (hidden)
"depends": ["core"], # Module dependencies
# Display fields
"icon_class": "fa-solid fa-cube", # FontAwesome icon class
"color": "#7c3aed", # Theme color (purple)
"description": "What my app does",
"long_description": "Detailed description of the app's features.",
}
module.py
Contains lifecycle hooks that run when sparQ starts:
# module.py
def init_app(app):
"""Called when Flask app initializes."""
pass
def init_database(db):
"""Called after database tables are created."""
# Good place to seed initial data
pass
controllers/
Flask blueprints that handle HTTP requests. Each file typically handles one area:
# controllers/main.py
from flask import Blueprint, render_template
bp = Blueprint('main', __name__, url_prefix='/myapp')
@bp.route('/')
def index():
return render_template('myapp/desktop/index.html')
models/
SQLAlchemy models for your database tables:
# models/item.py
from system.db import db
class Item(db.Model):
__tablename__ = 'myapp_items'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(100))
views/
Contains templates and static assets. Templates go in a namespaced subfolder with desktop/ and mobile/ variants:
<!-- views/templates/myapp/desktop/index.html -->
{% extends "core/desktop/base.html" %}
{% block content %}
<h1>Welcome to My App</h1>
{% endblock %}
CSS files go in views/assets/css/:
/* views/assets/css/myapp.css */
.myapp-app .content-card {
/* Your custom styles */
}
lang/
JSON files with translations for each language:
// lang/en.json
{
"welcome": "Welcome to My App",
"save": "Save",
"cancel": "Cancel"
}
App Navigation
Apps can define their own sidebar navigation using nav_sections in the manifest:
# __manifest__.py
{
'name': 'Helpdesk',
'nav_sections': [
{
'title': 'Tickets',
'icon': 'ticket',
'items': [
{'route': 'helpdesk.tickets.list', 'label': 'All Tickets'},
{'route': 'helpdesk.tickets.unassigned', 'label': 'Unassigned'}
]
}
]
}
This creates a custom left sidebar when users are in your app.
Module Loading Order
When sparQ starts, modules load in this order:
- Core - The foundation (authentication, base templates)
- Dashboard - Business dashboard (Home)
- Team - Employee management
- All other base modules - Alphabetically
- Apps - From
data/modules/apps/, alphabetically
This order matters if your app depends on another module being loaded first.
Key Takeaways
- Everything in sparQ is a module with a consistent structure
- The folder location determines if it's a base module or app
- Your apps go in
data/modules/apps/ - Use
cd sdk && make app name=myappto scaffold a new app - Each module has manifest (with mappid), controllers, models, views, and lang files
- Templates go in
views/templates/appname/desktop/ - Apps can define their own navigation
App Configuration
Chapter 3: Setting up your app's identity and behavior.
Every sparQ app needs configuration files that tell sparQ what your app is called, how it should appear, and what it depends on. Let's explore the manifest files.
The Two Manifest Files
sparQ apps use two manifest files:
| File | Purpose | When Needed |
|---|---|---|
__manifest__.py |
Tells sparQ how to load your app | Always (required) |
marketplace.json |
Marketplace listing information | Only when publishing |
For development, you only need __manifest__.py. Add marketplace.json when you're ready to publish.
The __manifest__.py File
This is the heart of your app's configuration. Here's a complete example:
# __manifest__.py
manifest = {
# Required fields
"name": "TaskManager", # Display name (PascalCase)
"version": "1.0", # Semantic version
"mappid": "x7k2m9", # Marketplace app ID (auto-generated)
"main_route": "/tasks", # URL slug (used in /m/{mappid}/{main_route})
"type": "App", # "App" (visible) or "System" (hidden)
"depends": ["core"], # Module dependencies
# Display fields
"icon_class": "fa-solid fa-check-square", # FontAwesome icon class
"color": "#3B82F6", # Theme color (blue)
"description": "Simple task management for teams",
"long_description": "Organize tasks, set priorities, and track progress.",
}
Required Fields
| Field | Description | Example |
|---|---|---|
name |
Display name shown in UI (PascalCase) | "TaskManager" |
version |
Version number (MAJOR.MINOR format) | "1.0" |
mappid |
Unique 6-char marketplace app ID | "x7k2m9" |
main_route |
URL slug for your app | "/tasks" |
type |
"App" (visible) or "System" (hidden) |
"App" |
depends |
List of module dependencies | ["core"] |
About mappid
The mappid (marketplace app ID) is a unique 6-character identifier for your app. It's used to create collision-free routes in the format /m/{mappid}/{slug}.
You don't need to create this yourself. When you scaffold a new app using the SDK, the mappid is auto-generated:
cd sdk
make app name=myapp
This generates a unique mappid and inserts it into your manifest. Your app will be accessible at /m/{mappid}/myapp.
/notes or /tasks would collide. The mappid ensures every app has a unique namespace while you still control the slug portion of the URL.
Display Fields
| Field | Description | Default |
|---|---|---|
icon_class |
FontAwesome icon class | "fa-solid fa-cube" |
color |
Theme color (hex code) | "#7c3aed" |
description |
Short description (1-2 sentences) | Empty |
long_description |
Detailed description for marketplace listing | Empty |
The color field sets your app's accent color throughout the UI:
Dependencies
The depends field lists modules your app requires:
'depends': ['core', 'team']
Common dependencies:
core- Base functionality (almost always needed)team- Employee/user databilling- Invoice and payment features
Navigation
Apps can define their own sidebar navigation with nav_sections:
'nav_sections': [
{
'title': 'Dashboard',
'icon': 'speedometer2',
'route': 'myapp.main.dashboard'
},
{
'title': 'Settings',
'icon': 'gear',
'route': 'myapp.settings.index'
}
]
Each section needs:
title- Text shown in sidebaricon- Bootstrap icon nameroute- Flask route name (blueprint.function)
The marketplace.json File
When you're ready to publish your app, create a marketplace.json file:
{
"marketplace_id": "task-manager",
"name": "Task Manager",
"type": "app",
"author": "Your Name",
"author_email": "you@example.com",
"description": "Simple task management for teams",
"category": "productivity",
"tags": ["tasks", "productivity", "team"],
"min_sparq_version": "1.0",
"license": "MIT",
"screenshots": [
"screenshots/dashboard.png",
"screenshots/task-list.png"
]
}
__manifest__.py when you run make archive or make release. This ensures your version is always in sync.
Required Marketplace Fields
| Field | Description |
|---|---|
marketplace_id |
Unique ID (lowercase, hyphens only) |
name |
Display name |
type |
"app", "lang_pack", or "plugin" |
author |
Your name or company |
author_email |
Contact email |
Categories
Choose one category for your app:
productivity- Task management, notes, organizationcommunication- Chat, messaging, notificationsfinance- Accounting, invoicing, expenseshr- Human resources, hiring, onboardingsales- CRM, quotes, proposalsservice- Tickets, support, field serviceanalytics- Reports, dashboards, metricsintegrations- Third-party connectionsutilities- Tools, utilities, helpers
Version Numbers
sparQ uses a simple MAJOR.MINOR versioning format:
- MAJOR - Breaking changes or major rewrites
- MINOR - New features, improvements, and bug fixes
Examples:
1.0→1.1(new feature or bug fix)1.1→1.2(another update)1.9→2.0(breaking change or major rewrite)
__manifest__.py. The SDK automatically syncs this to marketplace.json when packaging.
Key Takeaways
__manifest__.pyis required and tells sparQ about your appmappidis auto-generated by the SDK and creates unique routesmarketplace.jsonis only needed for publishing- Use
nav_sectionsto create custom sidebar navigation - The
colorfield sets your app's theme - Follow semantic versioning for your version numbers