| name | acc-clean-arch-knowledge |
| description | Clean Architecture knowledge base. Provides patterns, antipatterns, and PHP-specific guidelines for Clean Architecture and Hexagonal Architecture audits. |
Clean Architecture Knowledge Base
Quick reference for Clean Architecture / Hexagonal Architecture patterns and PHP implementation guidelines.
Core Principles
The Dependency Rule
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ 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.
Hexagonal Architecture (Ports & Adapters)
โโโโโโโโโโโโโโโโโโโ
โ Primary โ
โ Adapters โ
โ (Controllers) โ
โโโโโโโโโโฌโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโบโ PORTS โโโโโโโโโโโโโ
โ โ (Interfaces) โ โ
โ โโโโโโโโโโฌโโโโโโโโโ โ
โ โ โ
โ โผ โ
โ โโโโโโโโโโโโโโโโโโโ โ
โ โ APPLICATION โ โ
โ โ (Use Cases) โ โ
โ โโโโโโโโโโฌโโโโโโโโโ โ
โ โ โ
โ โผ โ
โ โโโโโโโโโโโโโโโโโโโ โ
โ โ DOMAIN โ โ
โ โ (Entities) โ โ
โ โโโโโโโโโโโโโโโโโโโ โ
โ โ
โ โโโโโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโ Secondary โโโโโโโโโโโโโ
โ Adapters โ
โ (Repositories, โ
โ External APIs) โ
โโโโโโโโโโโโโโโโโโโ
Rule: Application core defines Ports (interfaces). Adapters implement them.
Quick Checklists
Domain Layer Checklist
Application Layer Checklist
Interface Adapters Checklist
Frameworks & Drivers Checklist
Common Violations Quick Reference
| 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 |
PHP 8.5 Clean Architecture Patterns
Port (Driven Port)
namespace Application\Order\Port;
interface PaymentGatewayInterface
{
public function charge(PaymentRequest $request): PaymentResponse;
public function refund(string $transactionId, Money $amount): RefundResponse;
}
Adapter (Driven Adapter)
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)
);
}
}
Use Case (Application Service)
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);
}
(->(), ->());
}
}
Controller (Driving Adapter)
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,
]);
}
}
References
For detailed information, load these reference files:
references/dependency-rule.md โ The Dependency Rule explained
references/layer-boundaries.md โ Layer responsibilities and boundaries
references/port-adapter-patterns.md โ Hexagonal Architecture patterns
references/antipatterns.md โ Common violations with detection patterns