Claude-skill-registry code-organization
Enforce code organization principles - "Directory X contains ONLY class type X", DDD naming patterns, PHP best practices, type safety, and SOLID principles. Use when reviewing code structure, placing classes, or ensuring proper organization.
git clone https://github.com/majiayu000/claude-skill-registry
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/code-organization" ~/.claude/skills/majiayu000-claude-skill-registry-code-organization && rm -rf "$T"
skills/data/code-organization/SKILL.mdCode Organization Skill
Core Principle
Directory X contains ONLY class type X
This is the fundamental rule for code organization in this codebase.
Context (Input)
- Creating new classes and determining correct directory
- Moving classes to proper locations
- Reviewing code for organizational compliance
- Fixing organizational issues from code reviews
- Ensuring class names match their responsibilities
Task (Function)
Enforce strict code organization principles: proper directory structure, DDD naming conventions, specific variable names, type safety, SOLID principles, and PHP best practices.
Directory Type Classification
Classes MUST be in directories matching their type:
| Directory | Contains ONLY | Example |
|---|---|---|
| Type converters | |
| Data transformers (DB/serial) | |
| Validation logic | |
| Object builders | |
| Data fixers/modifiers | |
| Data cleaners/filters | |
| Object factories | |
| Value resolvers | |
| Serializers/normalizers | |
| Data formatters | |
| Data mappers | |
| Data/service providers | |
| API Platform processors | |
| Event listeners (Symfony) | |
| Event subscribers (Symfony/App) | |
DDD Naming Patterns
By Layer and Type
| Layer | Class Type | Naming Pattern | Example |
|---|---|---|---|
| Domain | Entity | | |
| Value Object | | , | |
| Domain Event | | | |
| Repository Iface | | | |
| Exception | | | |
| Application | Command | | |
| Command Handler | | | |
| Event Subscriber | | | |
| DTO | | | |
| Processor | | | |
| Transformer | | | |
| Infrastructure | Repository | | |
| Doctrine Type | | | |
| Bus Implementation | | |
Directory Structure by Layer
src/{Context}/ ├── Application/ │ ├── Command/ ← Commands │ ├── CommandHandler/ ← Command Handlers │ ├── EventSubscriber/ ← Event Subscribers │ ├── DTO/ ← Data Transfer Objects │ ├── Processor/ ← API Platform Processors │ ├── Transformer/ ← Data Transformers │ ├── Validator/ ← Validators │ ├── Converter/ ← Type Converters │ ├── Resolver/ ← Value Resolvers │ ├── Factory/ ← Factories │ ├── Builder/ ← Builders │ ├── Formatter/ ← Formatters │ └── MutationInput/ ← GraphQL Mutation Inputs ├── Domain/ │ ├── Entity/ ← Entities & Aggregates │ ├── ValueObject/ ← Value Objects │ ├── Event/ ← Domain Events │ ├── Repository/ ← Repository Interfaces │ └── Exception/ ← Domain Exceptions └── Infrastructure/ ├── Repository/ ← Repository Implementations ├── DoctrineType/ ← Custom Doctrine Types ├── EventSubscriber/ ← Infrastructure Event Subscribers ├── EventListener/ ← Symfony Event Listeners └── Bus/ ← Message Bus Implementations
Verification Checklist
When creating or reviewing a class, verify:
- ✅ Class Type Matches Directory (Directory X contains ONLY class type X)
- Example:
inUlidValidator
, NOTValidator/Transformer/
- Example:
- ✅ Class Name Follows DDD Pattern for its type
- ✅ Namespace Matches Directory Structure exactly
- ✅ Class Name Reflects Actual Functionality
- ✅ Correct Layer (Domain/Application/Infrastructure)
- ✅ Domain Layer Has NO Framework Imports (Symfony/Doctrine/API Platform)
- ✅ Variable Names Are Specific (not vague)
- ✅
,$typeConverter
(specific)$scalarResolver - ❌
,$converter
(too vague)$resolver
- ✅
- ✅ Parameter Names Match Actual Types
- ✅
when accepts any typemixed $value - ❌
when accepts mixedstring $binary
- ✅
- ✅ No "Helper" or "Util" Classes (extract specific responsibilities)
PHP Best Practices
Required Patterns
- ✅ Constructor property promotion
- ✅ Inject ALL dependencies (no default instantiation)
- ✅ Use
when appropriatereadonly - ✅ Use
for classes that shouldn't be extendedfinal - ✅ No static methods (except named constructors like
,create()
)from()
Anti-Patterns (Forbidden)
- ❌ Helper/Util classes - Extract specific responsibilities
- ❌ Default instantiation in constructors - Inject dependencies
- ❌ Vague variable names - Be specific
- ❌ Namespace mismatches - Must match directory structure
Factory Pattern (Maintainability & Flexibility)
Use factories when creating typed classes with dependencies or configuration
When Factories Are REQUIRED (Production Code)
- Objects with injected dependencies (timestamp providers, config, etc.)
- Objects requiring complex construction logic
- Objects needing different implementations per environment
- Objects created from external input (DTOs, metrics, etc.)
When Factories Are OPTIONAL (Tests)
- Tests can instantiate objects directly for simplicity
- Test-specific factories can be created for reusable fixtures
Factory Benefits
- ✅ Centralized object creation logic
- ✅ Easy to inject different implementations
- ✅ Configuration changes don't affect consumers
- ✅ Single place for validation/transformation
- ✅ Enables dependency injection for complex objects
Example: Bad vs Good
// ❌ BAD: Direct instantiation with configuration public function emit(BusinessMetric $metric): void { $timestamp = (int)(microtime(true) * 1000); $payload = new EmfPayload( new EmfAwsMetadata($timestamp, new EmfCloudWatchMetricConfig(...)), new EmfDimensionValueCollection(...), new EmfMetricValueCollection(...) ); $this->logger->info($payload); } // ✅ GOOD: Factory handles complexity public function emit(BusinessMetric $metric): void { $payload = $this->payloadFactory->createFromMetric($metric); $this->logger->info($payload); }
Factory Naming Convention
- creates{ObjectName}Factory
instances{ObjectName}- Location: Same namespace as the object being created
- Example:
createsEmfPayloadFactoryEmfPayload
Type Safety: Classes Over Arrays
Prefer typed classes and collections over arrays for structured data
Arrays lack type safety and self-documentation. Use concrete classes instead.
Array vs Class Comparison
| Pattern | Bad (Array) | Good (Class) |
|---|---|---|
| Configuration | | |
| Return data | | |
| Method params | | |
| Events data | | |
Benefits of Typed Classes
- ✅ IDE autocompletion and refactoring support
- ✅ Static analysis catches type errors
- ✅ Self-documenting code
- ✅ Encapsulation (validation in constructor)
- ✅ Single Responsibility
- ✅ Open/Closed principle (extend via new classes)
Collection Pattern
// ❌ BAD: Array of arrays $metrics = [ ['name' => 'OrdersPlaced', 'value' => 1], ['name' => 'OrderValue', 'value' => 99.99], ]; // ✅ GOOD: Typed collection of objects $metrics = new MetricCollection( new OrdersPlacedMetric(value: 1), new OrderValueMetric(value: 99.99) );
When Arrays ARE Acceptable
- Simple key-value maps for serialization output (
methods)toArray() - Framework integration points requiring arrays
- Temporary internal data within a single method
Cross-Cutting Concerns Pattern
Use event subscribers for cross-cutting concerns (metrics, logging), NOT direct injection into handlers
Anti-Pattern: Metrics in Command Handler
// ❌ WRONG: Metrics in command handler final class CreateCustomerHandler { public function __construct( private CustomerRepository $repository, private BusinessMetricsEmitterInterface $metrics // Wrong place! ) {} public function __invoke(CreateCustomerCommand $cmd): void { $customer = Customer::create(...); $this->repository->save($customer); $this->metrics->emit(new CustomersCreatedMetric()); // Violates SRP } }
Correct Pattern: Dedicated Event Subscriber
// ✅ CORRECT: Clean command handler final class CreateCustomerHandler { public function __construct( private CustomerRepository $repository, private EventBusInterface $eventBus ) {} public function __invoke(CreateCustomerCommand $cmd): void { $customer = Customer::create(...); $this->repository->save($customer); $this->eventBus->publish(...$customer->pullDomainEvents()); // Metrics subscriber handles emission } } // ✅ CORRECT: Metrics in dedicated subscriber final class CustomerCreatedMetricsSubscriber implements DomainEventSubscriberInterface { public function __invoke(CustomerCreatedEvent $event): void { // Error handling is automatic via DomainEventMessageHandler. // Subscribers are executed in async workers - failures are logged + emit metrics. // This ensures observability never breaks the main request (AP from CAP). $this->metricsEmitter->emit($this->metricFactory->create()); } }
Common Issues and Fixes
Issue 1: Class in Wrong Type Directory
❌ WRONG: src/Shared/Infrastructure/Transformer/UlidValidator.php ✅ CORRECT: src/Shared/Infrastructure/Validator/UlidValidator.php # Fix: mv src/Shared/Infrastructure/Transformer/UlidValidator.php \ src/Shared/Infrastructure/Validator/UlidValidator.php # Update namespace and all imports
Issue 2: Vague Variable Names
❌ WRONG: private UlidTypeConverter $converter; // Converter of what? ✅ CORRECT: private UlidTypeConverter $typeConverter; // Specific!
Issue 3: Misleading Parameter Names
❌ WRONG: public function fromBinary(mixed $binary): Ulid // Accepts mixed, not just binary ✅ CORRECT: public function fromBinary(mixed $value): Ulid // Accurate!
Issue 4: Helper/Util Classes
❌ WRONG: class CustomerHelper { public function validateEmail() {} public function formatName() {} public function convertData() {} } ✅ CORRECT: Extract specific responsibilities - CustomerEmailValidator (Validator/) - CustomerNameFormatter (Formatter/) - CustomerDataConverter (Converter/)
Issue 5: Namespace Mismatch
❌ WRONG: // File: src/Shared/Infrastructure/Validator/UlidValidator.php namespace App\Shared\Infrastructure\Transformer; // Mismatch! ✅ CORRECT: // File: src/Shared/Infrastructure/Validator/UlidValidator.php namespace App\Shared\Infrastructure\Validator; // Matches directory!
Decision Tree: Where Does It Belong?
What does the class DO? ├─ Converts between types (string ↔ object)? → Converter/ ├─ Transforms for DB/serialization? → Transformer/ ├─ Validates values? → Validator/ ├─ Builds/constructs objects? → Builder/ ├─ Fixes/modifies data? → Fixer/ ├─ Cleans/filters data? → Cleaner/ ├─ Creates complex objects? → Factory/ ├─ Resolves/determines values? → Resolver/ ├─ Normalizes/serializes? → Serializer/ ├─ Formats data for display? → Formatter/ ├─ Maps data between structures? → Mapper/ └─ Something else? → Define specific responsibility!
Verification Commands
# Check namespace consistency make phpcsfixer make psalm # Find organizational issues grep -r "class.*Helper" src/ # Find Helper classes grep -r "class.*Util" src/ # Find Util classes grep -r "private.*\$converter;" src/ # Find vague names # Verify architecture compliance make deptrac # Must show 0 violations
Constraints (Never Do This)
NEVER:
- Place class in wrong type directory (violates "Directory X contains ONLY class type X")
- Allow Domain layer to import framework code (Symfony/Doctrine/API Platform)
- Use vague variable names (
,$converter
- be specific!)$resolver - Create "Helper" or "Util" classes (extract specific responsibilities)
- Allow namespace to mismatch directory structure
- Use arrays for structured data when typed classes would be appropriate
- Inject cross-cutting concerns (metrics, logging) into command handlers
- Create complex objects directly without factories in production code
ALWAYS:
- Verify "Directory X contains ONLY class type X" principle
- Use specific variable names (
, not$typeConverter
)$converter - Use accurate parameter names (match actual types)
- Ensure namespace matches directory structure exactly
- Extract specific responsibilities from Helper/Util classes
- Prefer typed classes over arrays for structured data
- Use collections instead of arrays of objects
- Use event subscribers for cross-cutting concerns
- Use factories for complex object creation in production code
Related Skills
- code-review: References this skill for organization verification during PR reviews
- implementing-ddd-architecture: DDD patterns and layer structure
- deptrac-fixer: Fixes architectural boundary violations
- quality-standards: Maintains overall code quality metrics
Related Documentation
See
reference/troubleshooting.md for detailed troubleshooting and examples/organization-fixes.md for real-world examples.