A small experiment in building a simpler web application

There is a particular moment in software development when a collection of files begins to feel like an application.

It does not happen when the first page renders.

It does not necessarily happen when an API returns JSON.

It happens somewhere in between, when the pieces begin to have clear responsibilities and the system starts making sense even to the person who did not write the first line of it.

Jaroa began at that moment.

What started as a small project called headless-php-application gradually became an attempt to answer a much larger question:

Can we build a modern web application that remains understandable, lightweight, portable, and genuinely simple to operate?

The answer is not complete yet. In fact, this project is deliberately unfinished.

That is part of the point.

Jaroa is intended to evolve.


1. The original idea

The starting point was simple.

We wanted WordPress to remain responsible for the complicated backend work:

  • content management
  • users and permissions
  • database operations
  • media
  • administration
  • custom post types
  • the familiar WordPress ecosystem

But we did not want WordPress to dictate how the public-facing application should look or behave.

The traditional WordPress model looks roughly like this:

Browser
   ↓
WordPress
   ↓
Theme
   ↓
HTML

Everything lives together.

That model is enormously useful, but it also means that the frontend and backend are tightly connected.

Our experiment took a different route:

Browser
   ↓
PHP Frontend
   ↓
API
   ↓
WordPress Backend

The distinction is small in appearance, but significant architecturally.

WordPress becomes a backend service.

The frontend becomes an independent application.

And the API becomes the contract between them.

That last part would eventually become the central idea behind Jaroa.


2. The first project: headless-php-application

The original project had a deliberately modest ambition.

We wanted to see whether a lightweight PHP application could communicate with WordPress without loading WordPress itself.

Instead of doing this:

require_once 'wp-load.php';

the frontend would communicate with WordPress over HTTP.

That meant WordPress could remain somewhere else entirely.

For development, we used DDEV, Docker, PHP and WordPress. The backend was developed independently from the frontend.

Eventually, the backend exposed endpoints such as:

/wp-json/fullstack/v1/status
/wp-json/fullstack/v1/posts
/wp-json/fullstack/v1/apps

The WordPress plugin became the API layer.

The frontend then needed only an API client.

This was our first important architectural boundary.


3. The API became the bridge

The more we worked with the system, the more obvious one thing became:

The API is not merely a convenient way to retrieve data.

It is the boundary that allows the two halves of the application to evolve independently.

The architecture became:

                API CONTRACT
                     │
        ┌────────────┴────────────┐
        │                         │
     BACKEND                  FRONTEND
        │                         │
   WordPress                 PHP Application
        │                         │
   FullStack API          Controllers / Services
        │                         │
        └──────────────┬──────────┘
                       │
                    Browser

The frontend does not need to know how WordPress stores a post.

The backend does not need to know how the frontend renders one.

That separation is powerful.

If tomorrow we redesign the frontend completely, the backend does not necessarily need to change.

If one day we replace WordPress with our own backend, the frontend can potentially remain intact as long as the API contract remains compatible.

That possibility became important to our long-term vision.


4. The frontend gradually became an application

Initially, it would have been easy to write everything in index.php.

Fetch some data.

Loop through it.

Print HTML.

Done.

But that approach becomes increasingly difficult to maintain as the application grows.

So we started separating responsibilities.

The first important piece was the API client:

app/Api/ApiClient.php

Its responsibility was simple:

Communicate with the API.

It should not know how an article is rendered.

It should not know how a homepage is structured.

It should not contain application-specific business logic.

It simply talks to the backend.


5. Services came next

Once the API client existed, we introduced services.

For example:

app/Services/PostService.php
app/Services/AppService.php

The distinction was useful.

The API client knows how to make a request.

The service knows what kind of data the application is asking for.

So instead of a controller doing something like:

$api->get('/posts?...');

we could say:

$postService->latest(6);

or:

$postService->find($slug);

The application became easier to reason about.

We were slowly moving away from “a PHP script that happens to work” toward “a small application whose components have responsibilities.”


6. Routing made the frontend feel real

Then came routing.

We wanted URLs such as:

/articles/123

and eventually:

/articles/hello-world

That led to a dedicated router:

app/Routing/Router.php

The router became responsible for answering a very simple question:

Given this HTTP request, which part of the application should handle it?

