Files
2026-05-29 04:33:03 -04:00

290 lines
13 KiB
Markdown

# Laravel SchoolContext Phase 1 Implementation
This package-style scaffold implements Phase 1 of the SchoolContext foundation for a Laravel app.
It includes:
- immutable `SchoolContext` value object;
- context factory, resolver, validator, store, and exception;
- `ResolveSchoolContext` middleware;
- service provider bindings;
- helper for legacy adapters only;
- query scoping trait;
- first student read/list slice;
- payment-file access slice that resolves files through payment context, not arbitrary filenames;
- context payload helper for jobs/events;
- unit, feature, and architecture tests.
## Install into an existing Laravel app
Copy the `app`, `config`, `routes`, and `tests` folders into your Laravel project.
Register the provider in `config/app.php` if your Laravel version does not auto-discover app providers:
```php
App\Providers\SchoolContextServiceProvider::class,
```
For Laravel 10 and older, you may also register the middleware alias in `app/Http/Kernel.php`:
```php
protected $middlewareAliases = [
'school.context' => \App\Http\Middleware\ResolveSchoolContext::class,
];
```
For Laravel 11+, register it in `bootstrap/app.php`:
```php
->withMiddleware(function (Middleware $middleware) {
$middleware->alias([
'school.context' => \App\Http\Middleware\ResolveSchoolContext::class,
]);
})
```
Load the example routes only after adapting controllers/model names to your app:
```php
require base_path('routes/school_context_examples.php');
```
## Required model assumptions
This scaffold expects these model concepts to exist or be adapted:
- `App\Models\School` with optional `timezone`, `locale`, `currency`, `domain_profile`, `active` fields.
- `App\Models\Configuration::getConfig($key)` during migration only.
- `App\Models\Student` with `school_id`.
- `App\Models\Payment` with `school_id` and a `files()` relationship.
- user model with `school_id`, optional `role_id`, optional `roles()`, optional `schools()`, optional `is_platform_admin`.
## Migration policy
New SchoolCore services should accept `SchoolContext` explicitly. Do not call `auth()`, `request()`, or `Configuration::getConfig()` inside SchoolCore services. The architecture test exists to catch that predictable human shortcut.
## Route middleware order
Use this order:
```txt
auth:api
school.context
permission/policy/controller
```
Do not run `school.context` before authentication for protected routes.
## Important note
`SchoolContext::isIslamicSundaySchool()` exists as a convenience check, but SchoolCore should not branch into Islamic-specific concepts. Domain-specific behavior belongs in policies, bindings, labels, calendars, and extension providers.
# Phase 2 Add-on: Core Contracts and Module Boundaries
This scaffold now includes the Phase 2 contract layer:
- neutral `SchoolCore` contracts for students, guardians, enrollment, attendance, academics, finance, files, communication, and reporting;
- immutable DTO/read-model skeletons for cross-module payloads;
- `SchoolCoreServiceProvider` with default core bindings;
- `IslamicSundaySchoolServiceProvider` with extension policy/provider overrides;
- race-safe student identifier generation through `StudentIdentifierGeneratorContract`;
- payment file access through `FileAccessPolicyContract` and `FileStorageServiceContract`;
- attendance policy and academic calendar provider stubs;
- architecture tests for forbidden dependency direction, raw request leakage, `auth()` leakage, and non-neutral contract vocabulary;
- a migration stub for `unique(school_id, school_id_number)`.
## Register Phase 2 providers
Add these after `SchoolContextServiceProvider`:
```php
App\Providers\SchoolCoreServiceProvider::class,
App\Providers\IslamicSundaySchoolServiceProvider::class,
```
Enable the extension policy overrides only when the school profile requires them:
```env
ISLAMIC_SUNDAY_SCHOOL_EXTENSION_ENABLED=true
```
## Student identifier slice
`StudentService` creates the student row first, then calls `StudentIdentifierGeneratorContract` with the persisted ID. This avoids the classic `max(id) + 1` race. The migration stub adds a database unique constraint, because relying on application code alone for uniqueness is how duplicates sneak in wearing a fake mustache.
## Payment file access slice
Use payment IDs and stored file references. Do not serve files by guessed filename. The policy checks school context first, then role/relationship metadata. Adapt relationship metadata to your actual finance schema.
## Attendance policy stub
Core scanner logic should call `AttendancePolicyContract` and `AcademicCalendarProviderContract`. The extension provider supplies domain-specific attendance behavior without making `SchoolCore` speak extension vocabulary.
## Phase 3: Modular Finance Extraction
Phase 3 adds a reusable `SchoolCore\Finance` module. The core owns financial correctness: invoices, payments, refunds, payment files, audit events, balance calculation, idempotency, and transaction boundaries. Islamic Sunday School behavior is extension-owned through finance policies and reports.
Register these providers after the Phase 1/2 providers:
```php
App\Providers\SchoolCoreFinanceServiceProvider::class,
App\Domain\IslamicSundaySchool\Finance\Providers\IslamicSundaySchoolFinanceServiceProvider::class,
```
Add `routes/finance_phase3.php` to your route loader, then migrate one vertical slice at a time. Start with payment-file access, then payment recording, payment editing, invoice recalculation, refunds/reversals, and only then reports. Reports consume finance truth. They do not invent it, despite humanity's long-running spreadsheet addiction.
Important integration notes:
- Finance services require `SchoolContext`.
- Finance mutation services use `FinanceTransactionRunner`.
- Payment files are downloaded by payment ID, never by arbitrary filename.
- `Money` stores integer minor units and rejects float math.
- `finance_audit_logs` are append-only.
- Existing legacy finance methods should be labeled `canonical`, `adapter`, `deprecated`, or `remove` before migration continues.
## Phase 4: Modular Attendance and Scanner Extraction
Phase 4 adds `SchoolCore\\Attendance` as the neutral attendance/scanner module. It includes scanner delegation, school-scoped badge lookup, scan idempotency, student/staff attendance services, session resolution, status policies, audit logging, migrations, HTTP requests/controllers, route examples, provider bindings, and Islamic Sunday School extension overrides.
Register these providers after the base SchoolCore providers:
```php
App\\Providers\\SchoolCoreAttendanceServiceProvider::class,
App\\Domain\\IslamicSundaySchool\\Attendance\\Providers\\IslamicSundaySchoolAttendanceServiceProvider::class, // only for the Islamic Sunday School profile/app
```
Load the example routes from `routes/attendance_phase4.php` or merge them into your API route file. Keep legacy scanner endpoints as adapters only until compatibility tests pass. Controllers should delegate to `ScannerServiceContract`, not recreate attendance logic in private methods.
## Phase 5: Modular Student Lifecycle Extraction
Phase 5 adds a neutral `SchoolCore\Students` lifecycle module for race-safe identifiers, student creation/update/status transitions, guardians, households, enrollment, assignments, promotion, read models, audit logging, and Islamic Sunday School profile extension points.
Core student lifecycle stays reusable: no Qur'an, Arabic, Islamic studies, halaqa, masjid/community, or sensitive religious/community note fields belong in `SchoolCore\Students`. Those live in `App\Domain\IslamicSundaySchool\Students`. The point is not aesthetic purity; it is avoiding a global student model that secretly belongs to one domain and then acts innocent.
Register:
```php
App\Providers\SchoolCoreStudentServiceProvider::class,
App\Domain\IslamicSundaySchool\Students\Providers\IslamicSundaySchoolStudentServiceProvider::class,
```
Example routes: `routes/student_lifecycle_phase5.php`.
## Phase 6: Modular Communication Extraction
Phase 6 adds `SchoolCore\Communication` as the neutral communication engine. Bulk communication now uses a preview-first path: resolve recipients, dedupe, apply preferences, store a snapshot, confirm with a token, then send through channel contracts. This prevents accidental mass sends, duplicate deliveries, and cross-school recipient leakage.
Register:
```php
App\Providers\SchoolCoreCommunicationServiceProvider::class,
App\Domain\IslamicSundaySchool\Communication\Providers\IslamicSundaySchoolCommunicationServiceProvider::class,
```
Routes are in `routes/communication_phase6.php`. Include them only after authentication and `ResolveSchoolContext` are active.
Key rules: bulk sends require `preview_id` and `confirmation_token`; recipient queries live in resolvers, not controllers; email/SMS/WhatsApp/notification delivery lives behind channel contracts; Islamic Sunday School recipient behavior is extension-owned.
## Phase 7: Modular Reporting Extraction
Phase 7 adds `SchoolCore\Reporting` as the reporting boundary. Reports are read-only, require `SchoolContext`, and must consume read models instead of inventing finance, attendance, communication, or student lifecycle truth with ad hoc queries.
Added provider:
```php
App\Providers\SchoolCoreReportingServiceProvider::class,
App\Domain\IslamicSundaySchool\Reporting\Providers\IslamicSundaySchoolReportingServiceProvider::class,
```
Added route file:
```php
require base_path('routes/reporting_phase7.php');
```
The core reporting registry supports tagged report definitions using `schoolcore.reports`. Islamic Sunday School reports register through the extension provider and are only available when the context domain profile is `islamic_sunday_school`.
Phase 7 deliberately treats exports as controlled access, not a casual CSV vending machine. Export paths go through `ReportExportServiceContract`, audit hooks exist, snapshots are school-scoped, and architecture tests prevent `SchoolCore\Reporting` from importing extension classes.
## Phase 8: Controller and API Cleanup
Phase 8 adds the HTTP cleanup layer:
- v2 route file: `routes/api_phase8.php`
- standardized API response envelope: `App\Support\Api\ApiResponse`
- centralized exception rendering helper: `App\Support\Api\ApiExceptionRenderer`
- domain-profile middleware: `EnsureDomainProfile`
- deprecation header middleware: `DeprecationHeaders`
- route inventory generator: `php artisan api:route-inventory --markdown`
- controller classification map: `config/api_controller_classification.php`
- OpenAPI baseline: `docs/openapi.phase8.yaml`
- route inventory docs: `docs/api-route-inventory.md`
- v2 canonical controllers for finance, attendance, students, communication, reporting, and Islamic Sunday School extension routes
- architecture tests blocking raw DB/provider/controller business logic patterns
Phase 8 does not erase legacy routes. It creates canonical v2 delivery adapters and gives old routes a sane path to become adapters or deprecated endpoints. Because breaking clients for “architecture” is still breaking clients, just with nicer folder names.
## Phase 9: Boundary Enforcement and Modular Governance
Phase 9 adds enforceable modular governance on top of the Phase 1-8 scaffold.
Installed governance pieces:
- `app:architecture-scan` artisan command for forbidden imports, HTTP leakage, controller/provider calls, finance float usage, unsafe file routes, extension vocabulary, and critical modular rules.
- `app:dependency-map` command for dependency map artifacts.
- `app:route-inventory-check` command for route inventory governance.
- `app:docs-coverage` command for route/OpenAPI/deprecation coverage checks.
- `tests/Architecture/*BoundaryTest.php` suites for module, controller, service, finance, attendance, student, communication, reporting, Islamic Sunday School, route inventory, and deprecation rules.
- `.github/CODEOWNERS`, PR checklist, architecture workflow, and release gate workflow.
- `docs/architecture/*`, `docs/modules/*`, `docs/deprecations.md`, and `docs/release-checklist.md`.
Recommended rollout:
```bash
php artisan app:architecture-scan --fail-on=none
php artisan test tests/Architecture
php artisan api:route-inventory --markdown
php artisan app:route-inventory-check
php artisan app:dependency-map --markdown
php artisan app:docs-coverage
```
Start in warning mode for existing legacy code. Critical new violations should fail immediately. Letting legacy code break every build on day one is not governance; it is a morale experiment with predictable results.
## Phase 10: Migration, Release, and Production Readiness
Phase 10 adds production rollout governance for the modular platform:
- feature flag rollout map
- migration validation commands
- shadow comparison scaffolding
- release readiness gate
- rollback playbooks
- monitoring and alerting docs
- security/performance readiness checklists
- UAT scenarios
- support playbooks
- pilot and Islamic Sunday School production rollout plans
- legacy unsafe route audit
Useful commands:
```bash
php artisan app:release-feature-flags
php artisan app:release-migration-validate
php artisan app:release-shadow-compare
php artisan app:legacy-unsafe-route-audit
php artisan app:release-readiness-gate --warning-mode
```
Phase 10 is not a magic permission slip to ship everything. It is the checklist that stops the team from discovering rollback strategy during an incident, which is traditionally when everyone becomes philosophical and useless.