feat(Production): Complete production deployment infrastructure

- Add comprehensive health check system with multiple endpoints
- Add Prometheus metrics endpoint
- Add production logging configurations (5 strategies)
- Add complete deployment documentation suite:
  * QUICKSTART.md - 30-minute deployment guide
  * DEPLOYMENT_CHECKLIST.md - Printable verification checklist
  * DEPLOYMENT_WORKFLOW.md - Complete deployment lifecycle
  * PRODUCTION_DEPLOYMENT.md - Comprehensive technical reference
  * production-logging.md - Logging configuration guide
  * ANSIBLE_DEPLOYMENT.md - Infrastructure as Code automation
  * README.md - Navigation hub
  * DEPLOYMENT_SUMMARY.md - Executive summary
- Add deployment scripts and automation
- Add DEPLOYMENT_PLAN.md - Concrete plan for immediate deployment
- Update README with production-ready features

All production infrastructure is now complete and ready for deployment.
This commit is contained in:
2025-10-25 19:18:37 +02:00
parent caa85db796
commit fc3d7e6357
83016 changed files with 378904 additions and 20919 deletions

View File

@@ -0,0 +1,195 @@
<?php
declare(strict_types=1);
namespace App\Framework\Idempotency;
use App\Framework\Cache\Cache;
use App\Framework\Cache\CacheItem;
use App\Framework\Cache\CacheKey;
use App\Framework\Core\ValueObjects\Duration;
use App\Framework\Core\ValueObjects\IdempotencyKey;
/**
* Generic Idempotency Service
*
* Ensures that duplicate requests with the same idempotency key
* return the same cached result without re-executing the operation.
*
* Use Cases:
* - Preventing duplicate form submissions
* - Handling network retries safely
* - Ensuring at-most-once execution of critical actions
* - Payment processing deduplication
* - Queue job deduplication
* - Webhook deduplication
*
* Flow:
* 1. Check if idempotency key exists in cache
* 2. If exists: Return cached result
* 3. If not: Execute operation, cache result, return
*
* Usage:
* ```php
* $result = $service->execute(
* key: $idempotencyKey,
* operation: fn() => $this->processPayment($amount),
* ttl: Duration::fromMinutes(10)
* );
* ```
*/
final readonly class IdempotencyService
{
public function __construct(
private Cache $cache
) {
}
/**
* Execute operation with idempotency protection
*
* @template T
* @param IdempotencyKey $key Idempotency key for this operation
* @param callable(): T $operation Operation to execute
* @param Duration $ttl How long to cache the result
* @return T Cached or fresh result
*/
public function execute(
IdempotencyKey $key,
callable $operation,
Duration $ttl
): mixed {
$cacheKey = CacheKey::fromString($key->toStorageKey());
// Check if we have a cached result
$cached = $this->cache->get($cacheKey);
if ($cached !== null) {
// Return cached result (idempotent - same key, same result)
return $cached->value;
}
// Execute operation
$result = $operation();
// Cache the result
$cacheItem = CacheItem::forSetting(
key: $cacheKey,
value: $result,
ttl: $ttl
);
$this->cache->set($cacheItem);
return $result;
}
/**
* Check if idempotency key has been processed
*
* @return bool True if key exists in cache (operation already executed)
*/
public function hasBeenProcessed(IdempotencyKey $key): bool
{
$cacheKey = CacheKey::fromString($key->toStorageKey());
return $this->cache->has($cacheKey);
}
/**
* Get cached result for idempotency key
*
* @template T
* @return T|null Cached result or null if not found
*/
public function getCachedResult(IdempotencyKey $key): mixed
{
$cacheKey = CacheKey::fromString($key->toStorageKey());
$cached = $this->cache->get($cacheKey);
return $cached?->value;
}
/**
* Store result for idempotency key
*
* Useful for pre-caching results or storing results from external operations.
*
* @param IdempotencyKey $key Idempotency key
* @param mixed $result Result to cache
* @param Duration $ttl Cache TTL
*/
public function storeResult(
IdempotencyKey $key,
mixed $result,
Duration $ttl
): void {
$cacheKey = CacheKey::fromString($key->toStorageKey());
$cacheItem = CacheItem::forSetting(
key: $cacheKey,
value: $result,
ttl: $ttl
);
$this->cache->set($cacheItem);
}
/**
* Invalidate idempotency key
*
* Useful for testing or admin operations.
*/
public function invalidate(IdempotencyKey $key): void
{
$cacheKey = CacheKey::fromString($key->toStorageKey());
$this->cache->forget($cacheKey);
}
/**
* Execute with automatic retry handling
*
* If operation fails, returns cached result if available.
* If no cached result and operation fails, throws exception.
*
* @template T
* @param IdempotencyKey $key Idempotency key
* @param callable(): T $operation Operation to execute
* @param Duration $ttl Cache TTL
* @return T Result
* @throws \Throwable If operation fails and no cached result exists
*/
public function executeWithRetry(
IdempotencyKey $key,
callable $operation,
Duration $ttl
): mixed {
// Check cache first
$cached = $this->getCachedResult($key);
if ($cached !== null) {
return $cached;
}
try {
// Execute operation
$result = $operation();
// Cache result
$this->storeResult($key, $result, $ttl);
return $result;
} catch (\Throwable $e) {
// Operation failed - check if we have stale cache
$cached = $this->getCachedResult($key);
if ($cached !== null) {
// Return stale cache instead of failing
return $cached;
}
// No cached result - re-throw exception
throw $e;
}
}
}

View File

@@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
namespace App\Framework\Idempotency;
use App\Framework\Cache\Cache;
use App\Framework\DI\Attributes\Initializer;
use App\Framework\DI\Container;
/**
* Idempotency Service Initializer
*
* Registers IdempotencyService as singleton in DI container.
*/
final readonly class IdempotencyServiceInitializer
{
#[Initializer]
public function __invoke(Container $container): IdempotencyService
{
$cache = $container->get(Cache::class);
return new IdempotencyService($cache);
}
}