Eventually, the flow became:

Browser
   ↓
/articles/hello-world
   ↓
Router
   ↓
ArticleController
   ↓
PostService
   ↓
ApiClient
   ↓
WordPress API

This was one of the most satisfying moments in the project.

The URL no longer felt like a collection of PHP files.

It became a route through an application.


7. Controllers gave the routes a home

We then introduced:

app/Controllers/HomeController.php
app/Controllers/ArticleController.php

The controllers became the application-facing layer.

The router decides where a request goes.

The controller decides what the application should do with it.

The service retrieves the required data.

The view renders the result.

That gave us a clean chain:

Request
  ↓
Router
  ↓
Controller
  ↓
Service
  ↓
API Client
  ↓
Backend

And on the way back:

Backend
  ↓
API Client
  ↓
Service
  ↓
Controller
  ↓
View
  ↓
Response

There is nothing particularly revolutionary about this architecture.

That is precisely why we like it.

It is understandable.


8. Views separated presentation

The next step was introducing:

app/View.php

and views such as:

views/home.php
views/article.php

This gave the application another useful boundary.

The controller should not contain large amounts of HTML.

The view should not be responsible for calling the API.

The service should not know anything about HTML.

The API client should not know anything about the browser.

Each layer has one primary concern.

The resulting structure became:

frontend/
├── app/
│   ├── Api/
│   ├── Controllers/
│   ├── Routing/
│   ├── Services/
│   ├── Application.php
│   └── View.php
│
├── config/
│   └── app.php
│
├── views/
│   ├── home.php
│   └── article.php
│
├── public/
│   └── index.php
│
└── composer.json

At this point, something had changed.

The project was no longer really a collection of PHP pages.

It had become a small framework-like application structure.


9. The front controller

Then we made another important architectural decision.

Instead of allowing multiple PHP files to act as entry points, we wanted one front controller:

public/index.php

Its job became beautifully small.

Conceptually:

require __DIR__ . '/../vendor/autoload.php';

$config = require __DIR__ . '/../config/app.php';

$app = new App\Application($config);

$app->run();

That reminded me of another ecosystem where application startup is deliberately simple:

app → run()

The similarity is conceptual rather than technological.

The front controller should not contain the application.

It should start the application.

So the request lifecycle became:

Browser
   ↓
public/index.php
   ↓
Application
   ↓
Router
   ↓
Controller
   ↓
Service
   ↓
API

The front controller became small because the architecture had become larger underneath it.

That is a good kind of smallness.


10. Composer completed the structure

Once the application had several classes, manually including files became increasingly unattractive.

So we introduced Composer and PSR-4 autoloading.

Our composer.json defines the application namespace, and Composer generates:

vendor/autoload.php

The application can now simply say:

require __DIR__ . '/../vendor/autoload.php';

and classes are discovered automatically.

We deliberately decided not to install Composer globally just for this project.

Our development environment already uses DDEV, so:

ddev composer install

and:

ddev composer dump-autoload

keep the development environment portable.

This is a small detail, but it fits the larger philosophy:

The project should carry as much of its environment as reasonably possible.


11. The backend remained independent

While the frontend was evolving, the WordPress backend was also becoming more clearly defined.

We created a custom plugin:

fullstack-app

The plugin exposes the API required by the frontend.

Among other things, it handles:

Posts
Applications
Status

The important part is that WordPress itself remains replaceable in principle.

The frontend does not say:

“I am a WordPress theme.”

It says:

“I consume this API.”

That distinction is fundamental.


12. The moment we tested the whole chain

Eventually we reached the point where a real request could travel through the entire system.

For example:

/articles/hello-world

could become:

Browser
    ↓
public/index.php
    ↓
Application
    ↓
Router
    ↓
ArticleController
    ↓
PostService
    ↓
ApiClient
    ↓
WordPress REST API
    ↓
FullStack plugin
    ↓
WordPress

The response then travels back through the same architecture in reverse.

At that point we had something more interesting than a demonstration.

We had a working headless application architecture.


13. Then came Jaroa

But there was still a problem.

The name.

headless-php-application was descriptive, but it described the implementation rather than the idea.

And we began thinking beyond the current implementation.

What if this eventually supported several frontend templates?

What if someone could choose:

