Support Class Inventory & Anti-Patterns
Reference for the *Support layer in middag-io/moodle — the static, stateless facade that sits between the boundary and native Moodle APIs, one class per Moodle subsystem.
What a *Support class is
| Property | Rule |
|---|---|
| Scope | One class per Moodle subsystem (db_support, cache_support, context_support, …) |
| State | None — static, stateless methods only |
| Logic | No business logic — pure interface translation, never a behavior decision |
| Return type | Typed (?Type), converting Moodle's false/stdClass results |
| Dependencies | shared/-equivalent code only — the one exception is the router bridge support, which reaches the kernel router for URL generation |
| Admission threshold | 2+ real call sites in the framework consuming the same subsystem; a single-call convenience method is accepted only when no type transformation is involved |
Legacy inventory — 40 support classes across 8 groups
The moodle-local_middag legacy plugin grouped its 40 support classes as follows; all were @internal, consumed only through a generated facade mirror.
| Group | Support classes |
|---|---|
| Data/Persistence | db_support, cache_support, config_support, preference_support |
| Auth/Authorization | auth_support, capability_support, session_support, role_support |
| Context/Entities | context_support, user_support, course_support, category_support, group_support, cohort_support, enrol_support, custom_field_support, competency_support |
| Content/Files | file_support, grade_support |
| Presentation/UI | page_support, output_support, html_writer_support, url_support, lang_support |
| Communication/Events | message_support, event_support, notification_support, calendar_support |
| Runtime/Infra | task_support, plugin_support, request_support, version_support, time_support, lock_support, check_support, settings_support |
| Bridge | di_bridge_support, router_bridge_support |
Current inventory in this adapter (middag-io/moodle)
The OSS extraction grew the inventory to 45 *Support classes under src/Support/, plus Support/Moodle — a static aggregator that returns fresh *Support instances, rather than a generated per-class facade.
The legacy facade-generation mechanism (build:facades, one mirror class per support) is not part of this OSS adapter — it is a consumer-product concern. MIDDAG product facades are generated by dev-tools inside the consumer plugin ({component}\facade\...), never inside this package. Consequence: importing Support\*Support classes directly from this package is not an anti-pattern the way it was in the legacy plugin — there is no OSS-side facade layer to bypass here. The facade-bypass anti-pattern (below) is scoped to whichever facade convention a consumer project has adopted on top of this package, not to this package's own usage.
Auth/Capability — dual consumption path
AuthSupport/CapabilitySupport-equivalent classes still follow the *Support pattern, but consumption is deliberately split by context:
| Path | Mechanism | When to use |
|---|---|---|
| DI (preferred) | Security\Authentication / Security\Authorizer / Security\Capability contracts, in src/Security/Contract/ | Controllers, services, extensions — anywhere DI is available |
| Facade / direct call (fallback) | Direct *Support static call | Procedural contexts only: lib.php, navigation hooks, install/upgrade |
Anti-patterns
| Anti-pattern | Why it's wrong |
|---|---|
Direct import of a support-equivalent class from a consumer extension, bypassing that consumer's own facade convention | Defeats the indirection the consumer's facade layer exists to provide |
| A support class carrying business logic | Should be a pure adapter — logic belongs in a service or domain layer |
Calling a Moodle API ($DB, $PAGE, …) outside a *Support class | Bypasses the boundary whitelist entirely |
| Creating a support class with only 1 call site | Below the 2+ real call-site admission threshold |
| Using the auth/capability facade fallback inside a controller instead of DI | Controllers always have DI available — no reason to fall back |
A support class depending on domain/, service/, or kernel/-equivalent layers | Only shared/-equivalent code is allowed (router bridge support is the sole documented exception) |
Decision record: MDL-003 — Support Layer Pattern.