Framework/NOTES.md

137 lines
6.8 KiB
Markdown

# Phred Framework: Project Notes & Brainstorming
This document serves as a mind-map and repository for ideas, architectural decisions, and future considerations for the Phred Framework. It is a living document that captures the "why" behind the "what" defined in the specifications and milestones.
## Core Vision & Philosophy
- **Batteries-Included but Swappable**: Provide sensible defaults (REST, JSON:API, Eyrie, Pairity) while ensuring every core component can be swapped via Service Providers.
- **Standards First**: Strict adherence to PSRs (PSR-7, PSR-11, PSR-14, PSR-15, PSR-18).
- **Modular by Nature**: Django-style "Apps" (Modules) where features are encapsulated.
- **Developer Happiness (DX)**: Zero-config where possible, robust scaffolding, and clear documentation.
## Architectural Brainstorming
### 1. The HTTP Pipeline (PSR-15)
- Use `Relay` for the middleware stack.
- **Middleware Layers**:
- Global: CORS, Security Headers, Error Handling (Problem Details).
- Route-specific: Auth (JWT), Validation, Rate Limiting.
- Negotiation: `ContentNegotiationMiddleware` to handle REST vs JSON:API vs HTML.
### 2. Pluggability & Service Providers
- **Contracts Package**: Define all core interfaces in `Phred\Support\Contracts`.
- **Driver Selection**: Use `.env` keys like `ORM_DRIVER`, `TEMPLATE_DRIVER`, `CACHE_DRIVER`.
- **Lifecycle**: Providers should have `register()` (binding to container) and `boot()` (executing logic like route registration).
### 3. MVC & View Layer
- **Invokable Controllers**: "One action, one class" to prevent controller bloat.
- **View Objects**: A dedicated layer for data transformation/preparation before the template. This keeps controllers focused on flow and templates focused on markup.
- **Response Factories**: Abstract the creation of REST vs JSON:API responses.
### 4. Modular Architecture (Django-style)
- All user code lives in `modules/`.
- **Contract-First Persistence**: Modules should define pure domain models (POPOs) and repository interfaces. The framework uses a "Bridge" architecture to map these to a specific ORM.
- **Scaffolding**: CLI should be able to generate an entire module structure in one command.
### 5. Persistence Bridge Strategy (ORM-Agnostic)
To achieve true decoupling, Phred adopts a "Persistence Bridge" pattern, mirroring the TaskerBridges architecture. This removes the need for driver-specific directories (like `Persistence/Pairity/`) within modules and moves implementation details to the framework's infrastructure layer.
#### Implementation Concept
1. **Flattened Directory Structure**:
- `modules/<Module>/Models/`: Contains the data objects/entities (POPOs).
- `modules/<Module>/Repositories/`: Contains the repository interfaces.
2. **The Bridge as a Translator**:
- The framework boots an active **ORM Bridge** (e.g., `PairityBridge`, `EloquentBridge`) based on `.env`.
- The Bridge is responsible for taking a module's model and explaining it to its respective persistence engine using PHP 8 Attributes (metadata).
3. **Automated Discovery & Wiring**:
- Similar to Tasker's command discovery, Phred scans the `Models/` directory to register entities.
- Phred auto-binds repository implementations to their corresponding interfaces in the PSR-11 container.
4. **Environment-Driven**: Connection details and driver selection are handled exclusively via environment variables, keeping module code pure.
5. **Migration & Schema Management**:
- **Auto-Discovery**: Bridges scan `Models/` to detect schema changes.
- **Versioned Snapshots**: The framework can cache a "last-known" state of the models to generate diff-based migrations.
- **Tooling**: CLI commands can generate migration files by comparing current POPO attributes against the database or a cached snapshot.
#### Example Domain Model (Attribute-Driven)
```php
namespace App\Modules\User\Models;
use Phred\Persistence\Attributes\Entity;
use Phred\Persistence\Attributes\Column;
#[Entity(table: 'users')]
class User
{
#[Column(primary: true, autoIncrement: true)]
public int $id;
#[Column(unique: true)]
public string $email;
#[Column(nullable: true)]
public ?string $name;
#[Column(name: 'created_at')]
public \DateTimeImmutable $createdAt;
}
```
#### Benefits
- **Zero-Config Persistence**: Developers only write the Model and the Interface; Phred handles the connection and injection.
- **Multi-ORM Support**: The same model can work across different ORMs by switching the Bridge.
- **Simplified DX**: No deeply nested folders for every module; logic is localized and clean.
### 6. Command Discovery Strategy (Tasker Integration)
To provide a "zero-configuration" experience for developers, the framework should implement an automated directory-based command loader that bridges the application's `src/Commands` directory with Tasker.
#### Proposed Implementation Flow
1. **Directory Scan**: Use `RecursiveDirectoryIterator` to find all PHP files in `src/Commands`.
2. **Class Resolution**: Map file paths to fully qualified class names following PSR-4 conventions.
3. **Container-First Loading**:
- Check if the class is already registered in the PSR-11 container.
- If found, retrieve the instance from the container (ensuring dependency injection).
- If not found, Tasker's `Runner::register($className)` will instantiate it directly.
4. **Registration**: Pass the resolved class name or instance to `$taskerRunner->register()`.
5. **Migration & Schema Management**:
- **Auto-Discovery**: Bridges scan `Models/` to detect schema changes.
- **Versioned Snapshots**: The framework can cache a "last-known" state of the models to generate diff-based migrations.
- **Tooling**: CLI commands can generate migration files by comparing current POPO attributes against the database or a cached snapshot.
#### Benefits
- **Zero Config**: Developers just drop a class into the directory.
- **DI Support**: Automatically respects container-managed services.
- **Type Safety**: Tasker validates that the class implements `CommandInterface` or uses the `HasAttributes` trait.
#### Example Discovery Logic
```php
$commandPath = $appRoot . '/src/Commands';
$namespace = 'App\Commands\';
$files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($commandPath));
foreach ($files as $file) {
if ($file->getExtension() !== 'php') {
continue;
}
$className = $namespace . str_replace(
['/', '.php'],
['\', ''],
substr($file->getPathname(), strlen($commandPath) + 1)
);
$taskerRunner->register($className);
}
```
### 7. Security & Auth
- JWT by default for APIs.
- CSRF protection for traditional web routes.
- **Feature Flags**: Native integration with `FlagPole`.
### 8. Documentation & Discovery
- Documentation site built *with* Phred.
- **Dynamic Help**: A CLI command that opens the online docs for a specific framework command.
- **OpenAPI**: Auto-generation from annotations.