Intermediate 13 min

Structure a real application

Organize models, views, services, and platform edges without coupling your product to the renderer.

Split by responsibility

Keep the application model, its view composition, domain services, and platform adapters distinct. A practical starting point is app.rs for state and event routing, app/view.rs for Element construction, and focused service modules for external work.

Suggested structure
src/
  main.rs          # target entry point
  app.rs           # state and event routing
  app/view.rs      # Element composition
  services/        # domain I/O
  theme.rs         # product tokens

Keep boundaries renderer-neutral

Domain services should return ordinary Rust values. The model decides when to launch work and how results change state. The view consumes state and returns Elements; it should not own network clients or platform windows.

Create child models for ownership

Use a child Entity when a feature needs independent state, tasks, subscriptions, lifecycle, or cached rendering. Use a plain view function when it only transforms inputs into elements.

Reference files

These are the implementation and guide files used for this chapter.

app_examples/fake-ai-harness/src/app.rsapp_examples/fake-ai-harness/src/app/view.rsdocs/repo/structure.md
Compiled example

A model, task, view, and virtual list working together

The Rust file below is imported verbatim by this page and compiled into the WebAssembly application running underneath it.

Open exact source
app_examples/docs-examples/src/examples/project_structure.rs
use argui::{
    paint::{Border, CornerRadii},
    runtime::{Context, Render},
    text::{FontFamily, TextStyle},
    ui::{Element, Sides, percent},
    widgets::default_theme,
};

pub struct Example;

impl Render for Example {
    fn render(&mut self, cx: &mut Context<Self>) -> Element {
        let themes = default_theme(cx.environment());
        let theme = themes.resolve(cx.environment().color_scheme);
        let file = |name, purpose| {
            Element::row([
                Element::text(name).text_style(TextStyle {
                    family: FontFamily::Monospace,
                    color: theme.primary,
                    weight: 650,
                    ..TextStyle::default()
                }),
                Element::text(purpose)
                    .text_style(TextStyle {
                        color: theme.muted_foreground,
                        ..TextStyle::default()
                    })
                    .grow(1.0),
            ])
            .gap(16.0)
            .padding(Sides::length(14.0))
            .background(theme.card)
            .border(Border::all(1.0, theme.border))
            .radius(CornerRadii::all(8.0))
        };
        Element::column([
            file("main.rs", "target entry point"),
            file("app.rs", "state and event routing"),
            file("app/view.rs", "Element composition"),
            file("services/", "domain I/O"),
        ])
        .width(percent(1.0))
        .padding(Sides::length(24.0))
        .gap(9.0)
        .background(theme.background)
    }
}