Token导航 LogoToken导航TokenDH.com
研究检索external-servicegithub未标认证来源可访问许可证需确认审计通过

clean-architecture-phpclean 架构 PHP

Agent Skill

clean-architecture-php 用于查找、检索和筛选相关信息,适合在 Codex、Claude、Cursor、Gemini CLI 中需要根据关键词、任务场景或来源线索快速定位候选结果时使用。可结合来源仓库、安装命令和原始 README 继续核验具体用法。安装前建议确认权限范围、维护状态,以及是否会触发联网、命令执行或文件读写。

总安装

2,164

周安装

92

GitHub Stars

229

下载量

758
CodexClaudeCursorGemini CLI

安装说明

本站只整理中文说明和来源信息,不托管安装包,也不代用户安装。

GitHub

来源数

2

许可证

unknown

最后核验

2026-05-01

来源状态

来源可访问

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:clean-architecture-php(clean 架构 PHP)
来源仓库:https://github.com/giuseppe-trisciuoglio/developer-kit
仓库路径:skills/clean-architecture-php
安装命令:
npx skills add https://github.com/giuseppe-trisciuoglio/developer-kit --skill clean-architecture-php
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。该命令会通过 npx skills 从第三方来源获取 Skill;本站只展示命令,不托管安装包,也不自动执行。

skills.shnpx skills
npx skills add https://github.com/giuseppe-trisciuoglio/developer-kit --skill clean-architecture-php

简介

clean-architecture-php 提供 PHP 8.3+ 和 Symfony 7.x 的清洁架构、六边形架构及 DDD 实现指南。

  • 适合企业级 PHP 开发者、架构师和需要框架无关业务逻辑的团队。
  • 通过端口适配器模式和分层依赖规则确保高度可测试性和框架独立性。
  • 需熟悉 Symfony 组件、PHP 8.3 特性及领域驱动设计概念,建议从简单用例开始实践。
  • 适用宿主包括 Codex、Claude、Cursor、Gemini CLI,接入前应确认版本、权限和运行环境要求。

SKILL.md

Clean Architecture, Hexagonal Architecture & DDD for PHP/Symfony

Overview

This skill provides guidance for implementing Clean Architecture, Hexagonal Architecture (Ports & Adapters), and Domain-Driven Design patterns in PHP 8.3+ applications using Symfony 7.x. It ensures clear separation of concerns, framework-independent business logic, and highly testable code through layered architecture with inward-only dependencies.

When to Use

  • Architecting new enterprise PHP applications with Symfony 7.x
  • Refactoring legacy PHP code to modern, testable patterns
  • Implementing Domain-Driven Design in PHP projects
  • Creating maintainable applications with clear separation of concerns
  • Building testable business logic independent of frameworks
  • Designing modular PHP systems with swappable infrastructure

Instructions

1. Understand the Architecture Layers

Clean Architecture follows the dependency rule: dependencies only point inward.

+-------------------------------------+
|  Infrastructure (Frameworks)        |  Symfony, Doctrine, External APIs
+-------------------------------------+
|  Adapter (Interface Adapters)       |  Controllers, Repositories, Presenters
+-------------------------------------+
|  Application (Use Cases)            |  Commands, Handlers, DTOs
+-------------------------------------+
|  Domain (Entities & Business Rules) |  Entities, Value Objects, Domain Events
+-------------------------------------+

Hexagonal Architecture (Ports & Adapters):

  • Domain Core: Business logic, framework-agnostic
  • Ports: Interfaces (e.g., UserRepositoryInterface)
  • Adapters: Concrete implementations (Doctrine, InMemory for tests)

DDD Tactical Patterns:

  • Entities: Objects with identity (e.g., User, Order)
  • Value Objects: Immutable, defined by attributes (e.g., Email, Money)
  • Aggregates: Consistency boundaries with root entity
  • Domain Events: Capture business occurrences
  • Repositories: Persist/retrieve aggregates

2. Organize Directory Structure

Create the following directory structure to enforce layer separation:

