Structa Cloud Structa Cloud

Delivery

Build a system your team can inherit

The best platform handoff is not a technical monument. It is a set of understandable decisions that people can safely extend.

Delivery 7 min read ← All posts

Six projects, one repository, one CI pipeline. The structa.cloud monorepo holds Django sites, an Astro frontend, a desktop POS, and the libraries that bind them.

Shared everything

Configs, assets, and component templates live once under projects/. Sites pick from them instead of copying. A fix in django-fusion propagates to every site in one commit.

The cost

Monorepos trade isolation for consistency. We pay it down with a strict Makefile dispatcher and per-site tests, so a change to shared code is validated against every consumer before it lands.

For a small team shipping products that share a stack, the trade is worth it.

Formints: the SQLite schema shared across editions

Every Formints edition starts from the same local schema. Community uses it directly, Standard adds a sync layer, and Pro turns it into a cloud master. Here is the core of it:

CREATE TABLE sales (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  terminal_id TEXT NOT NULL,
  total_cents INTEGER NOT NULL,
  payment_method TEXT NOT NULL,
  created_at TEXT NOT NULL DEFAULT (datetime('now'))
);

CREATE TABLE sale_items (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  sale_id INTEGER NOT NULL REFERENCES sales(id),
  product_id TEXT NOT NULL,
  quantity INTEGER NOT NULL,
  unit_cents INTEGER NOT NULL
);

The Rust model that maps to this schema via Diesel ORM:

#[derive(Queryable, Insertable, Serialize)]
#[diesel(table_name = crate::db::schema::sales)]
pub struct Sale {
    pub id: i32,
    pub terminal_id: String,
    pub total_cents: i32,
    pub payment_method: String,
    pub created_at: String,
}

The Tauri command that generates an invoice — reusable across Standard and Pro:

#[tauri::command]
pub fn generate_invoice(sale_id: i32, state: State) -> Result {
    let conn = &mut state.pool.get().map_err(|e| e.to_string())?;
    let sale: Sale = sales::table.find(sale_id).first(conn).map_err(|e| e.to_string())?;
    let items: Vec = sale_items::table
        .filter(sale_items::sale_id.eq(sale_id)).load(conn).map_err(|e| e.to_string())?;
    render_invoice_pdf(&sale, &items)
}

One codebase, four editions — the schema and commands stay the same, and each edition gates features on top of them.

Comments

A useful first release beats a noisy roadmap

We start with the customer journey, launch a focused slice, and leave your team with the content and tools to keep improving it.

ready when you are · text/html