Skip to main content

StoreEngine Architecture Overview

StoreEngine is layered: a small bootstrap boots the engine, an addon framework loads features on demand, a PSR-4 autoloader wires up classes, and everything sits on a custom-table data layer with a REST API, hooks system, and a template-driven frontend. This page explains each layer and how they connect.

The layers

Bootstrap: the StoreEngine singleton

The main file storeengine.php defines a final class StoreEngine singleton. Its constructor drives startup in a deliberate order:

  1. Define constants — paths, URLs, version, and table names.
  2. Load dependencies — pull in the autoloader and Composer's vendor autoload. If core files are missing (e.g. an interrupted update), it bails with an admin notice rather than fataling.
  3. Register lifecycle hooksregister_activation_hook, register_deactivation_hook, and a shutdown error logger.
  4. Boot settings and installerSettings::load_settings() and StoreEngine\Installer::init() create/upgrade tables and register options.
  5. Initialize subsystems on WordPress hooks — addons, REST API, admin, AJAX, frontend, hooks, integrations, schedules, shortcodes, and blocks.
final class StoreEngine {
protected static ?StoreEngine $instance = null;

public ?Customer $customer = null;
public ?Cart $cart = null;
// …
}

The singleton is the composition root: it is the object other subsystems reach for the current cart, customer, and shared services.

The addon framework

The defining architectural choice: core features and commerce features ship as addons that can be toggled. Payment gateways (Stripe, PayPal, Razorpay, Paddle), subscriptions, memberships, affiliates, multi-currency, invoicing, and more are all addons living under addons/.

\StoreEngine\Addons loads them. Its addons_loader() reads a slug → class map through the storeengine/addons/loader_args filter, then for each addon:

  • registers the addon's namespace and directory with the autoloader,
  • initializes the addon's main class (which extends \StoreEngine\Classes\AbstractAddon) and calls run(),
  • tracks active addons so their schema is kept in sync.

Only active addons participate in schema syncing. Every addon declares a get_db_version(); the manager compares it against the single storeengine_addons_db_version option and reruns install_tables() on a mismatch — no per-addon option or bespoke upgrade hook required.

Extending the addon roster

Because the addon map runs through storeengine/addons/loader_args, a third-party plugin can register its own addon into StoreEngine's framework by filtering that array. See Building Addons.

Pro reuses this exact machinery via its own storeengine_pro/addons/loader_args filter and shares the core's \StoreEngine\Addons::sync_schema_for(). See Free vs Pro.

PSR-4 autoloading

Classes load through includes/autoload.php (StoreEngine\Autoload), a lightweight PSR-4 resolver kept as a singleton via Autoload::get_instance(). It maps namespaces to directories:

private array $autoload_directories = [
'StoreEngine' => STOREENGINE_ROOT_DIR_PATH . 'includes/',
'StoreEngine\Payment\Gateways' => STOREENGINE_INCLUDES_DIR_PATH . 'payment-gateways/',
];

Class names are transformed to lowercase, hyphenated file paths (e.g. StoreEngine\Classes\Cartincludes/classes/cart.php). Addons extend the map at runtime:

Autoload::get_instance()->add_namespace_directory(
'StoreEnginePro\\Addons\\Subscription',
STOREENGINE_PRO_ADDONS_DIR_PATH . 'subscription/'
);

Third-party Composer packages are vendored and Strauss-prefixed to avoid collisions with other plugins, loaded via Composer's own autoloader.

Data layer: custom tables + CPTs

StoreEngine splits persistence by access pattern:

  • Custom tables (wp_storeengine_*) back high-volume, relational data — orders, order items, order meta, addresses, carts, subscriptions, payment tokens, product prices/variations, shipping zones, and more. Schemas live as create-*.php files under includes/database/.
  • Custom post types back editable catalog entities — storeengine_product and storeengine_coupon — so they use the standard WordPress editor and taxonomy machinery.

Domain classes (includes/classes/) and models (includes/models/) provide the object API over this storage. Read and write orders through these, never through WP_Query.

warning

Orders and subscriptions are rows in storeengine_orders, discriminated by a type column. Treat post-based order queries from other plugins as inapplicable here. See Data & Objects.

REST API

The REST layer (includes/api/, bootstrapped by \StoreEngine\API) registers controllers under the storeengine/v1 namespace — cart, checkout, orders, products, customers, payments, payment methods, settings, shipping, coupons, analytics, and storefront auth, among others. It powers the admin React app, the storefront, and external integrations alike. See REST API.

Hooks system

StoreEngine exposes an extensive set of actions and filters namespaced under storeengine/…. Addons and integrations hook the commerce lifecycle — cart changes, checkout, order status transitions, payments — to add behavior without patching core. The addon loader map itself (storeengine/addons/loader_args) is a filter. See Actions and Filters.

Frontend

The storefront is rendered through three cooperating mechanisms:

  • Templates — PHP partials in templates/, overridable from a theme at yourtheme/storeengine/{template}.php. See Templates.
  • Shortcodesincludes/shortcode/ renders store surfaces (checkout, account, product listings) that can be dropped into any page.
  • Blocksincludes/blocks/ bridges shortcodes into Gutenberg blocks so the same surfaces are available in the block editor.

Where to go next