The previous article covered the deployment architecture. This article focuses on the application architecture and how requests flow through the system.
graph TD U[Users / Browser] FE[Frontend App
UI] API[Backend REST API] DB[(Database)] U --> |HTTP Request| FE FE --> |HTTP Response| U FE -->|HTTP Request| API API -->|HTTP Response| FE DB -->|ETL| API API -->|Write| DB
The application is split into three logical components: a frontend, a REST API, and a persistence layer. Requests originate in the browser, pass through the frontend, and are forwarded to the API only when additional data or server-side processing is required. Likewise, the API interacts with the database only when necessary.
Why use a layered architecture instead of a monolith?#
A monolithic application would certainly be simpler. However, the backend originally existed as a command-line tool before a web interface was introduced. Preserving that separation allows the core engine to remain usable both interactively through the UI and offline through the CLI.
Benefits#
- clear separation of responsibilities;
- frontend and backend evolve independently;
- backend remains usable through both the web UI and CLI;
- the UI has no direct access to persistence;
- shorter deployment and testing cycles;
- simpler debugging and maintenance.
One consequence of separating the frontend and backend is that both sides must agree on the API
contract. Frameworks such as GraphQL attempt to address this problem. For RetroEdge, however, a
conventional REST API backed by well-defined JSON payloads is sufficient and avoids introducing
another layer of complexity.
Standards such as JSON:API can also help formalise these contracts without introducing an entirely new query language.
Trade-offs#
- greater architectural complexity than a monolith;
- frontend and backend contracts must remain compatible;
- end-to-end testing spans multiple components.
Tech stack#
| Component | Technology | Reason |
|---|---|---|
| Frontend | Dioxus Web | Rust UI |
| Server | Axum | HTTP routing and middleware |
| Styling | Tailwind CSS | Utility-first CSS |
| Persistence | CSV | Lightweight storage during development |
Isomorphic Rust, is that a thing? What is it the future?#
A pleasant side effect of using Dioxus is that much of the application can be written in Rust rather than maintaining separate JavaScript and backend codebases. The idea is similar to the “isomorphic JavaScript” movement from several years ago, but applied to Rust instead.
Although the project uses Dioxus Fullstack, most of the server-side implementation is built directly on Axum. Dioxus makes this transition straightforward, allowing lower-level control where needed.
The current implementation stores data as CSV files rather than using a relational database. This is sufficient for the current workload, and the storage layer is abstracted so that SQLite or PostgreSQL can be introduced later without affecting the rest of the application.
With the architectural groundwork in place, the next article begins exploring the implementation itself.
Feel free to share the article on socials: