137 lines
6.8 KiB
Markdown
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.
|