Files
michaelschiemer/docs/ARCHITECTURE-DECISION.md
Michael Schiemer 36ef2a1e2c
Some checks failed
🚀 Build & Deploy Image / Determine Build Necessity (push) Failing after 10m14s
🚀 Build & Deploy Image / Build Runtime Base Image (push) Has been skipped
🚀 Build & Deploy Image / Build Docker Image (push) Has been skipped
🚀 Build & Deploy Image / Run Tests & Quality Checks (push) Has been skipped
🚀 Build & Deploy Image / Auto-deploy to Staging (push) Has been skipped
🚀 Build & Deploy Image / Auto-deploy to Production (push) Has been skipped
Security Vulnerability Scan / Check for Dependency Changes (push) Failing after 11m25s
Security Vulnerability Scan / Composer Security Audit (push) Has been cancelled
fix: Gitea Traefik routing and connection pool optimization
- Remove middleware reference from Gitea Traefik labels (caused routing issues)
- Optimize Gitea connection pool settings (MAX_IDLE_CONNS=30, authentication_timeout=180s)
- Add explicit service reference in Traefik labels
- Fix intermittent 504 timeouts by improving PostgreSQL connection handling

Fixes Gitea unreachability via git.michaelschiemer.de
2025-11-09 14:46:15 +01:00

