What a module is and provides

A module encapsulates one concern and everything that concern needs, and exposes a small interface the rest of the site calls. You build an application not by writing one large program but from modules that slot together, so the shape of an application is simply the set of modules it includes.

One concern, one module

A module owns a single piece of functionality and all of it: its data objects, its database tables, its pages and routing, its configuration, and the behaviour that ties them together. Nothing about that concern lives outside the module, and the module holds nothing that belongs to another concern. MemberFolder, for example, owns member-owned folder trees and nothing else - it stores folder structure and knows how to route a folder, but it stores no membership and knows nothing about the rows other modules keep in those folders.

Because every module has the same layout and the same kind of surface, once you have read one you can find your way around any of them. There is no framework core running behind the scenes; an application is the list of modules it includes, and each module is a self-contained unit you can read on its own.

What a module provides

A module exposes an interface other modules use. It provides some or all of:

  • Static functions, grouped on classes. Almost every class is a class of static functions; only Data objects, Union objects, and Exceptions are ever constructed with new. See Objects and types.
  • Data and Union objects that carry its values across boundaries: a Data object holds fields and nothing else, a Union holds exactly one of a set of variants.
  • Its own Exceptions, so callers can catch failures by type rather than thread checks through the happy path.
  • User interface and routing other modules can mount and reuse - the pages and anchors the concern owns.
  • Config classes whose functions let other modules register with it and extend what it does. See Config classes.

A module rarely offers all of these. A pure data module may expose only static functions and Data objects; a feature module adds UI, routing, and a config class so others can plug into it.

Dependencies run one way

A module relies on some other modules and requires them, listing each one with require_once at the top of its index file. It never hopes a dependency was pulled in by something else; it includes what it uses, directly and unconditionally. So MemberFolder requires Member, Database, Ui, Permalink and the rest at the top of its own index.php, and calls them knowing they are loaded.

Dependencies run one way and never form a cycle: if module A requires B, then B does not require A. That keeps the dependency graph a tree you can load in a single pass, and it keeps each module reusable on its own - a general module such as MemberFolder never depends on the application modules that use it, which is exactly why any of them can.

Encapsulation is the point

Because a module keeps its concern whole and hides everything else behind a small surface, it can be understood, changed, or reused without reading the rest of the site. Other modules see only the functions, objects and config the module chose to expose; they never reach into its tables or internals. That boundary is what lets a codebase stay readable as it grows: each concern is a box with a labelled interface, and the application is those boxes wired together. How they wire together, and how one extends another without breaking the boundary, is Loose coupling and extension.

Next: Module layout and the index file.