If you've built web apps for a while, "no JavaScript framework" probably sounds like a red flag. Fair — it usually is. But Dvl'epr isn't a framework with the JS ripped out. It's a different division of labour: you describe what a screen contains and where its data comes from, and the engine writes the code that makes it work.
This post covers the core loop, the file hierarchy every app is built from, and exactly what you stop having to write once the engine is doing its job.
The core loop
Every request to a Dvl'epr application runs the same cycle. There's no client-side router deciding what to render, and no separate API layer to keep in sync with the frontend — one engine does both, on every request:
Nothing above the SQL layer is hand-written imperative code. You're not calling fetch(), you're not writing a reducer, you're not wiring an event handler to a state update. You're declaring structure, and the engine's PHP core (never touched by app developers) walks that structure and does the wiring for you.
What an app actually is: four layers
A Dvl'epr application isn't one big file — it's a small hierarchy of XML files, each with one job. This is the entire shape of every app on the platform:
- App —
emxml_app[Role].xml— the outermost shell for a role (Manager, Sales Staff, Warehouse Staff…). A few lines: one<site>entry per site the role can reach. - Site —
emxml_site[Role].xml— header, sidebar and nav shared across that role's screens, plus the<modules>list that drives the menu. - Module —
emxml_mod[Role][Module].xml— one screen's layout, and the only place JS gets generated. - Block —
emxml_blk[Name].xml— a reusable content unit (a KPI card, a list, a detail panel) that any layer can pull in.
There used to be a fifth layer between Site and Module — a Page file whose whole job was to name the module it contained. Once screens became single-module, that file carried no information, so it was collapsed: a site lists its modules directly, and a module is the screen. One less file per screen, and one less place for a name to be typed wrong.
The last layer matters more than it looks: blocks are the unit of reuse. A "customer detail" block built for the CRM app can be dropped into a Sales dashboard or a Support ticket screen by reference, not by copy-paste — because every layer resolves to the same underlying tenant-scoped data.
A real block, simplified
Here's a trimmed version of a block from a live Bizheart build — an app built entirely on Dvl'epr. It's a KPI card pulling a live count from the database:
<block> <!-- Block-level SQL comes first: its columns become tokens for everything below. --> <sql> <query name="open_orders"><![CDATA[ SELECT COUNT(*) AS open_order_count FROM orders WHERE dept_id = '%deptid%' AND status = 'open' ]]></query> </sql> <div class="kpi-card"> <div class="kpi-label">Open Orders</div> <div class="kpi-value">%open_order_count%</div> </div> </block>
Nobody wrote a query function, a component class, or a re-render trigger for this card. The %open_order_count% token gets resolved against the query result and substituted straight into the HTML the engine emits. Drop this block file into any module, on any app, for any tenant — it just works, scoped automatically to that tenant's dept_id.
What you stop writing
This is the part that actually changes how fast a full application comes together. Every one of the following is generated by the engine, not written by an app developer:
Never hand-written in a Dvl'epr app
getXxx() data-fetch functionsTake forms as an example. A modal form in a hand-built stack usually means: a component, a controlled-input state hook, a submit handler, a validation layer, an API call, a success toast, and a way to close the modal and refresh the underlying list. In Dvl'epr, that's an input block XML file plus a processor XML file. Register both, and the engine generates the getXxx() loader, the setupXxx() submit handler, validation against your declared required fields, and the success callback that closes the modal — because the pattern is identical every time, so it only had to be built correctly once, in the engine.
What happens when a page is requested
There is no build step and no bundle. A request arrives, the engine walks the definitions it needs, and a finished page comes back — on that request:
request
└── the site → header, sidebar and navigation
├── the module → the screen that was asked for
└── its layout, walked one element at a time
├── a real HTML tag → rendered as written, %tokens% filled in
├── <block file="…"/> → that block pulled in and built here
├── <ajax …/> → the JavaScript for this piece, generated
└── <pageslot/> → where the module's content lands
every %token% resolved, then the finished HTML sent —
with all the generated JavaScript appended once, at the end
Under that structure, the same cycle runs for every piece of the tree: the platform executes the SQL a block declares, injects the resulting data straight into that block's HTML, and generates the JavaScript needed to wire it up — form handling, data loading, whatever the block calls for. Any developer-written JS is folded in alongside the generated JS. The result is assembled into the full page, or just the component, and sent to the browser ready to display. No block has to know how any other block gets its data or builds its JS — each one goes through the same pipeline independently, so they compose cleanly no matter how many are on a page.
Why this trade-off is worth it
It's worth noting that wherever a screen's structure — forms, lists, detail views, approvals, dashboards — is repetitive enough to describe declaratively, it's repetitive enough to generate. Against a traditional front-end framework, that means no state management code to write, no API client to hand-build and keep in sync with the backend, no build pipeline to babysit, and no framework version to upgrade out from under you later. A screen that would take a day of hand-rolled component work takes a single file containing the HTML structure and SQL — done in no time at all.
Anyone looking to build web applications faster, with a smaller team and fewer resources than a hand-rolled stack usually demands, will find that's exactly what the platform is built to deliver — regardless of the kind of application or the market it's aimed at.
Building a Data List: Using Only HTML Structure and SQL
Post 3 opens a short hands-on arc — a list, a table, and a detail screen, then a form and its processor, before post 8 pulls all of it together into one complete page.