We started with an older project called:

headless-php-application

That was our experimental house.

We then decided:

“This idea is worth becoming something of its own.”

So we created:

Jaroa

and pushed the first version to GitHub.

We are currently working on:

Jaroa 1.0

It is not a framework yet.

It is the first working application from which we are discovering what the framework should eventually become.

That distinction is very important.


1. What is Jaroa trying to do?

Imagine a restaurant.

There is a kitchen and there is a dining room.

The customers shouldn’t walk into the kitchen and start grabbing vegetables. 😄

We want the same separation in our web application.

Our current architecture is:

                    Jaroa
                      │
             ┌────────┴────────┐
             │                 │
          FRONTEND          BACKEND
             │                 │
        PHP application      WordPress
             │                 │
             └────── API ──────┘

The backend knows about content.

The frontend knows about presentation.

The API is the waiter carrying information between them.


2. What have we built so far?

Quite a lot, actually.

First: the frontend exists

We have:

frontend/
├── app/
├── config/
├── views/
├── public/
└── composer.json

And the important application pieces are:

app/
├── Api/
├── Controllers/
├── Routing/
├── Services/
├── Application.php
└── View.php

That’s already beginning to look like a proper application architecture.


3. The front door: public/index.php

This is where a browser request enters Jaroa.

Very simply:

Browser
   ↓
public/index.php
   ↓
Application

It loads Composer, loads configuration, creates the application and starts it.

So index.php is basically:

“Welcome! Let me wake up Jaroa.”


4. Application.php is the conductor

Application.php brings the important pieces together.

It creates:

ApiClient
PostService
AppService
Router
Controllers

and registers routes.

For example:

/

goes to:

HomeController

and:

/articles/{slug}

goes to:

ArticleController

So Application is currently saying:

“When somebody knocks on this door, send them to this person.”


5. Router

Our Router understands URLs.

For example:

/articles/my-post

becomes:

slug = my-post

The Router doesn’t fetch the article.

It doesn’t know anything about WordPress.

It simply says:

“This URL belongs to ArticleController.”

That’s good separation.


6. Controllers

We currently have:

HomeController
ArticleController

Think of controllers as waiters.

The HomeController says:

“Someone wants the homepage. I’ll ask for the latest posts and applications.”

The ArticleController says:

“Someone wants this particular article. I’ll ask for it.”

They shouldn’t know how WordPress stores the data.


7. Services

We have:

PostService
AppService

These are the people who know what information we need.

For example:

PostService
    ↓
latest posts
    ↓
find article
    ↓
search posts

The controller doesn’t need to know the API URL.

It simply says:

"PostService, please give me this article."

That’s a very useful separation.


8. ApiClient

Then we have:

ApiClient

This is the actual messenger.

It talks to:

https://backend.ddev.site/wp-json/fullstack/v1

and receives JSON.

So our chain is:

Controller
    ↓
Service
    ↓
ApiClient
    ↓
HTTPS
    ↓
WordPress API

The frontend never directly touches the WordPress database.

That’s one of the central ideas of Jaroa.


9. We also built proper error handling

This was an important little milestone.

Previously, a missing route produced an ugly PHP fatal error.

Now we have:

NotFoundException

and:

404.php

So:

Unknown route
      ↓
NotFoundException
      ↓
Application
      ↓
404.php

And a missing article follows the same path:

Article doesn't exist
      ↓
API returns 404
      ↓
NotFoundException
      ↓
Application
      ↓
404.php

So we don’t have two different ways of saying:

“Sorry, we couldn’t find that.”

We have one.

That’s much better.


10. We also created a view system

Our View class takes data and renders PHP templates.

We also added shared view information.

So:

config/app.php

contains:

'name' => 'Jaroa'

and that information can reach our views.

Therefore:

home.php
article.php
404.php

can all know:

Jaroa

without each one inventing its own application name.

That’s our first tiny piece of reusable infrastructure.


11. We have now started a layout system

This is where we are right now.

We created:

frontend/views/layouts/app.php

and taught View that a page can eventually be rendered inside a layout.

We successfully converted the homepage.

So now:

HomeController
      ↓
View
      ↓
home.php
      ↓
app.php
      ↓
Browser

The layout owns the common HTML structure.

That’s important because eventually we don’t want every page repeating:

<html>
<head>
<body>
<header>
<footer>

over and over.


12. We also made article navigation work

The homepage gets:

slug = my-post

from the API.

We then create:

/articles/my-post

rather than using WordPress’s backend URL.

That means Jaroa owns its frontend URLs.

This is exactly what we want from a decoupled architecture.