Master backend architectures, REST API design, microservices, and server-side development across multiple languages. Use this as a central index to access specialized sub-skills.
日本語の概要は準備中です。原文の説明を表示しています。
Generates ADR Responder classes for PHP 8.5. Creates HTTP response builders with PSR-7/PSR-17 support. Includes unit tests.
インストールする前に、エージェントに与えられる指示の中身を確認できます。
Generate ADR-compliant Responder classes for HTTP response building.
<?php
declare(strict_types=1);
namespace Presentation\Api\{Context}\{Action};
use Application\{Context}\UseCase\{Action}\{Action}Result;
use Psr\Http\Message\ResponseFactoryInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\StreamFactoryInterface;
final readonly class {Action}Responder
{
public function __construct(
private ResponseFactoryInterface $responseFactory,
private StreamFactoryInterface $streamFactory,
) {
}
public function respond({Action}Result $result): ResponseInterface
{
if ($result->isFailure()) {
return $this->handleFailure($result);
}
return $this->success($result);
}
private function success({Action}Result $result): ResponseInterface
{
{successResponse}
}
private function handleFailure({Action}Result $result): ResponseInterface
{
return match ($result->failureReason()) {
{errorMapping}
default => $this->badRequest($result->errorMessage()),
};
}
private function json(array $data, int $status = 200): ResponseInterface
{
$body = $this->streamFactory->createStream(
json_encode($data, JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE)
);
return $this->responseFactory->createResponse($status)
->withHeader('Content-Type', 'application/json; charset=utf-8')
->withBody($body);
}
{helperMethods}
}
<?php
declare(strict_types=1);
namespace Tests\Unit\Presentation\Api\{Context}\{Action};
use Application\{Context}\UseCase\{Action}\{Action}Result;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Group;
use PHPUnit\Framework\TestCase;
use Presentation\Api\{Context}\{Action}\{Action}Responder;
use Psr\Http\Message\ResponseFactoryInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\StreamFactoryInterface;
use Psr\Http\Message\StreamInterface;
#[Group('unit')]
#[CoversClass({Action}Responder::class)]
final class {Action}ResponderTest extends TestCase
{
private ResponseFactoryInterface $responseFactory;
private StreamFactoryInterface $streamFactory;
private {Action}Responder $responder;
protected function setUp(): void
{
$this->responseFactory = $this->createMock(ResponseFactoryInterface::class);
$this->streamFactory = $this->createMock(StreamFactoryInterface::class);
$this->responder = new {Action}Responder(
$this->responseFactory,
$this->streamFactory,
);
$this->setupMocks();
}
public function testSuccessReturns{ExpectedStatus}(): void
{
$result = {Action}Result::success({successData});
$response = $this->responder->respond($result);
self::assertSame({expectedStatusCode}, $response->getStatusCode());
}
{failureTests}
private function setupMocks(): void
{
$stream = $this->createMock(StreamInterface::class);
$this->streamFactory->method('createStream')->willReturn($stream);
$response = $this->createMock(ResponseInterface::class);
$response->method('withHeader')->willReturnSelf();
$response->method('withBody')->willReturnSelf();
$response->method('getStatusCode')->willReturnCallback(
fn () => $this->responseFactory->lastStatus ?? 200
);
$this->responseFactory->method('createResponse')->willReturnCallback(
function (int $status) use ($response) {
$this->responseFactory->lastStatus = $status;
$mock = clone $response;
$mock->method('getStatusCode')->willReturn($status);
return $mock;
}
);
}
}
<?php
declare(strict_types=1);
namespace Presentation\Api\User\Create;
use Application\User\UseCase\CreateUser\CreateUserResult;
use Psr\Http\Message\ResponseFactoryInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\StreamFactoryInterface;
final readonly class CreateUserResponder
{
public function __construct(
private ResponseFactoryInterface $responseFactory,
private StreamFactoryInterface $streamFactory,
) {
}
public function respond(CreateUserResult $result): ResponseInterface
{
if ($result->isFailure()) {
return match ($result->failureReason()) {
'email_exists' => $this->conflict('User with this email already exists'),
'invalid_email' => $this->badRequest('Invalid email format'),
default => $this->badRequest($result->errorMessage()),
};
}
return $this->created([
'id' => $result->userId(),
'email' => $result->email(),
]);
}
private function created(array $data): ResponseInterface
{
return $this->json($data, 201);
}
private function conflict(string $message): ResponseInterface
{
return $this->json(['error' => $message], 409);
}
private function badRequest(string $message): ResponseInterface
{
return $this->json(['error' => $message], 400);
}
private function json(array $data, int $status): ResponseInterface
{
$body = $this->streamFactory->createStream(
json_encode($data, JSON_THROW_ON_ERROR)
);
return $this->responseFactory->createResponse($status)
->withHeader('Content-Type', 'application/json')
->withBody($body);
}
}
<?php
declare(strict_types=1);
namespace Presentation\Api\User\GetById;
use Application\User\UseCase\GetUserById\GetUserByIdResult;
use Psr\Http\Message\ResponseFactoryInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\StreamFactoryInterface;
final readonly class GetUserByIdResponder
{
public function __construct(
private ResponseFactoryInterface $responseFactory,
private StreamFactoryInterface $streamFactory,
) {
}
public function respond(GetUserByIdResult $result): ResponseInterface
{
if ($result->isNotFound()) {
return $this->notFound('User not found');
}
$user = $result->user();
return $this->json([
'id' => $user->id()->toString(),
'email' => $user->email()->value(),
'name' => $user->name(),
'created_at' => $user->createdAt()->format('c'),
]);
}
private function notFound(string $message): ResponseInterface
{
return $this->json(['error' => $message], 404);
}
private function json(array $data, int $status = 200): ResponseInterface
{
$body = $this->streamFactory->createStream(
json_encode($data, JSON_THROW_ON_ERROR)
);
return $this->responseFactory->createResponse($status)
->withHeader('Content-Type', 'application/json')
->withBody($body);
}
}
<?php
declare(strict_types=1);
namespace Presentation\Api\User\ListAll;
use Application\User\UseCase\ListUsers\ListUsersResult;
use Psr\Http\Message\ResponseFactoryInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\StreamFactoryInterface;
final readonly class ListUsersResponder
{
public function __construct(
private ResponseFactoryInterface $responseFactory,
private StreamFactoryInterface $streamFactory,
) {
}
public function respond(ListUsersResult $result): ResponseInterface
{
$users = array_map(
fn ($user) => [
'id' => $user->id()->toString(),
'email' => $user->email()->value(),
'name' => $user->name(),
],
$result->users()
);
return $this->json([
'data' => $users,
'meta' => [
'total' => $result->total(),
'page' => $result->page(),
'per_page' => $result->perPage(),
'total_pages' => $result->totalPages(),
],
]);
}
private function json(array $data, int $status = 200): ResponseInterface
{
$body = $this->streamFactory->createStream(
json_encode($data, JSON_THROW_ON_ERROR)
);
return $this->responseFactory->createResponse($status)
->withHeader('Content-Type', 'application/json')
->withBody($body);
}
}
<?php
declare(strict_types=1);
namespace Presentation\Api\User\Delete;
use Application\User\UseCase\DeleteUser\DeleteUserResult;
use Psr\Http\Message\ResponseFactoryInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\StreamFactoryInterface;
final readonly class DeleteUserResponder
{
public function __construct(
private ResponseFactoryInterface $responseFactory,
private StreamFactoryInterface $streamFactory,
) {
}
public function respond(DeleteUserResult $result): ResponseInterface
{
if ($result->isNotFound()) {
return $this->notFound('User not found');
}
if ($result->isFailure()) {
return $this->badRequest($result->errorMessage());
}
return $this->noContent();
}
private function noContent(): ResponseInterface
{
return $this->responseFactory->createResponse(204);
}
private function notFound(string $message): ResponseInterface
{
return $this->json(['error' => $message], 404);
}
private function badRequest(string $message): ResponseInterface
{
return $this->json(['error' => $message], 400);
}
private function json(array $data, int $status): ResponseInterface
{
$body = $this->streamFactory->createStream(
json_encode($data, JSON_THROW_ON_ERROR)
);
return $this->responseFactory->createResponse($status)
->withHeader('Content-Type', 'application/json')
->withBody($body);
}
}
| Domain Condition | HTTP Status | Method |
|---|---|---|
| Success (create) | 201 | created() |
| Success (read) | 200 | json() |
| Success (update) | 200 | json() |
| Success (delete) | 204 | noContent() |
| Not found | 404 | notFound() |
| Already exists | 409 | conflict() |
| Validation error | 422 | unprocessableEntity() |
| Invalid input | 400 | badRequest() |
| Unauthorized | 401 | unauthorized() |
| Forbidden | 403 | forbidden() |
| Component | Path |
|---|---|
| Responder | src/Presentation/Api/{Context}/{Action}/{Action}Responder.php |
| Interface | src/Presentation/Shared/Responder/ResponderInterface.php |
| Abstract | src/Presentation/Shared/Responder/AbstractJsonResponder.php |
| Test | tests/Unit/Presentation/Api/{Context}/{Action}/{Action}ResponderTest.php |
When asked to create a Responder:
| HTTP Method | Responder Name | Success Status |
|---|---|---|
| GET (single) | Get{Resource}ByIdResponder | 200 |
| GET (list) | List{Resource}sResponder | 200 |
| POST | Create{Resource}Responder | 201 |
| PUT | Update{Resource}Responder | 200 |
| PATCH | Patch{Resource}Responder | 200 |
| DELETE | Delete{Resource}Responder | 204 |
For detailed patterns and examples:
references/templates.md — Additional Responder templatesreferences/examples.md — Real-world Responder examplesまだレビューはありません。使ってみた感想をお寄せください。
概要と使いどころ
Master backend architectures, REST API design, microservices, and server-side development across multiple languages. Use this as a central index to access specialized sub-skills.
日本語の概要は準備中です。原文の説明を表示しています。
在每次回复中使用固定前缀并列出本次实际参考的指令/文件,便于校验是否遵循上下文与规则。
日本語の概要は準備中です。原文の説明を表示しています。
Automate 21risk tasks via Rube MCP (Composio). Always search tools first for current schemas.
日本語の概要は準備中です。原文の説明を表示しています。
Automate 2chat tasks via Rube MCP (Composio). Always search tools first for current schemas.
日本語の概要は準備中です。原文の説明を表示しています。
Arquitecto de Soluciones Principal y Consultor Tecnológico de Andru.ia. Diagnostica y traza la hoja de ruta óptima para proyectos de IA en español.
日本語の概要は準備中です。原文の説明を表示しています。
[00] META. Orchestrate skills into dynamic chains for complex tasks. Analyzes the task, discovers available skills, builds an optimal chain, explains why each skill is needed, and executes step-by-step with user confirmation. Use for any complex task requiring multiple thinking/research/analysis steps. Triggers on "plan this", "how to approach", "what's the strategy", "build a plan", or any multi-step problem requiring skill orchestration.
日本語の概要は準備中です。原文の説明を表示しています。