Skip to content

Architecture

Ryan Shepherd edited this page May 21, 2026 · 5 revisions

Architecture

How HOF is built. Read this if you're contributing, extending, or just nosy.

the stack

Layer Tech Why
PHP architecture PSR-4 + Composer Real namespaces. Autoloading. IDE autocomplete that works.
Build tooling Vite Fast HMR, native ESM, no webpack pain
Frontend (facets) Preact + Signals ~4kb runtime, surgical reactivity
Admin UI React + @wordpress/components Native WP feel, zero design debt
Dev env docker-compose via OrbStack One command to a working WP+Woo
Database Custom indexed tables Optimized for facet lookups
Tests PHPUnit, Vitest, Playwright Unit, integration, end-to-end

why these choices

Preact + Signals, not React, on the frontend

React is ~45kb gzipped. Preact is ~4kb with the same JSX dev experience. On a product listing page that already has WooCommerce assets loaded, we're not adding a fifth of a megabyte just so filters can re-render. Signals give us granular reactivity without useEffect / useMemo gymnastics — when a facet value changes, only the components reading that signal re-render. The result: facets feel instant, even on a phone.

React + @wordpress/components in the admin

The admin is different. We need date pickers, modals, SlotFill, all the WordPress-native primitives. @wordpress/components ships them, auto-themes to WP version, and means the HOF admin feels like part of WordPress, not a wrapper grafted on. Zero design debt.

PSR-4 over the procedural plugin pattern

Because it's 2026. Because contributors deserve to find things. Because class Hooked_On_Facets_Public_Frontend_Helper_Functions_etc is a war crime.

Vite over webpack

Native ESM in dev. Build time you can ignore. HMR that actually works for plugin development. Webpack served us well; it's time to let it rest.

directory layout

hooked-on-facets/
├── composer.json
├── package.json
├── vite.config.ts
├── hooked-on-facets.php          # Plugin bootstrap
├── readme.txt                    # WP.org-style readme
├── src/
│   ├── Core/                     # Plugin lifecycle, container
│   ├── Indexer/                  # Index build + maintenance
│   ├── Query/                    # Facet query engine
│   ├── Facets/                   # Facet type registry
│   │   ├── Checkbox.php
│   │   ├── Radio.php
│   │   └── ...
│   ├── Admin/                    # Admin screens (PHP side)
│   ├── REST/                     # REST controllers
│   └── Integrations/             # Woo, ACF, page builders
├── frontend/                     # Preact + Signals app
│   ├── src/
│   ├── index.ts
│   └── components/
├── admin-app/                    # React + @wp/components app
│   ├── src/
│   └── index.tsx
├── tests/
│   ├── unit/
│   ├── integration/
│   └── e2e/
├── docker/
│   └── docker-compose.yml
└── docs/                         # Long-form docs (mirrored to wiki)

dev environment

A complete working WordPress + WooCommerce + MySQL 8 environment runs in Docker via OrbStack.

git clone https://github.com/Shepdesign/hooked-on-facets.git
cd hooked-on-facets
composer install
npm install
docker compose up -d
npm run dev

That gets you:

  • WordPress at http://localhost:8080
  • MySQL 8 at :3306
  • Vite dev server at :5173 with HMR
  • HOF mounted into wp-content/plugins/hooked-on-facets

Sample data (a Woo store with ~500 products across various categories) loads on first boot.

extensibility points

HOF exposes its surface area through:

  • PHP hooksapply_filters, do_action at all the points you'd want
  • JavaScript events — emitted on a window.hof event bus
  • REST endpoints — fully versioned, documented
  • Facet type registry — register your own facet type via PHP

Full reference: Developer Guide

Clone this wiki locally