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.