Design philosophy

The framework is built on a few ideas that hold everywhere. Learn them once and the rest of the guide reads as their consequences: small modules that stay independent, a single server-rendered application, declarative code worked through provided interfaces, and plain typed objects with few surprises.

Built from small modules

A site is assembled from modules, each owning one concern and everything that concern needs: its database tables, its data objects, its pages and routing, and its configuration. There is no framework core running behind the scenes; the shape of an application is simply the list of modules it includes. Because every module has the same layout, once you have read one you can find your way around any of them. The Modularity section covers what a module is and how they fit together.

Loosely coupled

Modules stay independent. A module includes exactly the other modules it uses and configures them by calling their own configuration functions; there is no global service container and no hidden autowiring. Dependencies run one way and never form a cycle, and one module extends another only through a small, declared surface. This is what lets a module be understood, changed, or reused on its own. See Loose coupling and extension.

Server-rendered, from one entry point

A site is a single PHP application served by Apache. Every request that is not a real file reaches one entry point, which includes the modules and hands control to the framework's routing; the page is rendered on the server and sent as HTML. The same set of modules also serves a JSON Api and runs background work. The Site structure and Routing pages show how a request becomes a page.

Declarative

You declare what you want and let the module work out how. Routing and the user interface are the clearest examples: a module declares its routes and describes its pages rather than writing the step-by-step each time. To keep that style you work through the provided interfaces instead of raw control flow, using the iteration helpers in Collection and the branching helpers in Logic rather than loops and if statements. Because the work is declared through those interfaces rather than run directly, the framework can later gather it up and emit it as JavaScript for the front end from the same PHP. The Declarative code standard explains this in full.

Typed objects, few surprises

Data moves through the system as small immutable objects: a Data object holds fields and nothing else, and a Union object holds exactly one of a set of variants. These, along with exceptions, are the only objects ever constructed; everything else is a class of static functions, and the framework does not use PHP namespaces. Every function is fully typed and documented, anything that may be absent is named with an optional prefix, and code prefers to throw when data is missing rather than thread null checks through the happy path. The Objects and types and Errors and the happy path pages set this out.

Opinionated on purpose

The framework asks you to lay things out exactly as it expects. That consistency is the point: a pattern you learn in one module holds in every other, which is what lets a codebase stay readable as it grows. The Coding standards gather these conventions in one place, each with the reasoning attached, so they read as a single coherent way of working rather than a list to memorise.

Next: Running locally.