oryx / adr
Action Domain Responder (ADR) library for PHP, compatible with oryx/mvc and PWA middleware
Requires
- php: ^8.1
- laminas/laminas-diactoros: ^2.0|^3.0
- oryx/mvc: *
- psr/http-message: ^1.0|^2.0
- psr/http-server-middleware: ^1.0
Requires (Dev)
- phpunit/phpunit: ^9.0|^10.0
- squizlabs/php_codesniffer: ^3.0
This package is auto-updated.
Last update: 2026-03-28 19:37:53 UTC
README
Action Domain Responder (ADR) library for PHP, compatible with oryx/mvc and PWA middleware.
Overview
This library implements the Action Domain Responder (ADR) pattern, a refinement of the MVC pattern that separates concerns into:
- Action: Receives input and delegates to Domain
- Domain: Contains business logic
- Responder: Prepares and returns HTTP responses
The library includes:
- ADR pattern interfaces and abstract base classes
- ManifestJsonMiddleware for PWA compatibility
- PSR-15 compliant middleware integration
- PSR-4 autoloading
- JSON:API and Problem Details (RFC 7807) responders
Installation
composer require oryx/adr
Requirements
- PHP 8.1+
- PSR interfaces (http-message, http-server-middleware)
- Laminas Diactoros (for JSON responses)
- oryx/mvc (framework integration)
Usage
Basic ADR Implementation
use Oryx\Adr\Action\ActionInterface; use Oryx\Adr\Domain\DomainInterface; use Oryx\Adr\Responder\ResponderFactory; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\ResponseInterface as PsrResponseInterface; use Laminas\Diactoros\ServerRequest; use Laminas\Diactoros\Response; // Define your domain class UserDomain implements DomainInterface { public function getUserProfile(int $userId): array { // Business logic here return [ 'id' => $userId, 'name' => 'John Doe', 'email' => 'john@example.com' ]; } } // Define your responder class JsonApiResponder implements ResponderInterface { private $data; public function __construct(array $data) { $this->data = $data; } public function respond(): ResponseInterface { // Simplified JSON:API responder for example return new \Laminas\Diactoros\JsonResponse($this->data); } } // Define your action class GetUserProfileAction implements ActionInterface { private UserDomain $domain; private ResponderFactory $responderFactory; public function __construct(UserDomain $domain, ResponderFactory $responderFactory) { $this->domain = $domain; $this->responderFactory = $responderFactory; } public function execute(DomainInterface $domain): string { // Extract user ID from request (in real implementation) $userId = 123; // Delegate to domain $userData = $domain->getUserProfile($userId); // Return responder class name return JsonApiResponder::class; } public function __invoke(ServerRequestInterface $request, PsrResponseInterface $response, callable $next = null): PsrResponseInterface { // Execute the action to get the responder class name $responderClass = $this->execute($this->domain); // Create the responder instance using the factory $responderInstance = $this->responderFactory->create($responderClass); // Generate and return the response return $responderInstance->respond(); } } // Usage in oryx/mvc $app = new \Oryx\Mvc\Application(); $container = $app->getContainer(); // Register your domain and responder factory with the container // Example: // $container->set(UserDomain::class, new UserDomain()); // $container->set(\Oryx\Adr\Responder\ResponderFactory::class, // new \Oryx\Adr\Responder\DefaultResponderFactory($container)); // Then create your action and process the request.
Using Built-in Responders
use Oryx\Adr\Responder\JsonApiResponder; use Oryx\Adr\Responder\ProblemDetailsResponder; // JSON:API responder for successful responses $jsonApiResponder = new JsonApiResponder([ 'data' => [ 'type' => 'users', 'id' => '1', 'attributes' => [ 'name' => 'John Doe', 'email' => 'john@example.com' ] ] ]); // Problem Details responder for error responses $problemDetailsResponder = new ProblemDetailsResponder( 'https://example.com/probs/out-of-credit', 'You do not have enough credit.', 403, 'Your account has only 5 USD left.', 'https://example.com/account/12345/msgs/abc' );
Using with oryx/mvc Middleware
The ManifestJsonMiddleware is PSR-15 compliant and can be used directly with oryx/mvc:
use Oryx\Adr\Middleware\ManifestJsonMiddleware; use Oryx\Mvc\Application; // Create application $app = new Application(); // Add PWA manifest middleware $app->addMiddleware(new ManifestJsonMiddleware()); // Add other middleware and routes... $app->run();
ManifestJsonMiddleware Features
The ManifestJsonMiddleware automatically handles requests for:
/manifest.json/manifest.webmanifest
It returns a standard PWA manifest with:
- App name and short name
- Description
- Start URL
- Display mode
- Theme and background colors
- Icon array for various sizes
The middleware delegates all other requests to the next middleware in the queue.
PWA Compatibility
This library provides PWA compatibility through the ManifestJsonMiddleware which serves the web app manifest required for:
- Installable PWAs
- Offline capabilities (when combined with service workers)
- Mobile app-like experience
For full PWA functionality, combine this middleware with:
- Service worker middleware (for offline caching)
- Secure HTTPS connection
- Proper icon assets
Testing
Run the test suite:
vendor/bin/phpunit
License
This library is licensed under the BSD 3-Clause License - see the LICENSE file for details.
Contributing
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -am 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Create a new Pull Request