postman-gpui Architecture Design
postman-gpui Architecture Design
What Is GPUI
GUI stands for Graphical User Interface. GPUI is a Rust framework for building native graphical applications.
At a high level, GPUI can be understood as an event-driven system:
Event → Update State → Render
Core Abstraction
A simplified GUI event loop can be expressed as:
loop {
event = wait_event();
state = update(state, event);
render(state);
}
The application waits for events such as mouse clicks, keyboard input, window resizing, timers, and asynchronous task results.
Each event may update the application state. When the state changes, GPUI schedules the affected interface to be rendered again.
How GPUI Draws the UI in a Window
The application state is not drawn directly to the screen. It passes through several stages:
State
↓ render()
Element Tree
↓ layout()
Layout
↓ paint()
Pixels
Each stage has a different responsibility:
State
The current application and UI state.
Element Tree
The UI structure produced by render().
Layout
The position and size of every element.
Paint
Drawing commands for text, backgrounds, borders, and shapes.
Pixels
The final image displayed in the window.
A complex interface grows by composing smaller elements:
Application
├── Header
├── Sidebar
└── Workspace
├── Request Editor
│ ├── Method Selector
│ ├── URL Input
│ └── Send Button
└── Response Panel
Each component renders a small part of the element tree. The root component combines these smaller trees into the complete user interface.
Complete Abstraction
The event loop and drawing pipeline can be combined into one model:
Event Loop
↓ dispatch Event
update(State, Event)
↓
New State
↓ render()
Element Tree
↓ layout()
Layout
↓ paint()
Pixels
↓
Wait for the next Event
It can also be expressed as pseudocode:
loop {
event = wait_event();
state = update(state, event);
element_tree = render(state);
layout = compute_layout(element_tree);
pixels = paint(layout);
present(pixels);
}
In a real GPUI application, developers do not write this event loop manually. GPUI owns the platform event loop, while application code updates state stored in entities and uses cx.notify() to request rendering.
In summary:
GPUI is an event-driven GUI framework. Events update application state, and application state is transformed into pixels through rendering, layout, and painting.
Why MVVM
GPUI explains how events are processed and how application state is drawn into a window:
Event
→ Update State
→ Render
However, GPUI does not decide where the state should live, who is allowed to change it, or where business logic and external side effects should be implemented.
For a small application, everything can be written directly inside the View. As the application grows, the View may begin to own too many responsibilities:
View
├── Read User Input
├── Store UI State
├── Validate Data
├── Apply Business Rules
├── Execute HTTP Requests
├── Handle Responses
├── Access Storage
└── Draw the UI
The visible result is usually a very large file. However, file size is only the symptom. The deeper problem is that display logic, application state, business rules, and external side effects have become tightly coupled.
MVVM applies the idea of separation of concerns to stateful GUI applications.
All-in-One Page
An unlayered JSP-style page provides a useful comparison.
A single page may read input, apply business rules, access a database, and generate HTML:
JSP Page
├── Read Input
├── Store Page State
├── Apply Business Logic
├── Access Database
└── Render HTML
It can be summarized as:
Page
= View
+ State
+ Business Logic
+ Infrastructure
This does not mean that JSP cannot be layered or that Java applications are automatically layered.
The real comparison is:
All-in-One Page
vs
Layered Application
The same problem can appear in a GUI application:
GPUI Component
├── Read Input
├── Store State
├── Build Request
├── Execute HTTP
├── Handle Response
└── Draw the UI
In this structure, every piece of logic belongs to the concrete page or component.
As the application grows:
More Features
→ More Event Handlers
→ More Shared State
→ More Dependencies
→ Larger View Files
→ Higher Coupling
Splitting one large View into several files does not necessarily solve the problem:
view_part_1.rs
view_part_2.rs
view_part_3.rs
If those files still mix state, business rules, HTTP, storage, and drawing, the architecture remains all-in-one.
Layered Application
A layered Java Web application separates responsibilities behind explicit boundaries:
Controller
↓
Service
↓
Repository
↓
Database
Each layer has a different responsibility:
Controller
Receives external requests.
Service
Coordinates application operations.
Repository
Loads and stores application data.
Database
Persists the data.
The central idea is:
Display
≠ State Management
≠ Business Logic
≠ External Side Effects
Layering does not remove complexity. It gives each kind of complexity a clear owner.
Once business logic no longer depends on a specific page, it can be reused by different entry points:
Web Page ─────┐
REST API ─────┼──> Service
CLI Command ──┤
Test ─────────┘
Without layering:
Business Logic
→ Belongs to a Specific Page
With layering:
Business Logic
→ Belongs to an Independent Service
This makes the logic easier to test, maintain, replace, and reuse.
Applying Layering to GUI
MVVM applies the same separation principle to the presentation side of a stateful GUI application:
View
↓ Action
ViewModel
↓ Command
Application Service
↓
Model / HTTP / Storage
Each part has a clear responsibility:
View
Receives user events and draws the current state.
ViewModel
Owns presentation state and handles user actions.
Model
Represents business data and business rules.
Application Service
Executes HTTP, storage, and other external side effects.
The ViewModel gives the state in GPUI’s event loop a clear owner:
Event
↓
View
↓ Action
ViewModel
↓
New State + Command
↓ ↓
Render View Execute Service
↓
Result
↓
Update ViewModel
For postman-gpui, this separation can be summarized as:
GPUI View
Draws inputs, buttons, tabs, history, and responses.
ViewModel
Owns URL, method, headers, body, loading,
response, error, and selected-tab state.
Application Service
Coordinates asynchronous request execution
and delivers results back to the ViewModel.
HTTP Core
Transforms a Request into
Result<Response, Error>.
The HTTP Core no longer belongs to a particular button or GPUI component:
GPUI Application ─┐
Future CLI ────────┼──> HTTP Core
Tests ─────────────┘
The ViewModel itself is usually presentation-specific, so a simple CLI may not reuse the complete ViewModel. The most reusable parts are normally the Model, business rules, and services that do not depend on GPUI types.
MVVM is not the only possible solution. MVC, MVP, MVU, and other architectural patterns can also separate responsibilities. The important idea is not the name of the pattern, but clear state ownership and explicit module boundaries.
In summary:
MVVM applies the idea of layered architecture to stateful GUI applications. It gives presentation state a clear owner and separates UI rendering from application logic and external side effects, making independent parts easier to test, maintain, and reuse.
postman-gpui Architecture
postman-gpui is a native, cross-platform HTTP client built with Rust and GPUI.
From the user’s perspective, it accepts request information, communicates with an HTTP server, and displays the response through a graphical interface.
At the highest level:
User Input
→ HTTP Request
→ HTTP Server
→ HTTP Response
→ Graphical Output
However, postman-gpui is not only an HTTP function. It is also a long-running GUI application that must preserve state, process events, execute asynchronous operations, and continuously redraw the interface.
It therefore combines two systems:
GUI System
Event → State → Render
HTTP System
Request → Network → Response
MVVM and the Application Service connect these two systems.
Core Abstraction
The core purpose of postman-gpui is to transform graphical user input into an HTTP request and transform the HTTP result back into graphical output:
User Input
↓
Request
↓
HTTP
↓
Response
↓
Application State
↓
Graphical Output
For example:
URL Input
Method Selector
Headers Editor
Body Editor
↓
HTTP Request
↓
Status
Response Headers
Response Body
The HTTP operation itself can be expressed as a function:
Result<Response, Error>
= HTTP(Request, Network Environment)
The GUI operation can also be expressed as a state transition:
(new_state, command)
= update(old_state, user_event)
The complete application combines them:
User Event
→ Update State
→ Produce HTTP Command
→ Execute HTTP
→ Receive Result
→ Update State
→ Render UI
Unlike a simple CLI command, this process does not end after one request. The application returns to the event loop and waits for the next user action.
Architecture Layers
postman-gpui separates its responsibilities into several architectural layers:
View
↓ Action
ViewModel
↓ Command
Application Service
↓
HTTP Core / Storage
Each layer has a different responsibility.
View
The View is the graphical interface built with GPUI:
View
├── URL Input
├── Method Selector
├── Parameters Editor
├── Headers Editor
├── Body Editor
├── Send Button
├── History List
└── Response Panel
The View is responsible for:
User Event → Action
State → Element Tree
It receives mouse and keyboard events, converts them into application actions, and draws the current state.
The View should not need to understand how an HTTP client opens a connection, follows redirects, stores cookies, or reads from SQLite.
ViewModel
The ViewModel owns the state required by the interface:
ViewModel
├── Request Draft
├── Selected Method
├── Selected Tab
├── Loading State
├── Response State
├── Error State
└── History State
It receives actions from the View:
Set URL
Select Method
Add Header
Click Send
Cancel Request
Select History
It then produces a new state or a command:
(new_state, command)
= ViewModel.update(old_state, action)
The ViewModel does not draw pixels. It describes what the application currently means:
Not Sent
Loading
Success
Error
Cancelled
The View converts that state into visual elements.
Model
The Model represents the data and rules of the application:
Request
Response
Header
Request Body
History Entry
HTTP Error
These types do not need to know about buttons, windows, colors, focus, or layout.
They form the contracts passed between the other layers.
Application Service
The Application Service connects the ViewModel with external capabilities:
ViewModel Command
↓
Application Service
↓
HTTP Core / Storage
↓
Result
↓
ViewModel
It coordinates operations such as:
Start HTTP Request
Cancel HTTP Request
Receive HTTP Result
Load History
Save History
The Application Service is not part of the View, and it is not the HTTP implementation itself. It is the coordinator between application state and external side effects.
HTTP Core and Storage
The HTTP Core is responsible for network behavior:
Request
→ HTTP Execution
→ Result<Response, Error>
Storage is responsible for persistence:
History Entry
→ Storage
→ Stored History
These modules should remain independent from the graphical interface.
Complete Request Flow
When the user clicks the Send button, the complete flow can be expressed as:
Click Send
↓
View emits Action
↓
ViewModel validates current state
↓
ViewModel becomes Loading
↓
ViewModel produces Send Command
↓
Application Service executes Command
↓
HTTP Core sends Request
↓
HTTP Core returns Result
↓
Application Service delivers Result
↓
ViewModel becomes Success or Error
↓
GPUI renders the new State
The same process can be written more compactly:
Click Send
→ Action
→ Command
→ HTTP Request
→ Result
→ State
→ Render
The important point is that the View does not directly execute HTTP and then modify another UI component.
Instead:
View
produces Action
ViewModel
produces State and Command
Application Service
executes Command
HTTP Core
produces Result
ViewModel
accepts Result and produces New State
View
renders New State
This creates a predictable one-directional flow:
Event
→ Action
→ Command
→ Result
→ State
→ View
The result is then followed by another event, so the application continues as a loop.
Isolating the HTTP Core
The HTTP Core should have a small and explicit boundary:
HTTP(Request)
→ Result<Response, Error>
Its input is a normal Request:
Request
├── Method
├── URL
├── Headers
└── Body
Its output is a normal result:
Result
├── Response
│ ├── Status
│ ├── Headers
│ └── Body
└── Error
The HTTP Core may understand:
HTTP Methods
Headers
Bodies
Cookies
Redirects
Timeouts
Network Errors
It should not understand:
GPUI Window
Button
Input Element
Selected Tab
Response Panel
Element Tree
Layout
Paint
This dependency boundary is what makes the HTTP Core independent from the GUI:
GUI depends on HTTP Core
HTTP Core does not depend on GUI
Because the HTTP Core accepts and returns ordinary data, it can be reused by different entry points:
GPUI Application ─┐
Future CLI ────────┼──> HTTP Core
Tests ─────────────┘
The GPUI application and CLI may use different presentation states, but they can share the same request models, response models, HTTP execution rules, authentication logic, redirect behavior, and error handling.
The same isolation principle applies to Storage:
Application
→ Storage Interface
→ SQLite
The View does not need to know where History is stored or which SQL statements are executed.
Complete Abstraction
The complete architecture can be summarized as:
postman-gpui
= GPUI
+ MVVM
+ Application Service
+ HTTP Core
+ Storage
Each part answers a different question:
GPUI
How are events processed and the UI drawn?
MVVM
How are View, state, and Model organized?
Application Service
How are commands and external operations coordinated?
HTTP Core
How is a Request transformed into a Response?
Storage
How is application history persisted?
The complete runtime flow is:
Event Loop
↓
User Event
↓
GPUI View
↓ Action
ViewModel
↓ Command
Application Service
↓
HTTP Core / Storage
↓ Result
ViewModel
↓ New State
GPUI View
↓ render()
Element Tree
↓ layout()
Layout
↓ paint()
Pixels
↓
Wait for the next Event
In functional form:
(new_state, command)
= ViewModel(old_state, user_event)
result
= ApplicationService(command, environment)
final_state
= ViewModel(new_state, result)
pixels
= GPUI(final_state)
In summary:
postman-gpuicombines an event-driven GUI, an MVVM-style state architecture, application services, and an isolated HTTP Core. User actions become commands, commands produce results, results update state, and GPUI transforms that state into pixels.