acc-adr-knowledge
1
总安装量
1
周安装量
#54981
全站排名
安装命令
npx skills add https://github.com/dykyi-roman/awesome-claude-code --skill acc-adr-knowledge
Agent 安装分布
opencode
1
claude-code
1
Skill 文档
ADR Knowledge Base
Quick reference for Action-Domain-Responder pattern and PHP implementation guidelines.
Core Principles
ADR Components
HTTP Request â Action (collects input)
â
Domain (executes business logic)
â
Responder (builds HTTP Response)
â
HTTP Response
Rule: One action = one HTTP endpoint. Responder builds the COMPLETE response.
Component Responsibilities
| Component | Responsibility | Contains |
|---|---|---|
| Action | Collects input, invokes Domain, passes to Responder | Request parsing, DTO creation, UseCase invocation |
| Domain | Business logic (same as DDD Domain/Application) | Entities, Value Objects, UseCases, Services |
| Responder | Builds HTTP Response (status, headers, body) | Response building, template rendering, content negotiation |
ADR vs MVC Comparison
| Aspect | MVC Controller | ADR Action |
|---|---|---|
| Granularity | Multiple actions | Single action per class |
| Response building | Mixed in controller | Separate Responder class |
| HTTP concerns | Scattered | Isolated in Responder |
| Testability | Lower (many concerns) | Higher (single responsibility) |
| File structure | Few large files | Many focused files |
Quick Checklists
Action Checklist
- Single
__invoke()method - No
new Response()or response building - No business logic (if/switch on domain state)
- Only input parsing and domain invocation
- Returns Responder result
Responder Checklist
- Receives domain result only
- Builds complete HTTP Response
- Handles content negotiation
- Sets status codes based on result
- No domain/business logic
- No database/repository access
Domain Checklist
- Same as DDD Domain/Application layers
- No HTTP/Response concerns
- Returns domain objects (not HTTP responses)
- Pure business logic
Common Violations Quick Reference
| Violation | Where to Look | Severity |
|---|---|---|
new Response() in Action |
*Action.php | Critical |
->withStatus() in Action |
*Action.php | Critical |
if ($result->isError()) in Action |
*Action.php | Warning |
$repository-> in Responder |
*Responder.php | Critical |
$service-> in Responder |
*Responder.php | Critical |
| Multiple public methods in Action | *Action.php | Warning |
| Template logic in Action | *Action.php | Warning |
PHP 8.5 ADR Patterns
Action Pattern
<?php
declare(strict_types=1);
namespace Presentation\Api\User\Create;
use Application\User\UseCase\CreateUser\CreateUserUseCase;
use Application\User\UseCase\CreateUser\CreateUserInput;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
final readonly class CreateUserAction
{
public function __construct(
private CreateUserUseCase $useCase,
private CreateUserResponder $responder,
) {
}
public function __invoke(ServerRequestInterface $request): ResponseInterface
{
$input = $this->parseInput($request);
$result = $this->useCase->execute($input);
return $this->responder->respond($result);
}
private function parseInput(ServerRequestInterface $request): CreateUserInput
{
$body = (array) $request->getParsedBody();
return new CreateUserInput(
email: $body['email'] ?? '',
name: $body['name'] ?? '',
);
}
}
Responder Pattern
<?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 $this->error($result->error());
}
return $this->success($result->userId());
}
private function success(string $userId): ResponseInterface
{
return $this->json(['id' => $userId], 201);
}
private function error(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);
}
}
Detection Patterns
Action Detection
# Find Action classes
Glob: **/*Action.php
Glob: **/Action/**/*.php
Grep: "implements.*ActionInterface|extends.*Action" --glob "**/*.php"
# Detect Action pattern usage
Grep: "public function __invoke.*Request" --glob "**/*Action.php"
Responder Detection
# Find Responder classes
Glob: **/*Responder.php
Glob: **/Responder/**/*.php
Grep: "implements.*ResponderInterface" --glob "**/*.php"
# Detect Responder pattern usage
Grep: "public function respond" --glob "**/*Responder.php"
Violation Detection
# Response building in Action (Critical)
Grep: "new Response|->withStatus|->withHeader|->withBody" --glob "**/*Action.php"
# Business logic in Action (Warning)
Grep: "if \(.*->status|switch \(|->isValid\(\)" --glob "**/*Action.php"
# Domain calls in Responder (Critical)
Grep: "Repository|Service|UseCase" --glob "**/*Responder.php"
# Multiple public methods in Action (Warning)
Grep: "public function [^_]" --glob "**/*Action.php" | wc -l
File Structure
Recommended Structure
src/
âââ Presentation/
â âââ Api/
â â âââ {Context}/
â â âââ {Action}/
â â âââ {Action}Action.php
â â âââ {Action}Responder.php
â â âââ {Action}Request.php (optional DTO)
â âââ Web/
â â âââ {Context}/
â â âââ {Action}/
â â âââ {Action}Action.php
â â âââ {Action}Responder.php
â â âââ templates/ (for HTML)
â âââ Shared/
â âââ Action/
â â âââ ActionInterface.php
â âââ Responder/
â âââ ResponderInterface.php
âââ Application/
â âââ {Context}/
â âââ UseCase/
â âââ {Action}/
â âââ {Action}UseCase.php
â âââ {Action}Input.php
â âââ {Action}Result.php
âââ Domain/
âââ ...
Alternative Structure (Feature-Based)
src/
âââ User/
â âââ Presentation/
â â âââ CreateUser/
â â â âââ CreateUserAction.php
â â â âââ CreateUserResponder.php
â â âââ GetUser/
â â âââ GetUserAction.php
â â âââ GetUserResponder.php
â âââ Application/
â â âââ ...
â âââ Domain/
â âââ ...
Integration with DDD
ADR fits naturally with DDD layering:
| ADR | DDD Layer | Notes |
|---|---|---|
| Action | Presentation | Entry point for HTTP |
| Responder | Presentation | Exit point for HTTP |
| Domain | Domain + Application | Business logic via UseCases |
PSR Integration
ADR works with PSR-7/PSR-15:
| PSR | Usage |
|---|---|
| PSR-7 | Request/Response interfaces |
| PSR-15 | Middleware for cross-cutting concerns |
| PSR-17 | Response/Stream factories in Responder |
Antipatterns
1. Fat Action (Critical)
// BAD: Action doing too much
class CreateUserAction
{
public function __invoke(Request $request): Response
{
$data = $request->getParsedBody();
// Validation in Action
if (empty($data['email'])) {
return new Response(400, [], 'Email required');
}
// Business logic in Action
$user = new User($data['email']);
$this->repository->save($user);
// Response building in Action
return new Response(201, [], json_encode(['id' => $user->id()]));
}
}
2. Anemic Responder (Warning)
// BAD: Responder not doing its job
class CreateUserResponder
{
public function respond($data): Response
{
return new Response(200, [], json_encode($data));
}
}
3. Smart Responder (Critical)
// BAD: Responder with business logic
class CreateUserResponder
{
public function respond(User $user): Response
{
// Domain logic in Responder!
if ($user->isAdmin()) {
$this->notificationService->notifyAdmins();
}
return $this->json(['id' => $user->id()], 201);
}
}
References
For detailed information, load these reference files:
references/action-patterns.mdâ Action class patterns and best practicesreferences/responder-patterns.mdâ Responder class patternsreferences/domain-integration.mdâ Integration with DDD Domain layerreferences/mvc-comparison.mdâ Detailed MVC vs ADR comparisonreferences/antipatterns.mdâ Common ADR violations with examples
Assets
assets/report-template.mdâ ADR audit report template