Blog
Profile
Portfolio
Documentation
...

and simply provide the API endpoint?

What if installation became automated?

What if the backend could eventually be something other than WordPress?

What if a person with very little programming knowledge could assemble a useful web application without understanding the underlying architecture?

At that point, headless-php-application felt too small a name for what we were imagining.

We wanted something short.

Something independent of technology.

Something that did not say “PHP”, “WordPress”, “CMS”, “API”, or even “Web”.

We chose:

Jaroa

The name does not explain the technology.

That is intentional.

Jaroa is the name of the project, not the name of one implementation detail.


14. Why simplicity became the philosophy

The word “simple” can be misleading in software.

Simple does not necessarily mean:

few files

or:

few lines of code

A serious application may contain hundreds or thousands of files and still have a simple architecture.

What matters is whether the relationships between those pieces are understandable.

Our desired relationship is straightforward:

Frontend
    │
    │ API
    ▼
Backend

The frontend can evolve independently.

The backend can evolve independently.

The API remains the contract.

That is the simplicity we are pursuing.


15. The future Jaroa vision

Jaroa is still young.

At present, WordPress is doing the heavy lifting on the backend.

That is intentional.

WordPress gives us a mature foundation while we concentrate on the architecture around it.

But we have a longer-term ambition.

One day, perhaps years from now, we may build our own backend.

Not because WordPress is inadequate.

Rather, because building our own backend would be the natural continuation of understanding every layer of the system.

The evolution might eventually look something like:

TODAY

Jaroa Frontend
      │
      ▼
Jaroa API
      │
      ▼
WordPress

and perhaps, much later:

FUTURE

Jaroa Frontend
      │
      ▼
Jaroa API
      │
      ▼
Jaroa Backend
      │
      ▼
Database

The frontend architecture does not have to be thrown away when that happens.

That is one of the reasons the API boundary matters so much.


16. Templates are part of the vision

Another direction we want Jaroa to take is frontend templates.

Rather than asking everyone to build a frontend from scratch, Jaroa could eventually provide ready-made starting points.

For example:

Jaroa
│
├── Blog
├── Profile
├── Portfolio
├── Documentation
├── Magazine
└── ...

These would initially be built using straightforward:

PHP
HTML
CSS
JavaScript

No unnecessary framework dependency.

The goal is not to compete with every frontend framework.

The goal is to make building a useful web application less intimidating.

A person could choose a template, point it at an API, configure a few things, and have a functioning application.


17. And eventually, the installer

This is where the project becomes particularly interesting.

We have begun thinking about an installer that could eventually automate the entire process.

Conceptually:

Installer
    │
    ├── Create frontend
    │
    ├── Install dependencies
    │
    ├── Configure API endpoint
    │
    ├── Install WordPress
    │
    ├── Install FullStack plugin
    │
    └── Activate backend

The user should not have to understand all the pieces underneath.

Ideally, they should be able to say:

I want a blog.

And Jaroa should eventually be able to turn that intention into a working application.

That is still a future goal.

We are nowhere near claiming that Jaroa can do all of this today.

But having a destination matters.


18. From developer tool to something approachable

This is perhaps the most ambitious part of the vision.

We would eventually like Jaroa to be approachable even to someone who does not know how to program.

Not necessarily by hiding the technology.

Rather, by putting a friendly layer over it.

Imagine eventually being able to say:

Create a profile website.

Or:

Change the homepage layout.

Or:

Add a page called Projects.

Or:

Show my latest articles on the homepage.

The underlying system would still be PHP, APIs, databases, templates and configuration.

But the user would not necessarily need to manipulate all of those things manually.

That is a much larger undertaking.

And it may take years.

That’s perfectly fine.

Jaroa is intended to be an evolving project, not a weekend framework.


19. Why WordPress is still important

It might seem strange that a project dreaming about its own backend is currently using WordPress.

I don’t think it is strange at all.

WordPress is giving us something extremely valuable:

a working laboratory.

Instead of spending the first year building:

  • authentication
  • administration
  • database abstractions
  • content management
  • media handling
  • user management
  • permissions
  • editorial workflows

we can use WordPress as the backend while concentrating on the architectural boundaries.

That means we can learn what our frontend actually needs from a backend.

Eventually, those observations can inform a future Jaroa backend.