src/
+-- Domain/                 # Innermost layer - no dependencies
|   +-- Entity/
|   |   +-- User.php
|   |   +-- Order.php
|   +-- ValueObject/
|   |   +-- Email.php
|   |   +-- Money.php
|   |   +-- OrderId.php
|   +-- Repository/
|   |   +-- UserRepositoryInterface.php
|   +-- Event/
|   |   +-- UserCreatedEvent.php
|   +-- Exception/
|       +-- DomainException.php
+-- Application/            # Use cases - depends on Domain
|   +-- Command/
|   |   +-- CreateUserCommand.php
|   |   +-- UpdateOrderCommand.php
|   +-- Handler/
|   |   +-- CreateUserHandler.php
|   |   +-- UpdateOrderHandler.php
|   +-- Query/
|   |   +-- GetUserQuery.php
|   +-- Dto/
|   |   +-- UserDto.php
|   +-- Service/
|       +-- NotificationServiceInterface.php
+-- Adapter/                # Interface adapters
|   +-- Http/
|   |   +-- Controller/
|   |   |   +-- UserController.php
|   |   +-- Request/
|   |       +-- CreateUserRequest.php
|   +-- Persistence/
|       +-- Doctrine/
|           +-- Repository/
|           |   +-- DoctrineUserRepository.php
|           +-- Mapping/
|               +-- User.orm.xml
+-- Infrastructure/         # Framework & external concerns
    +-- Config/
    |   +-- services.yaml
    +-- Event/
    |   +-- SymfonyEventDispatcher.php
    +-- Service/
        +-- SendgridEmailService.php

3. Implement Domain Layer

Start from the innermost layer (Domain) and work outward:

  1. Create Value Objects with validation at construction time - they must be immutable using PHP 8.1+ readonly
  2. Create Entities with domain logic and business rules - entities should encapsulate behavior, not just be data bags
  3. Define Repository Interfaces (Ports) - keep them small and focused
  4. Define Domain Events to decouple side effects from core business logic

4. Implement Application Layer

Build use cases that orchestrate domain objects:

  1. Create Commands as readonly DTOs representing write operations
  2. Create Queries for read operations (CQRS pattern)
  3. Implement Handlers that receive commands/queries and coordinate domain objects
  4. Define Service Interfaces for external dependencies (notifications, etc.)

5. Implement Adapter Layer

Create interface adapters that connect Application to Infrastructure:

  1. Create Controllers that receive HTTP requests and invoke handlers
  2. Create Request DTOs with Symfony validation attributes
  3. Implement Repository Adapters that bridge domain interfaces to persistence layer

6. Configure Infrastructure

Set up framework-specific configuration:

  1. Configure Symfony DI to bind interfaces to implementations
  2. Create test doubles (In-Memory repositories) for unit testing without database
  3. Configure Doctrine mappings for persistence

7. Test Without Framework

Ensure Domain and Application layers are testable without Symfony, Doctrine, or database. Use In-Memory repositories for fast unit tests.

Examples

Example 1: Value Object with Validation

<?php
// src/Domain/ValueObject/Email.php

namespace App\Domain\ValueObject;

use InvalidArgumentException;

final readonly class Email
{
    public function __construct(
        private string $value
    ) {
        if (!filter_var($value, FILTER_VALIDATE_EMAIL)) {
            throw new InvalidArgumentException(
                sprintf('"%s" is not a valid email address', $value)
            );
        }
    }

    public function value(): string
    {
        return $this->value;
    }

    public function equals(self $other): bool
    {
        return $this->value === $other->value;
    }

    public function domain(): string
    {
        return substr($this->value, strrpos($this->value, '@') + 1);
    }
}

Example 2: Entity with Domain Logic

<?php
// src/Domain/Entity/User.php

namespace App\Domain\Entity;

use App\Domain\ValueObject\Email;
use App\Domain\ValueObject\UserId;
use App\Domain\Event\UserCreatedEvent;
use DateTimeImmutable;

class User
{
    private array $domainEvents = [];

    public function __construct(
        private UserId $id,
        private Email $email,
        private string $name,
        private DateTimeImmutable $createdAt,
        private bool $isActive = true
    ) {
        $this->recordEvent(new UserCreatedEvent($id->value()));
    }

    public static function create(
        UserId $id,
        Email $email,
        string $name
    ): self {
        return new self(
            $id,
            $email,
            $name,
            new DateTimeImmutable()
        );
    }

    public function deactivate(): void
    {
        $this->isActive = false;
    }

    public function canPlaceOrder(): bool
    {
        return $this->isActive;
    }

    public function id(): UserId
    {
        return $this->id;
    }

    public function email(): Email
    {
        return $this->email;
    }

    public function domainEvents(): array
    {
        return $this->domainEvents;
    }

    public function clearDomainEvents(): void
    {
        $this->domainEvents = [];
    }

    private function recordEvent(object $event): void
    {
        $this->domainEvents[] = $event;
    }
}

Example 3: Repository Port (Interface)

<?php
// src/Domain/Repository/UserRepositoryInterface.php

