Help us improve
Share bugs, ideas, or general feedback.
From acc
Provides Clean Architecture and Hexagonal Architecture patterns, antipatterns, and PHP-specific guidelines for code audits.
npx claudepluginhub dykyi-roman/awesome-claude-code --plugin accHow this skill is triggered — by the user, by Claude, or both
Slash command
/acc:clean-arch-knowledgeThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Quick reference for Clean Architecture / Hexagonal Architecture patterns and PHP implementation guidelines.
Provides patterns, antipatterns, and PHP-specific guidelines for auditing Hexagonal Architecture (Ports & Adapters) implementations.
Implements Clean Architecture layers, SOLID principles, dependency injection, DDD, hexagonal architecture, and code quality patterns. Use for new service design or refactoring legacy code.
Guides applying Clean Architecture, Hexagonal Architecture, and Domain-Driven Design to structure systems with isolated business logic, layer boundaries, and dependency rules.
Share bugs, ideas, or general feedback.
Quick reference for Clean Architecture / Hexagonal Architecture patterns and PHP implementation guidelines.
┌────────────────────────────────────────────────────────────────┐
│ FRAMEWORKS & DRIVERS │
│ (Web, UI, DB, External Services, Devices) │
├────────────────────────────────────────────────────────────────┤
│ INTERFACE ADAPTERS │
│ (Controllers, Gateways, Presenters, Repositories) │
├────────────────────────────────────────────────────────────────┤
│ APPLICATION BUSINESS RULES │
│ (Use Cases, Application Services) │
├────────────────────────────────────────────────────────────────┤
│ ENTERPRISE BUSINESS RULES │
│ (Entities, Value Objects, Domain Services) │
└────────────────────────────────────────────────────────────────┘
▲
│
Dependencies point INWARD only
Rule: Source code dependencies must point INWARD. Inner layers know nothing about outer layers.
┌─────────────────┐
│ Primary │
│ Adapters │
│ (Controllers) │
└────────┬────────┘
│
▼
┌─────────────────┐
┌──────────►│ PORTS │◄──────────┐
│ │ (Interfaces) │ │
│ └────────┬────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ APPLICATION │ │
│ │ (Use Cases) │ │
│ └────────┬────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ DOMAIN │ │
│ │ (Entities) │ │
│ └─────────────────┘ │
│ │
│ ┌─────────────────┐ │
└───────────│ Secondary │───────────┘
│ Adapters │
│ (Repositories, │
│ External APIs) │
└─────────────────┘
Rule: Application core defines Ports (interfaces). Adapters implement them.
| Violation | Where to Look | Severity |
|---|---|---|
| Inner layer imports outer | Domain/Application importing Infrastructure | Critical |
| Framework in core | Doctrine/Symfony in Domain | Critical |
| Use Case with HTTP details | Request/Response in Application | Critical |
| Business logic in Controller | if/switch on domain state | Warning |
| Missing Port | Direct external service call | Warning |
| Adapter with logic | Repository doing validation | Warning |
// Application layer - defines the contract
namespace Application\Order\Port;
interface PaymentGatewayInterface
{
public function charge(PaymentRequest $request): PaymentResponse;
public function refund(string $transactionId, Money $amount): RefundResponse;
}
// Infrastructure layer - implements the contract
namespace Infrastructure\Payment;
final readonly class StripePaymentGateway implements PaymentGatewayInterface
{
public function __construct(
private StripeClient $stripe
) {}
public function charge(PaymentRequest $request): PaymentResponse
{
$charge = $this->stripe->charges->create([
'amount' => $request->amount->cents(),
'currency' => $request->currency->value,
'source' => $request->token,
]);
return new PaymentResponse(
transactionId: $charge->id,
status: PaymentStatus::from($charge->status)
);
}
}
namespace Application\Order\UseCase;
final readonly class ProcessPaymentUseCase
{
public function __construct(
private OrderRepositoryInterface $orders,
private PaymentGatewayInterface $paymentGateway, // Port
private EventDispatcherInterface $events
) {}
public function execute(ProcessPaymentCommand $command): PaymentResult
{
$order = $this->orders->findById($command->orderId);
$payment = $this->paymentGateway->charge(
new PaymentRequest($order->total(), $command->paymentToken)
);
if ($payment->isSuccessful()) {
$order->markAsPaid($payment->transactionId());
$this->orders->save($order);
}
return new PaymentResult($payment->transactionId(), $payment->status());
}
}
namespace Presentation\Api\Order;
final readonly class PaymentController
{
public function __construct(
private ProcessPaymentUseCase $processPayment
) {}
public function process(Request $request): JsonResponse
{
$command = new ProcessPaymentCommand(
orderId: new OrderId($request->get('order_id')),
paymentToken: $request->get('payment_token')
);
$result = $this->processPayment->execute($command);
return new JsonResponse([
'transaction_id' => $result->transactionId,
'status' => $result->status->value,
]);
}
}
For detailed information, load these reference files:
references/dependency-rule.md — The Dependency Rule explainedreferences/layer-boundaries.md — Layer responsibilities and boundariesreferences/port-adapter-patterns.md — Hexagonal Architecture patternsreferences/antipatterns.md — Common violations with detection patterns