Skip to content

Commit e9fcab3

Browse files
committed
docs: Add Architecture and Off-Main-Thread guide for DevIndex (#9259)
- Explain the minimal Main Thread role and the App Worker paradigm - Detail the lazy loading setup of the Viewport - Detail the hierarchical MVC and MVVM layer structure of DevIndex - Link to the GitHub source code
1 parent c91297d commit e9fcab3

1 file changed

Lines changed: 129 additions & 0 deletions

File tree

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
# App Shell & MVVM: The DevIndex Architecture
2+
3+
The DevIndex application is built with the Neo.mjs framework, which utilizes a revolutionary architecture: it runs almost entirely outside the browser's main thread. This guide details how the DevIndex frontend is structured to leverage this "Off-Main-Thread" paradigm, along with a hybrid approach to MVC and MVVM patterns.
4+
5+
> **Explore the Source:** You can find the complete source code for the DevIndex application here: [GitHub: apps/devindex](https://github.com/neomjs/neo/tree/dev/apps/devindex).
6+
7+
## The "Fat Client" Philosophy
8+
9+
Before diving into the components, it's crucial to understand the core philosophy of DevIndex: it is a pure **"Fat Client"**.
10+
11+
Unlike traditional web apps that rely on a server for sorting, filtering, and pagination, DevIndex streams in a single `users.jsonl` file on startup. From that point forward, the client literally does *everything*. All 50,000+ developer records are held in memory within the App Worker. Every filter, every sort, and every view transition happens instantly on the client side without ever hitting a backend server again.
12+
13+
*(For a deep dive into how this streaming works, see [The Backend Twist](#/learn/Backend)).*
14+
15+
## The Minimal Main Thread
16+
17+
If you inspect the source of the DevIndex application's entry point (`apps/devindex/index.html`), you will notice how remarkably empty it is:
18+
19+
```html readonly
20+
<!DOCTYPE HTML>
21+
<html>
22+
<head>
23+
<meta name="viewport" content="width=device-width, initial-scale=1">
24+
<meta charset="UTF-8">
25+
<title>DevIndex</title>
26+
</head>
27+
<body>
28+
<script src="../../src/MicroLoader.mjs" type="module"></script>
29+
</body>
30+
</html>
31+
```
32+
33+
The `<body>` tag is completely empty. The only script loaded is `MicroLoader.mjs`.
34+
35+
The main thread of the browser is intentionally kept as idle as possible. It has three primary responsibilities:
36+
1. **Starting Workers**: Bootstrapping the Neo.mjs multi-worker environment.
37+
2. **Delegating UI Events**: Capturing native DOM events (like scroll or click), serializing them as JSON, and delegating them to the App Worker.
38+
3. **Applying Delta Updates**: Receiving minimal Virtual DOM diffs and applying them efficiently via the `domApiRenderer`.
39+
40+
By keeping the main thread clear of application logic, data processing, and state management, the DevIndex application ensures the UI remains fluid and responsive, completely immune to "jank" even under heavy computational load.
41+
42+
## The App Worker: Where DevIndex Lives
43+
44+
After the MicroLoader boots up the framework, it spawns the application inside a dedicated background thread known as the **App Worker**.
45+
46+
```mermaid
47+
graph TD
48+
subgraph Browser Engine
49+
Main(Main Thread<br/>DOM & Events)
50+
end
51+
52+
subgraph Neo.mjs Workers
53+
App(App Worker<br/>Business Logic & State)
54+
VDom(VDom Worker<br/>Diffing & Deltas)
55+
end
56+
57+
Main -->|UI Events JSON| App
58+
App -->|VDOM JSON| VDom
59+
VDom -->|DOM Deltas| Main
60+
```
61+
62+
*(Note: For more on how other workers like the Canvas and Data workers fit in, see the [OffscreenCanvas & Rendering](#/learn/frontend/OffscreenCanvas) and [The 50k-Row Grid](#/learn/frontend/TheGrid) guides.)*
63+
64+
The entry point for DevIndex in the App Worker is `app.mjs`:
65+
66+
```javascript readonly
67+
import Overwrites from './Overwrites.mjs';
68+
import Viewport from './view/Viewport.mjs';
69+
70+
export const onStart = () => Neo.app({
71+
mainView: Viewport,
72+
name : 'DevIndex'
73+
});
74+
```
75+
76+
This is where the magic happens. The entire application—every component, every piece of state, every network request—runs within this App Worker.
77+
78+
## Viewport and Lazy Loading
79+
80+
The root component of the application is the `Viewport` (`apps/devindex/view/Viewport.mjs`). It defines the declarative layout using Neo.mjs's JSON-based Virtual DOM blueprints.
81+
82+
Crucially, DevIndex utilizes **lazy loading** for its main sections to keep the initial bundle size minimal:
83+
84+
```javascript readonly
85+
items: [{
86+
module: Header,
87+
flex : 'none'
88+
}, {
89+
ntype : 'container',
90+
flex : 1,
91+
layout : {ntype: 'card', activeIndex: null},
92+
reference: 'main-content',
93+
items : [
94+
{module: () => import('./home/MainContainer.mjs')},
95+
{module: () => import('./learn/MainContainer.mjs')}
96+
]
97+
}]
98+
```
99+
100+
Notice the `module: () => import(...)` syntax. The framework will only fetch and execute the code for the Home (Grid) or Learn (Documentation) sections when the routing logic dictates they should be visible.
101+
102+
## A Hybrid Approach: MVC meets MVVM
103+
104+
DevIndex organizes its codebase using a blend of classic Model-View-Controller (MVC) for its data layer and Model-View-ViewModel (MVVM) for its UI components.
105+
106+
### 1. The MVC Foundation
107+
108+
At the top level of the `apps/devindex` folder, you will find a traditional MVC directory structure:
109+
* **Model**: `model/Contributor.mjs` defines the schema and virtual fields for the developer data.
110+
* **Store**: `store/Contributors.mjs` acts as the collection manager, holding the 50,000+ records in memory.
111+
* **View**: Folders like `view/home/` contain the visual components (e.g., `GridContainer.mjs`).
112+
113+
### 2. The MVVM UI Layer (Hierarchical Architecture)
114+
115+
While the data layer leans MVC, the UI components utilize a powerful, instance-based MVVM pattern. Instead of global controllers or single-store monoliths, DevIndex attaches **State Providers** and **View Controllers** directly to specific container instances within the component tree.
116+
117+
These providers and controllers form a **hierarchical chain**:
118+
119+
* **Hierarchical State Providers (ViewModel)**:
120+
When a component requests data (e.g., `bind: { text: data => data.title }`), the framework searches up the component tree. If the current component's State Provider doesn't have the data, it checks the parent's State Provider, and so on. This creates a deeply nested chain of merged data access. The `ViewportStateProvider` holds global UI state, while the `MainContainerStateProvider` might hold the specific `Contributors` store.
121+
122+
* **Hierarchical View Controllers**:
123+
Event handling works the same way. When a button is configured with a string-based listener (e.g., `handler: 'onButtonClick'`), the framework doesn't look in a global registry. It walks up the component tree to find the *closest* View Controller that implements the `onButtonClick` method. This elegantly encapsulates logic exactly where it's needed, preventing bloated, monolithic controllers.
124+
125+
For a deeper dive into how these controllers manage filtering and global state, see the [State Management & Controls](#/learn/frontend/StateAndControls) guide.
126+
127+
## Summary
128+
129+
By running its entire architecture inside an App Worker, DevIndex achieves true concurrency. While the Main Thread paints, the App Worker handles state changes, lazy loads modules, and coordinates with other workers. Combined with a hierarchical MVVM approach and a "Fat Client" philosophy, this ensures that even when processing 50,000 developer records, the UI remains perfectly fluid and maintainable.

0 commit comments

Comments
 (0)