namespace App\Domain\Repository;

use App\Domain\Entity\User;
use App\Domain\ValueObject\Email;
use App\Domain\ValueObject\UserId;

interface UserRepositoryInterface
{
    public function findById(UserId $id): ?User;

    public function findByEmail(Email $email): ?User;

    public function save(User $user): void;

    public function delete(UserId $id): void;
}

Example 4: Command and Handler

<?php
// src/Application/Command/CreateUserCommand.php

namespace App\Application\Command;

final readonly class CreateUserCommand
{
    public function __construct(
        public string $id,
        public string $email,
        public string $name
    ) {
    }
}
<?php
// src/Application/Handler/CreateUserHandler.php

namespace App\Application\Handler;

use App\Application\Command\CreateUserCommand;
use App\Domain\Entity\User;
use App\Domain\Repository\UserRepositoryInterface;
use App\Domain\ValueObject\Email;
use App\Domain\ValueObject\UserId;
use InvalidArgumentException;

readonly class CreateUserHandler
{
    public function __construct(
        private UserRepositoryInterface $userRepository
    ) {
    }

    public function __invoke(CreateUserCommand $command): void
    {
        $email = new Email($command->email);

        if ($this->userRepository->findByEmail($email) !== null) {
            throw new InvalidArgumentException(
                'User with this email already exists'
            );
        }

        $user = User::create(
            new UserId($command->id),
            $email,
            $command->name
        );

        $this->userRepository->save($user);
    }
}

Example 5: Symfony Controller

<?php
// src/Adapter/Http/Controller/UserController.php

namespace App\Adapter\Http\Controller;

use App\Adapter\Http\Request\CreateUserRequest;
use App\Application\Command\CreateUserCommand;
use App\Application\Handler\CreateUserHandler;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpKernel\Attribute\AsController;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Uid\Uuid;

#[AsController]
class UserController
{
    public function __construct(
        private CreateUserHandler $createUserHandler
    ) {
    }

    #[Route('/api/users', methods: ['POST'])]
    public function create(CreateUserRequest $request): JsonResponse
    {
        $command = new CreateUserCommand(
            id: Uuid::v4()->toRfc4122(),
            email: $request->email,
            name: $request->name
        );

        ($this->createUserHandler)($command);

        return new JsonResponse(['id' => $command->id], 201);
    }
}

Example 6: Request DTO with Validation

<?php
// src/Adapter/Http/Request/CreateUserRequest.php

namespace App\Adapter\Http\Request;

use Symfony\Component\Validator\Constraints as Assert;

class CreateUserRequest
{
    #[Assert\NotBlank]
    #[Assert\Email]
    public string $email;

    #[Assert\NotBlank]
    #[Assert\Length(min: 2, max: 100)]
    public string $name;
}

Example 7: Doctrine Repository Adapter

<?php
// src/Adapter/Persistence/Doctrine/Repository/DoctrineUserRepository.php

namespace App\Adapter\Persistence\Doctrine\Repository;

use App\Domain\Entity\User;
use App\Domain\Repository\UserRepositoryInterface;
use App\Domain\ValueObject\Email;
use App\Domain\ValueObject\UserId;
use Doctrine\ORM\EntityManagerInterface;

readonly class DoctrineUserRepository implements UserRepositoryInterface
{
    public function __construct(
        private EntityManagerInterface $entityManager
    ) {
    }

    public function findById(UserId $id): ?User
    {
        return $this->entityManager
            ->getRepository(User::class)
            ->find($id->value());
    }

    public function findByEmail(Email $email): ?User
    {
        return $this->entityManager
            ->getRepository(User::class)
            ->findOneBy(['email.value' => $email->value()]);
    }

    public function save(User $user): void
    {
        $this->entityManager->persist($user);
        $this->entityManager->flush();
    }

    public function delete(UserId $id): void
    {
        $user = $this->findById($id);
        if ($user !== null) {
            $this->entityManager->remove($user);
            $this->entityManager->flush();
        }
    }
}

Example 8: Symfony DI Configuration

# config/services.yaml
services:
    _defaults:
        autowire: true
        autoconfigure: true

    App\:
        resource: '../src/'
        exclude:
            - '../src/Domain/Entity/'
            - '../src/Kernel.php'

    # Repository binding - Port to Adapter
    App\Domain\Repository\UserRepositoryInterface:
        class: App\Adapter\Persistence\Doctrine\Repository\DoctrineUserRepository

    # In-memory repository for tests
    App\Domain\Repository\UserRepositoryInterface $inMemoryUserRepository:
        class: App\Tests\Infrastructure\Repository\InMemoryUserRepository