1088 lines
33 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Architecture Decision: Layer-Based vs Module-Based Organization
**Decision Date**: 2025-01-28
**Status**: Recommended
**Deciders**: Development Team
**Related Documents**:
- `SRC-ARCHITECTURE-ANALYSIS.md` - Current architecture analysis
- `DOCUMENTATION-ANALYSIS.md` - Documentation structure analysis
---
## Executive Summary
**Decision**: Maintain 4-layer Clean Architecture with enhanced modular organization within each layer (Hybrid Approach)
**Rationale**:
- Framework layer constitutes 85.3% of codebase (3,883 files)
- Pure module-based approach would duplicate this massive framework across modules
- Hybrid approach provides modular benefits while preserving shared framework infrastructure
- Estimated effort: 2-3 months (vs 6-12 months for pure module approach)
**Impact**:
- ✅ Maintains Clean Architecture compliance
- ✅ Achieves Domain-Driven Design benefits
- ✅ Avoids massive code duplication
- ✅ Minimizes refactoring effort
- ✅ Preserves existing framework investments
---
## Context & Problem Statement
### Current Architecture State
**Analyzed Structure** (from `SRC-ARCHITECTURE-ANALYSIS.md`):
```
Total Files: 4,551 PHP files
Total Directories: 1,052
Layer Distribution:
├── Framework: 3,883 files (85.3%) - TOO LARGE
├── Application: 420 files (9.2%) - Below target
├── Domain: 184 files (4.0%) - TOO THIN
└── Infrastructure: 64 files (1.5%) - Below target
```
**Current Grade**: C+ (75%) - "Good foundation with critical architectural flaws"
### Identified Problems
**3 Critical Architectural Violations**:
1. **HTTP Layer in Domain** (`src/Domain/Meta/Http/`)
- Controllers with `#[Route]` attributes in Domain layer
- 406 lines of HTTP-specific code misplaced
2. **DI Container Setup in Domain** (5 domains affected)
- Framework-specific dependency injection in Domain
- Violates Dependency Rule (Domain depends on Framework)
3. **Infrastructure Nested in Application** (`src/Application/Website/Infrastructure/GeoIp/`)
- Wrong dependency direction
- Confusing architecture
### Strategic Question
**"Should we maintain 4-layer architecture or switch to pure module-based organization?"**
This decision has fundamental implications:
- Code organization strategy
- Framework reusability
- Refactoring effort (months vs years)
- Team cognitive load
- Maintainability long-term
---
## Decision Drivers
### Key Factors Influencing Decision
1. **Framework Size**: 85.3% of codebase is framework infrastructure
2. **Code Duplication Risk**: Pure modules would replicate framework per module
3. **Clean Architecture Benefits**: Dependency inversion, testability, maintainability
4. **Domain-Driven Design Goals**: Bounded contexts, domain focus
5. **Refactoring Effort**: Time and risk considerations
6. **Team Familiarity**: Existing knowledge of current structure
7. **Framework Reusability**: Shared infrastructure across all modules
### Stakeholder Concerns
- **Development Team**: Minimize disruption, preserve framework investments
- **Architecture**: Maintain Clean Architecture principles
- **Business**: Minimize refactoring time, maintain velocity
- **Quality**: Improve testability, reduce coupling
---
## Considered Options
### Option 1: Pure Module-Based (Vertical Slicing)
**Structure**:
```
src/
├── User/ # Self-contained module
│ ├── Domain/
│ ├── Application/
│ ├── Infrastructure/
│ └── Framework/ # Duplicated framework code!
├── Order/ # Self-contained module
│ ├── Domain/
│ ├── Application/
│ ├── Infrastructure/
│ └── Framework/ # Duplicated framework code!
└── Product/ # Self-contained module
├── Domain/
├── Application/
├── Infrastructure/
└── Framework/ # Duplicated framework code!
```
**Advantages**:
- ✅ True bounded contexts - modules are completely independent
- ✅ Team autonomy - each module owned by dedicated team
- ✅ Deploy modules independently (microservices-ready)
- ✅ Pure Domain-Driven Design implementation
- ✅ No cross-module dependencies
**Disadvantages**:
-**CRITICAL**: Framework duplication (85.3% × modules = massive overhead)
- ❌ Framework updates must propagate to ALL modules
- ❌ Code duplication: HTTP layer, Database, Cache, Queue, Events
- ❌ Increased maintenance burden (N frameworks to update)
- ❌ Inconsistency risk across modules
- ❌ Massive refactoring effort (6-12 months)
- ❌ Loss of framework investment (3,883 files built over time)
**Estimated Effort**: 6-12 months
**Risk Level**: HIGH
---
### Option 2: Hybrid Approach (Layers + Modules)
**Recommended Structure**:
```
src/
├── Domain/ (20-30% - business logic)
│ ├── User/ (Module - bounded context)
│ │ ├── Entity/
│ │ ├── ValueObject/
│ │ ├── Event/
│ │ ├── Repository/ (Interfaces only!)
│ │ └── Service/
│ ├── Order/ (Module - bounded context)
│ │ ├── Entity/
│ │ ├── ValueObject/
│ │ ├── Event/
│ │ ├── Repository/
│ │ └── Service/
│ └── Product/ (Module - bounded context)
├── Application/ (25-35% - use cases)
│ ├── User/ (Module - aligned with domain)
│ │ ├── Controller/
│ │ ├── Command/
│ │ └── Query/
│ ├── Order/ (Module - aligned with domain)
│ │ ├── Controller/
│ │ ├── Command/
│ │ └── Query/
│ └── Admin/ (Feature module - cross-cutting)
│ ├── Analytics/
│ ├── Content/
│ └── System/
├── Framework/ (40-50% - SHARED infrastructure)
│ ├── Core/ # Shared by ALL modules
│ ├── Database/ # Shared by ALL modules
│ ├── Http/ # Shared by ALL modules
│ ├── Cache/ # Shared by ALL modules
│ ├── Queue/ # Shared by ALL modules
│ ├── Events/ # Shared by ALL modules
│ └── [32 more shared components]
└── Infrastructure/ (5-10% - external integrations)
└── ServiceProviders/ # Module-specific DI setup
├── UserServiceProvider.php
├── OrderServiceProvider.php
└── ProductServiceProvider.php
```
**Advantages**:
- ✅ Clean Architecture compliance maintained
-**Framework shared across all modules** (no duplication!)
- ✅ Modular organization where beneficial (Domain, Application)
- ✅ Domain-Driven Design benefits (bounded contexts)
- ✅ Reduced refactoring effort (2-3 months)
- ✅ Framework investment preserved
- ✅ Single framework to maintain and update
- ✅ Consistency across modules (shared patterns)
- ✅ Testability (clear boundaries, mocking framework components)
**Disadvantages**:
- ⚠️ Framework is shared dependency (not pure bounded contexts)
- ⚠️ Module deployment still coupled via framework
- ⚠️ Requires discipline to avoid cross-domain dependencies
**Estimated Effort**: 2-3 months
**Risk Level**: LOW-MEDIUM
---
### Option 3: Keep Current Structure (Do Nothing)
**Structure**: Current 4-layer architecture without modular organization
**Advantages**:
- ✅ Zero refactoring effort
- ✅ No disruption to development
**Disadvantages**:
- ❌ Architectural violations remain (HTTP in Domain, DI in Domain)
- ❌ Poor domain organization (Domain layer too thin at 4%)
- ❌ Framework bloat (85.3% - too large)
- ❌ Technical debt accumulation
- ❌ Reduced maintainability over time
**Estimated Effort**: 0 months (no change)
**Risk Level**: HIGH (accumulating technical debt)
---
## Decision Outcome
### Chosen Option: **Option 2 - Hybrid Approach (Layers + Modules)**
**Rationale**:
1. **Framework Reusability**: 85.3% of codebase is framework infrastructure that SHOULD be shared
- Database layer (387 files) - shared across all modules
- HTTP layer (156 files) - shared routing, middleware
- Queue system (89 files) - shared background processing
- Cache layer (67 files) - shared caching infrastructure
- 40 more framework components - all reusable
2. **Clean Architecture Preserved**: Maintains dependency rule while adding modularity
- Domain depends on nothing
- Application depends on Domain
- Framework provides infrastructure
- Infrastructure implements interfaces
3. **Domain-Driven Design Benefits**: Modules represent bounded contexts
- `Domain/User/` - user bounded context
- `Domain/Order/` - order bounded context
- `Domain/Product/` - product bounded context
- Clear boundaries, no cross-domain dependencies
4. **Realistic Refactoring Timeline**: 2-3 months vs 6-12 months
- Phase 1: Priority 1 Fixes (2-3 days)
- Phase 2: Domain Layer Modularization (1-2 weeks)
- Phase 3: Application Layer Modularization (1-2 weeks)
- Phase 4: Infrastructure Consolidation (1-2 weeks)
5. **Risk Mitigation**: Incremental changes, testable at each phase
---
## Implementation Strategy
### Phase 1: Priority 1 Fixes (2-3 days) - CRITICAL
**Objective**: Fix architectural violations
**Tasks**:
1. **Move HTTP out of Domain**
```bash
# Before: src/Domain/Meta/Http/Controller/MetaAdminController.php
# After: src/Application/Admin/Controllers/Meta/MetaAdminController.php
mv src/Domain/Meta/Http/Controller/ src/Application/Admin/Controllers/Meta/
mv src/Domain/Meta/Http/Middleware/ src/Framework/Http/Middlewares/
```
2. **Move DI to Infrastructure**
```bash
mkdir -p src/Infrastructure/ServiceProviders
# Transform Initializers to ServiceProviders
mv src/Domain/Asset/DI/AssetServiceInitializer.php \
src/Infrastructure/ServiceProviders/AssetServiceProvider.php
mv src/Domain/Cms/DI/CmsServiceInitializer.php \
src/Infrastructure/ServiceProviders/CmsServiceProvider.php
# Repeat for User, Order, Console domains
```
3. **Fix Infrastructure Nesting**
```bash
mv src/Application/Website/Infrastructure/GeoIp/ \
src/Infrastructure/GeoIp/
```
4. **Update Namespaces and Imports**
- Run PSR-4 compliance check
- Update all references to moved files
- Test all affected functionality
**Success Criteria**:
- ✅ Zero architectural violations
- ✅ All tests pass
- ✅ PSR-4 compliance: 100%
**Code Example - ServiceProvider Transformation**:
```php
// Before: src/Domain/Asset/DI/AssetServiceInitializer.php
namespace App\Domain\Asset\DI;
use App\Framework\DI\Container;
use App\Framework\DI\Initializer;
final readonly class AssetServiceInitializer
{
#[Initializer]
public function __invoke(Container $container): void
{
$container->singleton(AssetRepository::class, ...);
$container->singleton(AssetService::class, ...);
}
}
// After: src/Infrastructure/ServiceProviders/AssetServiceProvider.php
namespace App\Infrastructure\ServiceProviders;
use App\Framework\DI\Container;
use App\Framework\DI\ServiceProvider;
final readonly class AssetServiceProvider implements ServiceProvider
{
public function register(Container $container): void
{
$container->singleton(AssetRepository::class, ...);
$container->singleton(AssetService::class, ...);
}
}
```
---
### Phase 2: Domain Layer Modularization (1-2 weeks)
**Objective**: Organize Domain layer by bounded contexts
**Current State**:
```
src/Domain/
├── User/ (23 files) - mixed structure
├── Asset/ (18 files) - mixed structure
├── Cms/ (15 files) - mixed structure
├── Order/ (29 files) - mixed structure
└── Product/ (12 files) - mixed structure
```
**Target State**:
```
src/Domain/
├── User/ # User bounded context
│ ├── Entity/
│ │ └── User.php
│ ├── ValueObject/
│ │ ├── UserId.php
│ │ ├── Email.php
│ │ └── UserName.php
│ ├── Event/
│ │ ├── UserRegisteredEvent.php
│ │ └── UserUpdatedEvent.php
│ ├── Repository/
│ │ └── UserRepositoryInterface.php # Interface only!
│ └── Service/
│ └── UserService.php
├── Order/ # Order bounded context
│ ├── Entity/
│ │ ├── Order.php
│ │ └── OrderItem.php
│ ├── ValueObject/
│ │ ├── OrderId.php
│ │ ├── OrderStatus.php
│ │ └── Money.php
│ ├── Event/
│ │ ├── OrderCreatedEvent.php
│ │ └── OrderCompletedEvent.php
│ ├── Repository/
│ │ └── OrderRepositoryInterface.php
│ └── Service/
│ └── OrderService.php
└── Product/ # Product bounded context
├── Entity/
├── ValueObject/
├── Event/
├── Repository/
└── Service/
```
**Benefits**:
- Clear bounded contexts
- Self-documenting structure
- Easy to locate domain logic
- No cross-domain dependencies
- Repository interfaces (implementations in Infrastructure)
---
### Phase 3: Application Layer Modularization (1-2 weeks)
**Objective**: Align Application layer with Domain modules
**Current State**:
```
src/Application/
├── Website/ (89 files) - feature-based
├── Admin/ (234 files) - feature-based
├── Api/ (67 files) - feature-based
├── Contact/ (8 files) - feature-based
└── Console/ (22 files) - feature-based
```
**Target State**:
```
src/Application/
├── User/ # Aligned with Domain/User
│ ├── Controller/
│ │ └── UserController.php
│ ├── Command/
│ │ ├── CreateUserCommand.php
│ │ └── CreateUserHandler.php
│ └── Query/
│ ├── GetUserQuery.php
│ └── GetUserHandler.php
├── Order/ # Aligned with Domain/Order
│ ├── Controller/
│ ├── Command/
│ └── Query/
├── Product/ # Aligned with Domain/Product
│ ├── Controller/
│ ├── Command/
│ └── Query/
└── Admin/ # Cross-cutting feature module
├── Analytics/
├── Content/
├── Infrastructure/
├── Notifications/
└── System/
```
**Benefits**:
- Clear mapping: Application/User → Domain/User
- CQRS patterns (Command/Query separation)
- Feature-based organization where appropriate (Admin)
- Reduced cognitive load
---
### Phase 4: Infrastructure Consolidation (1-2 weeks)
**Objective**: Centralize infrastructure implementations
**Target State**:
```
src/Infrastructure/
├── ServiceProviders/ # DI setup per module
│ ├── UserServiceProvider.php
│ ├── OrderServiceProvider.php
│ └── ProductServiceProvider.php
├── Persistence/ # Database implementations
│ ├── User/
│ │ └── DatabaseUserRepository.php # Implements Domain/User/Repository/UserRepositoryInterface
│ ├── Order/
│ │ └── DatabaseOrderRepository.php
│ └── Product/
│ └── DatabaseProductRepository.php
├── GeoIp/ # External service integrations
│ ├── GeoIpService.php
│ └── MaxMindProvider.php
└── Email/ # External service integrations
├── EmailService.php
└── SmtpProvider.php
```
**Benefits**:
- Infrastructure implementations separated from Domain interfaces
- Clear dependency inversion (Domain → Interface ← Infrastructure)
- Easy to swap implementations (testing, different databases)
- ServiceProviders wire up dependencies
---
## Consequences
### Positive Consequences
1. **Clean Architecture Compliance**
- All layers respect Dependency Rule
- Domain layer pure (no framework dependencies)
- Testability improved (mock infrastructure easily)
2. **Domain-Driven Design Benefits**
- Clear bounded contexts (User, Order, Product)
- Domain logic focused and cohesive
- Business rules visible and maintainable
3. **Framework Preservation**
- No code duplication (Framework shared)
- Single framework to maintain
- Consistency across all modules
- Framework investment preserved (3,883 files)
4. **Maintainability**
- Clear structure reduces cognitive load
- Easy to locate code (Domain/User/Entity/User.php)
- Self-documenting architecture
- Onboarding new developers easier
5. **Realistic Timeline**
- 2-3 months total effort
- Incremental changes (low risk)
- Testable at each phase
### Negative Consequences (Mitigated)
1. **Framework is Shared Dependency**
- **Mitigation**: Framework is stable, well-tested infrastructure
- **Trade-off**: Accepting this trade-off for massive code reuse benefit
2. **Module Deployment Coupling**
- **Mitigation**: Not pursuing microservices architecture (monolith is appropriate)
- **Trade-off**: Deployment simplicity vs independent module deployment
3. **Discipline Required**
- **Mitigation**: Code review process, automated PSR-4 checks, architectural tests
- **Trade-off**: Team training vs long-term maintainability
---
## Comparison Matrix
| Aspect | Pure Modules | Hybrid (Recommended) | Keep Current |
|--------|-------------|---------------------|--------------|
| **Clean Architecture** | ✅ Yes | ✅ Yes | ❌ Violations exist |
| **DDD Bounded Contexts** | ✅ Pure | ✅ Practical | ❌ Weak |
| **Framework Duplication** | ❌ Massive (85.3% × N) | ✅ None (shared) | ✅ None |
| **Code Reuse** | ❌ Low (per-module) | ✅ High (shared) | ✅ High |
| **Refactoring Effort** | ❌ 6-12 months | ✅ 2-3 months | ✅ 0 months |
| **Risk Level** | ❌ HIGH | ✅ LOW-MEDIUM | ❌ HIGH (debt) |
| **Team Autonomy** | ✅ Full (per module) | ⚠️ Partial | ❌ None |
| **Deploy Independence** | ✅ Yes (microservices) | ❌ No (monolith) | ❌ No |
| **Maintenance Burden** | ❌ High (N frameworks) | ✅ Low (1 framework) | ⚠️ Medium |
| **Architectural Quality** | ✅ A+ (pure DDD) | ✅ A (pragmatic) | ❌ C+ (violations) |
**Scoring**:
- Pure Modules: 5/10 (ideal theory, impractical for this project)
- **Hybrid: 9/10 (recommended - pragmatic and effective)**
- Keep Current: 3/10 (accumulating technical debt)
---
## Migration Risks & Mitigation
### Risk 1: Breaking Changes During Refactoring
**Probability**: MEDIUM
**Impact**: HIGH
**Mitigation**:
- Comprehensive test suite BEFORE starting
- Feature flags for gradual rollout
- Incremental changes with continuous testing
- Rollback plan for each phase
### Risk 2: Team Resistance to Change
**Probability**: LOW
**Impact**: MEDIUM
**Mitigation**:
- Document rationale (this document)
- Training sessions on new structure
- Gradual transition (4 phases over 2-3 months)
- Team involvement in decision-making
### Risk 3: Overlooked Dependencies
**Probability**: MEDIUM
**Impact**: MEDIUM
**Mitigation**:
- Automated dependency analysis
- PSR-4 compliance checks
- Static analysis tools (PHPStan)
- Code review process for all moves
### Risk 4: Timeline Overrun
**Probability**: LOW
**Impact**: MEDIUM
**Mitigation**:
- Conservative time estimates (2-3 months)
- Phase-based approach (can pause between phases)
- Automated tooling for bulk operations
- Dedicated refactoring time (not mixed with feature work)
---
## Success Metrics
### Phase 1 Completion (Priority 1 Fixes)
- ✅ Zero architectural violations
- ✅ All tests pass (100%)
- ✅ PSR-4 compliance: 100%
- ✅ No HTTP code in Domain layer
- ✅ No DI code in Domain layer
- ✅ Infrastructure at top level (not nested)
### Phase 2-4 Completion (Full Migration)
- ✅ Domain layer: 15-20% of codebase (from 4%)
- ✅ Framework layer: 40-50% of codebase (from 85.3%)
- ✅ Clear bounded contexts in Domain layer
- ✅ Application layer aligned with Domain modules
- ✅ Infrastructure ServiceProviders per module
- ✅ Overall Architecture Grade: A- (90%) (from C+ 75%)
### Long-Term Success
- ✅ Reduced time to locate code (faster development)
- ✅ Easier onboarding (clear structure)
- ✅ Fewer architectural violations (ongoing)
- ✅ Higher test coverage (better boundaries)
- ✅ Improved maintainability (team feedback)
---
## Code Examples
### Example 1: Domain Module (User Bounded Context)
**Before Migration**:
```
src/Domain/User/
├── User.php # Mixed: Entity + logic
├── UserRepository.php # Confusion: Interface or implementation?
├── UserService.php # Service
└── DI/
└── UserServiceInitializer.php # WRONG: DI in Domain!
```
**After Migration**:
```
src/Domain/User/
├── Entity/
│ └── User.php # Pure entity
├── ValueObject/
│ ├── UserId.php # Type-safe ID
│ ├── Email.php # Validated email
│ └── UserName.php # Validated name
├── Event/
│ └── UserRegisteredEvent.php # Domain event
├── Repository/
│ └── UserRepositoryInterface.php # Interface only!
└── Service/
└── UserService.php # Domain service
```
**Domain Entity** (Pure business logic):
```php
// src/Domain/User/Entity/User.php
namespace App\Domain\User\Entity;
use App\Domain\User\ValueObject\{UserId, Email, UserName};
use App\Domain\User\Event\UserRegisteredEvent;
final readonly class User
{
public function __construct(
public UserId $id,
public Email $email,
public UserName $name,
public bool $active = true
) {}
public static function register(Email $email, UserName $name): self
{
$user = new self(
id: UserId::generate(),
email: $email,
name: $name
);
// Domain event - no framework dependency!
DomainEvents::raise(new UserRegisteredEvent($user));
return $user;
}
public function deactivate(): self
{
return new self(
id: $this->id,
email: $this->email,
name: $this->name,
active: false
);
}
}
```
**Repository Interface** (Domain layer):
```php
// src/Domain/User/Repository/UserRepositoryInterface.php
namespace App\Domain\User\Repository;
use App\Domain\User\Entity\User;
use App\Domain\User\ValueObject\{UserId, Email};
interface UserRepositoryInterface
{
public function find(UserId $id): ?User;
public function findByEmail(Email $email): ?User;
public function save(User $user): void;
public function delete(User $user): void;
}
```
**Repository Implementation** (Infrastructure layer):
```php
// src/Infrastructure/Persistence/User/DatabaseUserRepository.php
namespace App\Infrastructure\Persistence\User;
use App\Domain\User\Repository\UserRepositoryInterface;
use App\Domain\User\Entity\User;
use App\Framework\Database\EntityManager;
final readonly class DatabaseUserRepository implements UserRepositoryInterface
{
public function __construct(
private EntityManager $entityManager
) {}
public function find(UserId $id): ?User
{
return $this->entityManager->find(User::class, $id->value);
}
public function save(User $user): void
{
$this->entityManager->save($user);
}
}
```
**ServiceProvider** (Infrastructure layer - wires dependencies):
```php
// src/Infrastructure/ServiceProviders/UserServiceProvider.php
namespace App\Infrastructure\ServiceProviders;
use App\Framework\DI\Container;
use App\Domain\User\Repository\UserRepositoryInterface;
use App\Infrastructure\Persistence\User\DatabaseUserRepository;
final readonly class UserServiceProvider
{
public function register(Container $container): void
{
// Wire Domain interface → Infrastructure implementation
$container->singleton(
UserRepositoryInterface::class,
fn() => new DatabaseUserRepository(
$container->get(EntityManager::class)
)
);
}
}
```
---
### Example 2: Application Layer (CQRS Pattern)
**Structure**:
```
src/Application/User/
├── Controller/
│ └── UserController.php
├── Command/ # Write operations
│ ├── CreateUserCommand.php
│ └── CreateUserHandler.php
└── Query/ # Read operations
├── GetUserQuery.php
└── GetUserHandler.php
```
**Controller** (Thin - delegates to Command/Query handlers):
```php
// src/Application/User/Controller/UserController.php
namespace App\Application\User\Controller;
use App\Framework\Attributes\Route;
use App\Framework\Http\{Method, JsonResult, Status};
use App\Application\User\Command\{CreateUserCommand, CreateUserHandler};
use App\Application\User\Query\{GetUserQuery, GetUserHandler};
final readonly class UserController
{
public function __construct(
private CreateUserHandler $createHandler,
private GetUserHandler $getHandler
) {}
#[Route(path: '/api/users', method: Method::POST)]
public function create(CreateUserRequest $request): JsonResult
{
$command = new CreateUserCommand(
$request->email,
$request->name
);
$user = $this->createHandler->handle($command);
return new JsonResult(
['id' => $user->id->value],
status: Status::CREATED
);
}
#[Route(path: '/api/users/{id}', method: Method::GET)]
public function get(string $id): JsonResult
{
$query = new GetUserQuery(new UserId($id));
$user = $this->getHandler->handle($query);
return new JsonResult($user->toArray());
}
}
```
**Command Handler** (Business logic):
```php
// src/Application/User/Command/CreateUserHandler.php
namespace App\Application\User\Command;
use App\Domain\User\Entity\User;
use App\Domain\User\Repository\UserRepositoryInterface;
final readonly class CreateUserHandler
{
public function __construct(
private UserRepositoryInterface $repository
) {}
public function handle(CreateUserCommand $command): User
{
// Domain logic
$user = User::register($command->email, $command->name);
// Persistence via Domain interface
$this->repository->save($user);
return $user;
}
}
```
---
## Alternative Approaches Considered
### Alternative 1: Microservices Architecture
**Why Rejected**:
- Premature optimization for current scale
- Operational complexity (deployment, monitoring, debugging)
- Network latency overhead
- Distributed transaction challenges
- Team not structured for microservices (no dedicated platform team)
### Alternative 2: Event Sourcing + CQRS (Full)
**Why Rejected**:
- Significant complexity increase
- Learning curve for team
- Infrastructure requirements (event store)
- Eventual consistency challenges
- Overkill for current business requirements
### Alternative 3: Module-per-Repository (Monorepo)
**Why Rejected**:
- Still duplicates Framework code per repository
- Tooling complexity (cross-repo dependencies)
- Versioning challenges
- Overhead not justified for current team size
---
## Related Decisions
- **ADR-001**: Use Custom PHP Framework (already decided)
- **ADR-002**: Adopt Clean Architecture principles (already decided)
- **ADR-003**: Use Composition over Inheritance (already decided)
- **ADR-004**: Value Objects over Primitives (already decided)
- **ADR-005**: Attribute-based Discovery (already decided)
**This Decision**: **ADR-006 - Hybrid Layer-Module Architecture**
---
## References
### Internal Documents
- `docs/SRC-ARCHITECTURE-ANALYSIS.md` - Current architecture analysis
- `docs/DOCUMENTATION-ANALYSIS.md` - Documentation structure
- `docs/claude/architecture.md` - Framework architecture guide
- `docs/claude/guidelines.md` - Development guidelines
### Clean Architecture
- "Clean Architecture" by Robert C. Martin
- Dependency Rule: Dependencies point inward (Domain → Application → Framework)
- Testability: Inner layers testable without outer layers
### Domain-Driven Design
- "Domain-Driven Design" by Eric Evans
- Bounded Contexts: Clear boundaries between domains
- Ubiquitous Language: Domain-specific terminology
- Aggregates: Consistency boundaries
### Framework Patterns
- Martin Fowler's Enterprise Application Patterns
- Repository Pattern: Data access abstraction
- CQRS: Command-Query Responsibility Segregation
- Event-Driven Architecture: Loose coupling via events
---
## Timeline & Next Steps
### Immediate Next Steps (Week 1)
**Day 1-2**: Phase 1 - Priority 1 Fixes
- Move HTTP out of Domain (2 hours)
- Move DI to Infrastructure (4 hours)
- Fix Infrastructure nesting (1 hour)
- Update namespaces (3 hours)
- **Total**: 10 hours
**Day 3**: Testing & Validation
- Run full test suite
- PSR-4 compliance check
- Architectural violation check
- **Total**: 4 hours
### Short-Term (Weeks 2-4): Phase 2 - Domain Modularization
- Organize User domain (1 week)
- Organize Order domain (1 week)
- Organize Product, Asset, Cms domains (1 week)
### Mid-Term (Weeks 5-8): Phase 3 - Application Modularization
- Restructure User application layer (1 week)
- Restructure Order application layer (1 week)
- Restructure Admin feature module (2 weeks)
### Long-Term (Weeks 9-12): Phase 4 - Infrastructure Consolidation
- Create ServiceProviders (1 week)
- Organize Persistence layer (1 week)
- Migrate external service integrations (1 week)
- Final testing & documentation (1 week)
**Total Timeline**: 12 weeks (3 months) - conservative estimate
---
## Approval & Sign-Off
**Decision Maker**: Development Team Lead
**Date**: 2025-01-28
**Status**: Recommended for Approval
**Review Required By**:
- [ ] Technical Architect
- [ ] Development Team
- [ ] Product Owner
- [ ] CTO
**Implementation Start**: After approval
**Estimated Completion**: 3 months from start
---
## Appendix A: Framework Layer Breakdown
**Why Framework Must Be Shared** (3,883 files - 85.3%):
```
src/Framework/
├── Core/ (245 files) - Application bootstrap, routing, DI
├── Database/ (387 files) - EntityManager, Schema Builder, Migrations
├── Http/ (156 files) - Request/Response, Middleware, Routing
├── Queue/ (89 files) - Job processing, Workers, Queue drivers
├── Cache/ (67 files) - Cache abstraction, Drivers (Redis, File)
├── ExceptionHandling/ (124 files) - Error handling, Exception types
├── Discovery/ (78 files) - Attribute discovery, Convention scanning
├── View/ (201 files) - Template engine, Components
├── Events/ (95 files) - Event dispatcher, Domain events
├── Console/ (167 files) - CLI commands, TUI components
├── Validation/ (54 files) - Input validation, Rules
├── Security/ (89 files) - Authentication, Authorization, WAF
├── Logging/ (48 files) - Logger, Handlers
├── MCP/ (123 files) - Model Context Protocol (AI integration)
└── [28 more components] (1,960 files)
Total: 3,883 files
```
**Duplication Impact**: If pure module approach chosen, each module would duplicate most of these 3,883 files, resulting in:
- 5 modules × 3,883 files = **19,415 files** (vs current 3,883)
- Maintenance nightmare: Update framework → update 5 copies
- Consistency issues: Framework versions drift across modules
- Lost investment: Years of framework development duplicated
**Conclusion**: Framework MUST be shared infrastructure, not duplicated per module.
---
## Appendix B: PSR-4 Mapping
**After Migration**:
```json
{
"autoload": {
"psr-4": {
"App\\Domain\\": "src/Domain/",
"App\\Application\\": "src/Application/",
"App\\Framework\\": "src/Framework/",
"App\\Infrastructure\\": "src/Infrastructure/"
}
}
}
```
**Example Mappings**:
```
Namespace → File
App\Domain\User\Entity\User → src/Domain/User/Entity/User.php
App\Domain\User\Repository\UserRepositoryInterface → src/Domain/User/Repository/UserRepositoryInterface.php
App\Application\User\Command\CreateUserCommand → src/Application/User/Command/CreateUserCommand.php
App\Infrastructure\Persistence\User\DatabaseUserRepository → src/Infrastructure/Persistence/User/DatabaseUserRepository.php
App\Framework\Database\EntityManager → src/Framework/Database/EntityManager.php
```
**Validation**:
```bash
# Verify PSR-4 compliance
composer dump-autoload --optimize
composer validate --strict
# Check for PSR-4 violations
./vendor/bin/phpstan analyze --level=8
```
---
**End of Document**
**Version**: 1.0
**Last Updated**: 2025-01-28
**Authors**: Architecture Team
**Reviewers**: Development Team