From 2fb2ca392de88afc5973a4e8a74a4ca052545362 Mon Sep 17 00:00:00 2001 From: James Griffin Date: Mon, 30 Mar 2026 16:37:30 -0300 Subject: [PATCH] Restructure src/ and tests/ from package-by-type to package-by-domain All classes are now organised by domain (Availability, Booking, Auth). Each domain package contains its value object, repository, admin controller, REST endpoint, and any shortcode pages under a matching sub-namespace. Cross-cutting wiring (Plugin, AdminMenu, RestRegistrar, ShortcodeRegistrar, Schema) lives at src/ root. Tests mirror the domain structure. Co-Authored-By: Claude Sonnet 4.6 --- CLAUDE.md | 61 ++++++++++++------- memory/MEMORY.md | 1 + memory/feedback_architecture.md | 16 +++++ memory/project_schedular.md | 3 +- src/{Admin => }/AdminMenu.php | 10 +-- src/{Frontend => Auth}/LoginPage.php | 2 +- src/{Roles => Auth}/RoleManager.php | 2 +- .../AvailabilityController.php | 6 +- .../AvailabilityEndpoint.php | 6 +- .../AvailabilityRepository.php | 4 +- .../AvailabilitySlot.php | 2 +- src/{Api => Booking}/BookingEndpoint.php | 8 +-- src/{Frontend => Booking}/BookingPage.php | 4 +- src/{Data => Booking}/BookingRepository.php | 4 +- src/{Model => Booking}/Lesson.php | 2 +- src/{Admin => Booking}/LessonController.php | 5 +- src/Installer.php | 3 +- src/Plugin.php | 9 +-- src/{Api => }/RestRegistrar.php | 8 ++- src/{Data => }/Schema.php | 2 +- src/{Frontend => }/ShortcodeRegistrar.php | 5 +- .../Unit/{Roles => Auth}/RoleManagerTest.php | 4 +- .../AvailabilityRepositoryTest.php | 10 +-- .../AvailabilitySlotTest.php | 4 +- .../BookingRepositoryTest.php | 6 +- tests/Unit/{Model => Booking}/LessonTest.php | 4 +- 26 files changed, 108 insertions(+), 83 deletions(-) create mode 100644 memory/feedback_architecture.md rename src/{Admin => }/AdminMenu.php (82%) rename src/{Frontend => Auth}/LoginPage.php (97%) rename src/{Roles => Auth}/RoleManager.php (96%) rename src/{Admin => Availability}/AvailabilityController.php (89%) rename src/{Api => Availability}/AvailabilityEndpoint.php (94%) rename src/{Data => Availability}/AvailabilityRepository.php (96%) rename src/{Model => Availability}/AvailabilitySlot.php (94%) rename src/{Api => Booking}/BookingEndpoint.php (94%) rename src/{Frontend => Booking}/BookingPage.php (91%) rename src/{Data => Booking}/BookingRepository.php (97%) rename src/{Model => Booking}/Lesson.php (96%) rename src/{Admin => Booking}/LessonController.php (85%) rename src/{Api => }/RestRegistrar.php (73%) rename src/{Data => }/Schema.php (97%) rename src/{Frontend => }/ShortcodeRegistrar.php (87%) rename tests/Unit/{Roles => Auth}/RoleManagerTest.php (95%) rename tests/Unit/{Data => Availability}/AvailabilityRepositoryTest.php (93%) rename tests/Unit/{Model => Availability}/AvailabilitySlotTest.php (94%) rename tests/Unit/{Data => Booking}/BookingRepositoryTest.php (96%) rename tests/Unit/{Model => Booking}/LessonTest.php (95%) diff --git a/CLAUDE.md b/CLAUDE.md index f2a7488..451d211 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -13,7 +13,7 @@ composer cs # PHPCS coding standards check composer cs:fix # Auto-fix coding standards # Run a single test file -./vendor/bin/phpunit tests/Unit/Data/AvailabilityRepositoryTest.php +./vendor/bin/phpunit tests/Unit/Availability/AvailabilityRepositoryTest.php # Run a single test by name ./vendor/bin/phpunit --filter testInsertCallsWpdbInsertAndReturnsId @@ -28,19 +28,33 @@ composer cs:fix # Auto-fix coding standards ### Directory Structure ``` -src/ — All plugin PHP (PSR-4 namespace: Unsupervised\Schedular\) -templates/ — PHP view files included by controllers/shortcodes -assets/ — CSS and JS (vanilla JS, no build step) -tests/Unit/ — PHPUnit unit tests (PSR-4: Unsupervised\Schedular\Tests\) -docs/features/— One markdown file per feature describing data model, API, and test locations +src/ — All plugin PHP (PSR-4 namespace: Unsupervised\Schedular\) + Availability/ — Availability slots: value object, repository, controller, REST endpoint + Booking/ — Lessons/bookings: value object, repository, controller, REST endpoint, shortcode page + Auth/ — Roles, capabilities, login page + Plugin.php — Wires all components together on plugins_loaded + Installer.php — Creates DB tables and roles on activation + Schema.php — CREATE TABLE SQL for dbDelta + AdminMenu.php — Registers wp-admin menu pages + RestRegistrar.php — Registers all REST routes under us-scheduler/v1 + ShortcodeRegistrar.php — Registers [us_booking] and [us_student_login] shortcodes +templates/ — PHP view files included by controllers/shortcodes +assets/ — CSS and JS (vanilla JS, no build step) +tests/Unit/ — PHPUnit unit tests (PSR-4: Unsupervised\Schedular\Tests\) + Availability/ — Tests for src/Availability/ + Booking/ — Tests for src/Booking/ + Auth/ — Tests for src/Auth/ +docs/features/ — One markdown file per feature describing data model, API, and test locations ``` +**Code is organised package-by-domain** (Availability, Booking, Auth). Each domain package contains everything related to that domain: value objects, repositories, controllers, REST endpoints, and shortcode pages. Cross-cutting wiring classes (Plugin, AdminMenu, RestRegistrar, ShortcodeRegistrar, Schema) live directly under `src/`. + ### Data Storage Two custom database tables (created via `dbDelta` on activation): - `{prefix}us_availability` — instructor availability windows - `{prefix}us_lessons` — booked lessons -All database access goes through repository classes in `src/Data/`. No direct `$wpdb` calls outside repositories. +All database access goes through repository classes within their domain package. No direct `$wpdb` calls outside repositories. ### Key Classes @@ -48,20 +62,21 @@ All database access goes through repository classes in `src/Data/`. No direct `$ |---|---| | `Plugin` | Wires all components together on `plugins_loaded` | | `Installer` | Creates DB tables and roles on activation | -| `Roles\RoleManager` | Registers `us_instructor` and `us_student` roles with custom caps | -| `Data\AvailabilityRepository` | CRUD for availability slots | -| `Data\BookingRepository` | CRUD for lesson bookings | -| `Model\AvailabilitySlot` | Immutable value object for a slot row | -| `Model\Lesson` | Immutable value object for a lesson row | -| `Admin\AdminMenu` | Registers wp-admin menu pages | -| `Admin\AvailabilityController` | Instructor availability management page | -| `Admin\LessonController` | Admin and instructor lesson list pages | -| `Api\RestRegistrar` | Registers all REST routes under `us-scheduler/v1` | -| `Api\AvailabilityEndpoint` | REST handlers for availability CRUD | -| `Api\BookingEndpoint` | REST handlers for booking and status updates | -| `Frontend\ShortcodeRegistrar` | Registers `[us_booking]` and `[us_student_login]` shortcodes | -| `Frontend\BookingPage` | Renders student booking UI shell (JS takes over) | -| `Frontend\LoginPage` | Renders front-end student login form | +| `Schema` | CREATE TABLE SQL strings for dbDelta | +| `AdminMenu` | Registers wp-admin menu pages | +| `RestRegistrar` | Registers all REST routes under `us-scheduler/v1` | +| `ShortcodeRegistrar` | Registers `[us_booking]` and `[us_student_login]` shortcodes | +| `Auth\RoleManager` | Registers `us_instructor` and `us_student` roles with custom caps | +| `Auth\LoginPage` | Renders front-end student login form | +| `Availability\AvailabilitySlot` | Immutable value object for a slot row | +| `Availability\AvailabilityRepository` | CRUD for availability slots | +| `Availability\AvailabilityController` | Instructor availability management page | +| `Availability\AvailabilityEndpoint` | REST handlers for availability CRUD | +| `Booking\Lesson` | Immutable value object for a lesson row | +| `Booking\BookingRepository` | CRUD for lesson bookings | +| `Booking\BookingEndpoint` | REST handlers for booking and status updates | +| `Booking\BookingPage` | Renders student booking UI shell (JS takes over) | +| `Booking\LessonController` | Admin and instructor lesson list pages | ### REST API Namespace All endpoints live under `/wp-json/us-scheduler/v1/`. Permissions are enforced via `permission_callback` using capability checks (`manage_availability`, `book_lesson`), never role name checks. @@ -81,9 +96,9 @@ All test classes extend `tests/Unit/TestCase.php`, which handles `Monkey\setUp() ### Adding a Feature 1. Write the feature doc in `docs/features/.md` (data model, API, classes, test paths). -2. Implement the classes under `src/`. +2. Create a domain package under `src//` containing all classes for that feature. 3. Add template(s) under `templates/` if needed. -4. Write unit tests under `tests/Unit/` mirroring the `src/` directory structure. +4. Write unit tests under `tests/Unit//` mirroring the `src//` structure. 5. Run `composer test` — all tests must pass before the feature is complete. ### CI diff --git a/memory/MEMORY.md b/memory/MEMORY.md index c57c5df..fccfd19 100644 --- a/memory/MEMORY.md +++ b/memory/MEMORY.md @@ -2,3 +2,4 @@ - [Project: unsupervised-schedular](project_schedular.md) — WordPress lesson scheduling plugin; initial scaffold created March 2026 - [Feedback: Brain\Monkey testing patterns](feedback_brainmonkey.md) — specific API quirks discovered during test setup +- [Feedback: Package-by-domain architecture](feedback_architecture.md) — all new code must be organised by domain, never by type diff --git a/memory/feedback_architecture.md b/memory/feedback_architecture.md new file mode 100644 index 0000000..f068ebf --- /dev/null +++ b/memory/feedback_architecture.md @@ -0,0 +1,16 @@ +--- +name: Package-by-domain architecture +description: New features must be organised as domain packages, not by type (no Admin/, Api/, Data/ etc.) +type: feedback +--- + +Always organise code package-by-domain, not package-by-type. + +**Why:** User explicitly requested the restructure on 2026-03-30. Package-by-type (Admin/, Api/, Data/, Frontend/, Model/, Roles/) was the initial scaffold but was replaced before any real features shipped. + +**How to apply:** +- New domain → new directory under `src//` (e.g. `src/Availability/`, `src/Booking/`, `src/Auth/`) +- All classes for a domain (value objects, repositories, admin controllers, REST endpoints, shortcode pages) live inside that directory under the sub-namespace `Unsupervised\Schedular\\` +- Cross-cutting wiring that glues domains together (Plugin, AdminMenu, RestRegistrar, ShortcodeRegistrar, Schema) lives at `src/` root with namespace `Unsupervised\Schedular\` +- Tests mirror the domain structure: `tests/Unit//` +- Never create `src/Admin/`, `src/Api/`, `src/Data/`, `src/Frontend/`, `src/Model/`, or `src/Roles/` directories diff --git a/memory/project_schedular.md b/memory/project_schedular.md index 0372c82..6ea7f80 100644 --- a/memory/project_schedular.md +++ b/memory/project_schedular.md @@ -15,5 +15,6 @@ WordPress plugin for instructor/student lesson scheduling. Full scaffold created - REST API (`us-scheduler/v1`) for all front-end interactions; templates are minimal shell divs, JS (vanilla) takes over - Instructors use wp-admin login; students use front-end `[us_student_login]` shortcode calling `wp_signon()` - PSR-4 namespace `Unsupervised\Schedular\` from `src/` +- **Package-by-domain architecture** (restructured 2026-03-30): `src/Availability/`, `src/Booking/`, `src/Auth/` — each domain contains its value object, repository, controller, REST endpoint, and any shortcode pages. Cross-cutting wiring (Plugin, AdminMenu, RestRegistrar, ShortcodeRegistrar, Schema) lives at `src/` root. Tests mirror the domain structure under `tests/Unit//`. -**How to apply:** When adding features, follow the docs/features/ + src/ + tests/Unit/ pattern. Always run `composer test` after changes. +**How to apply:** When adding features, create a domain package under `src//` with all related classes, mirror it in `tests/Unit//`, write a feature doc in `docs/features/`, then run `composer test`. diff --git a/src/Admin/AdminMenu.php b/src/AdminMenu.php similarity index 82% rename from src/Admin/AdminMenu.php rename to src/AdminMenu.php index bce8c4f..bcdb486 100644 --- a/src/Admin/AdminMenu.php +++ b/src/AdminMenu.php @@ -1,11 +1,13 @@ db = Mockery::mock(\wpdb::class); + $this->db = Mockery::mock(\wpdb::class); $this->db->prefix = 'wp_'; - $this->repo = new AvailabilityRepository($this->db); + $this->repo = new AvailabilityRepository($this->db); } public function testInsertCallsWpdbInsertAndReturnsId(): void diff --git a/tests/Unit/Model/AvailabilitySlotTest.php b/tests/Unit/Availability/AvailabilitySlotTest.php similarity index 94% rename from tests/Unit/Model/AvailabilitySlotTest.php rename to tests/Unit/Availability/AvailabilitySlotTest.php index 441fae9..81b9487 100644 --- a/tests/Unit/Model/AvailabilitySlotTest.php +++ b/tests/Unit/Availability/AvailabilitySlotTest.php @@ -1,9 +1,9 @@