Example 9: In-Memory Repository for Testing

<?php
// tests/Infrastructure/Repository/InMemoryUserRepository.php

namespace App\Tests\Infrastructure\Repository;

use App\Domain\Entity\User;
use App\Domain\Repository\UserRepositoryInterface;
use App\Domain\ValueObject\Email;
use App\Domain\ValueObject\UserId;

class InMemoryUserRepository implements UserRepositoryInterface
{
    private array $users = [];

    public function findById(UserId $id): ?User
    {
        return $this->users[$id->value()] ?? null;
    }

    public function findByEmail(Email $email): ?User
    {
        foreach ($this->users as $user) {
            if ($user->email()->equals($email)) {
                return $user;
            }
        }
        return null;
    }

    public function save(User $user): void
    {
        $this->users[$user->id()->value()] = $user;
    }

    public function delete(UserId $id): void
    {
        unset($this->users[$id->value()]);
    }
}

Best Practices

  1. Dependency Rule: Dependencies only point inward - domain knows nothing of application or infrastructure
  2. Immutability: Value Objects MUST be immutable using readonly in PHP 8.1+ - never allow mutable state
  3. Rich Domain Models: Put business logic in entities with factory methods like create() - avoid anemic models
  4. Interface Segregation: Keep repository interfaces small and focused - do not create god interfaces
  5. Framework Independence: Domain and application layers MUST be testable without Symfony or Doctrine
  6. Validation at Construction: Validate in Value Objects at construction time - never allow invalid state
  7. Symfony Attributes: Use PHP 8 attributes for routing (#[Route]), validation (#[Assert\]), and DI
  8. Test Doubles: Always provide In-Memory implementations for repositories to enable fast unit tests
  9. Domain Events: Dispatch domain events to decouple side effects - do not call external services from entities
  10. XML/YAML Mappings: Use XML or YAML for Doctrine mappings instead of annotations in domain entities

Constraints and Warnings

Architecture Constraints

  • Dependency Rule: Dependencies only point inward. Domain knows nothing of Application, Application knows nothing of Infrastructure. Violating this breaks the architecture.
  • No Anemic Domain: Entities should encapsulate behavior, not just be data bags. Avoid getters/setters without business logic.
  • Interface Segregation: Keep repository interfaces small and focused. Do not create god interfaces.

PHP Implementation Constraints

  • Immutability: Value Objects MUST be immutable using readonly in PHP 8.1+. Never allow mutable state in Value Objects.
  • Validation: Validate in Value Objects at construction time. Never allow invalid state to exist.
  • Symfony Attributes: Use PHP 8 attributes for routing, validation, and DI (#[Route], #[Assert\Email], #[Autowire]).

Testing Constraints

  • Framework Independence: Domain and Application layers MUST be testable without Symfony, Doctrine, or database.
  • Test Doubles: Always provide In-Memory implementations for repository interfaces to enable fast unit tests.

Warnings

  • Avoid Rich Domain Models in Controllers: Controllers should only coordinate, not contain business logic.
  • Beware of Leaky Abstractions: Infrastructure concerns (like Doctrine annotations) should not leak into Domain entities. Use XML/YAML mappings instead.
  • Command Bus Consideration: For complex applications, use Symfony Messenger for async processing. Do not inline complex orchestrations in handlers.
  • Domain Events: Dispatch domain events to decouple side effects from core business logic. Do not call external services directly from entities.

References

适合场景

01

用户想查找某类 Agent Skill 时

02

需要根据任务场景推荐可安装能力包时

03

需要对比不同来源的安装命令和来源信息时

能力概览

能力 1

按任务关键词查找相关 Skills

能力 2

展示可复制的安装命令

能力 3

保留来源站点、仓库和原始说明,方便继续核验

能力 4

展示第三方安全扫描或审计结果

安装后应在对应宿主中按原始 README 的触发条件使用;具体调用方式请以来源页面和 README 为准。

平台分布

Codex

33.51%
按下载量换算254

Claude

30.87%
按下载量换算234

Cursor

18.12%
按下载量换算137

Gemini CLI

8.06%
按下载量换算61

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

通过

权限和风险

external-service

该 Skill 可能调用第三方服务、云服务或外部模型 API,使用前需要确认账号、额度、数据发送范围和服务条款。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。当前只有一个来源,正式发布前建议补源仓库或其他目录站核验。

来源信息

继续浏览同类 Skills