In that sense, WordPress is not the destination.

It is part of the road.


20. Portability became another principle

The project is also being developed with portability in mind.

We use:

Git
GitHub
DDEV
Docker
Composer
PHP
WordPress

The project can therefore be cloned onto another computer.

The source code lives in GitHub.

Generated dependencies such as Composer’s vendor/ directory are recreated rather than committed.

DDEV configuration is kept where it is useful for reproducing the development environment.

The result is a project that is not supposed to belong to one particular computer.

That matters.

A project should not depend on the accidental state of the laptop on which it was born.


21. The first Jaroa repository

After the architecture had stabilized, we created a new repository:

Jaroa

The old project:

headless-php-application

became the historical foundation.

The new repository became the home of the evolving project.

The structure now looks roughly like:

Jaroa/
│
├── backend/
│   ├── WordPress
│   └── fullstack-app
│
├── frontend/
│   ├── app/
│   │   ├── Api/
│   │   ├── Controllers/
│   │   ├── Routing/
│   │   ├── Services/
│   │   ├── Application.php
│   │   └── View.php
│   │
│   ├── config/
│   ├── views/
│   ├── public/
│   └── composer.json
│
├── README.md
└── .gitignore

It is still small.

But it has a direction.


22. This is only the first chapter

Jaroa is not finished.

That is perhaps the most important thing to say.

There will be more backend capabilities.

There will be more frontend templates.

There will be improvements to the API.

There will be an installer.

There will be deployment considerations.

There will probably be mistakes.

There will certainly be refactoring.

Some ideas will survive.

Others will disappear.

And the architecture may change substantially as our understanding improves.

That is normal.

The project will evolve in public, one step at a time.


23. We will document every step

One decision we have made is to document the journey as it happens.

This article is therefore not meant to be the final explanation of Jaroa.

It is the first chapter.

As the project grows, we will record:

  • architectural decisions
  • frontend development
  • backend development
  • API design
  • template creation
  • installer development
  • deployment
  • security
  • portability
  • performance
  • usability
  • experiments
  • failures
  • successful solutions
  • eventually, perhaps, the creation of a native Jaroa backend

The documentation will become a record of the project’s evolution.

Not merely a manual.

A history.


24. Why “Jaroa”?

Perhaps the simplest explanation is this:

We wanted a name that did not imprison the project inside its current technology.

headless-php-application described what we were building.

Jaroa describes what we hope it can become.

The technology underneath may change.

The backend may change.

The templates may change.

The installer may change.

The architecture may evolve.

But the underlying idea remains:

Make building a useful web application simpler without making the architecture careless.

That is what Jaroa is trying to become.


25. The road ahead

Today, the architecture is:

                JAROA
                  │
        ┌─────────┴─────────┐
        │                   │
     FRONTEND            BACKEND
        │                   │
   PHP Application       WordPress
        │                   │
        └────── API ────────┘

Tomorrow, we may have:

                JAROA
                  │
       ┌──────────┼──────────┐
       │          │          │
   Templates     API      Installer
       │          │          │
       └──────────┼──────────┘
                  │
               Backend

And perhaps much later:

                    JAROA
                      │
       ┌──────────────┼──────────────┐
       │              │              │
   Templates       Platform       Assistant
       │              │              │
       └──────────────┼──────────────┘
                      │
                Jaroa Backend
                      │
                   Database

Whether we eventually reach that final picture is unknown.

But that uncertainty is not a problem.

We have somewhere to begin.

And we have already begun.


Conclusion

Jaroa did not begin as a grand framework.

It began with a small question:

What if a lightweight PHP frontend could talk to WordPress through a clean API instead of being tied directly to WordPress?

That question led to another.

And another.

Eventually we had:

API
Services
Controllers
Router
Views
Application
Composer
Front Controller

And eventually we had a project with a name.

Jaroa.

The project is intentionally small.

The ambition is not.

For now, WordPress is our backend.

For now, PHP is our frontend technology.

For now, the templates are simple.

For now, installation still requires a developer.

But the direction is clear.

We want to build something that ordinary people can use without first becoming experts in everything underneath it.

And we want to do that without turning the architecture into a black box.

So this is not the story of a finished framework.

It is the beginning of a long experiment.

This is where Jaroa begins.