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

@@ -33,6 +33,11 @@ final class CacheBasedStateManager implements StateManager
private Duration $totalGetTime;
/** @var array<StateTransformer> */
private array $transformers = [];
private bool $transformersSorted = false;
public function __construct(
private readonly Cache $cache,
private readonly string $keyPrefix,
@@ -45,6 +50,28 @@ final class CacheBasedStateManager implements StateManager
$this->totalGetTime = Duration::zero();
}
/**
* Add a transformer to the pipeline
*
* Transformers are executed in priority order (high to low) on write,
* and in reverse order on read.
*
* @param StateTransformer $transformer The transformer to add
* @return self For method chaining
*/
public function addTransformer(StateTransformer $transformer): self
{
$this->transformers[] = $transformer;
$this->transformersSorted = false;
$this->log('debug', 'Transformer added', [
'transformer' => $transformer->getName(),
'priority' => $transformer->getPriority()
]);
return $this;
}
public function getState(string $key): mixed
{
$startTime = Timestamp::now();
@@ -61,15 +88,23 @@ final class CacheBasedStateManager implements StateManager
}
$data = $cacheItem->value;
if (! is_array($data)) {
$this->missCount++;
$this->log('warning', "Invalid state data for key: {$key}");
return null;
// Apply transformOut pipeline (reverse order) if transformers are registered
if (! empty($this->transformers)) {
$state = $this->applyTransformOutPipeline($data);
} else {
// Legacy path: no transformers, expect array data
if (! is_array($data)) {
$this->missCount++;
$this->log('warning', "Invalid state data for key: {$key}");
return null;
}
/** @var T $state */
$state = call_user_func([$this->stateClass, 'fromArray'], $data);
}
/** @var T $state */
$state = call_user_func([$this->stateClass, 'fromArray'], $data);
$this->hitCount++;
$this->log('debug', "State hit for key: {$key}");
@@ -92,7 +127,15 @@ final class CacheBasedStateManager implements StateManager
$cacheKey = $this->getCacheKey($key);
$effectiveTtl = $ttl ?? $this->defaultTtl ?? Duration::fromHours(1);
$this->cache->set($cacheKey, $state->toArray(), $effectiveTtl);
// Apply transformIn pipeline if transformers are registered
if (! empty($this->transformers)) {
$data = $this->applyTransformInPipeline($state);
} else {
// Legacy path: no transformers, store as array
$data = $state->toArray();
}
$this->cache->set($cacheKey, $data, $effectiveTtl);
$this->setCount++;
$this->log('debug', "State set for key: {$key}", ['ttl' => $effectiveTtl->toSeconds()]);
} finally {
@@ -219,4 +262,82 @@ final class CacheBasedStateManager implements StateManager
default => $this->logger->info("[StateManager] {$message}", $context),
};
}
/**
* Apply transformIn pipeline to state before storage
*
* Transformers execute in priority order (high to low).
*
* @param SerializableState $state The state to transform
* @return mixed The transformed data (final transformer determines type)
*/
private function applyTransformInPipeline(SerializableState $state): mixed
{
$this->ensureTransformersSorted();
$data = $state;
foreach ($this->transformers as $transformer) {
$this->log('debug', "Applying transformIn: {$transformer->getName()}");
$data = $transformer->transformIn($data);
}
return $data;
}
/**
* Apply transformOut pipeline to data after retrieval
*
* Transformers execute in REVERSE priority order (low to high).
*
* @param mixed $data The stored data to transform
* @return SerializableState The restored state object
*/
private function applyTransformOutPipeline(mixed $data): SerializableState
{
$this->ensureTransformersSorted();
// Reverse order for transformOut
$transformers = array_reverse($this->transformers);
foreach ($transformers as $transformer) {
$this->log('debug', "Applying transformOut: {$transformer->getName()}");
$data = $transformer->transformOut($data);
}
if (! $data instanceof SerializableState) {
throw new \RuntimeException(
'Transform pipeline must return SerializableState. ' .
'Got: ' . get_debug_type($data)
);
}
return $data;
}
/**
* Ensure transformers are sorted by priority (high to low)
*
* Uses stable sort to preserve registration order for same priority.
*/
private function ensureTransformersSorted(): void
{
if ($this->transformersSorted) {
return;
}
usort($this->transformers, function (StateTransformer $a, StateTransformer $b): int {
return $b->getPriority() <=> $a->getPriority(); // High to low
});
$this->transformersSorted = true;
$this->log('debug', 'Transformers sorted by priority', [
'count' => count($this->transformers),
'order' => array_map(
fn(StateTransformer $t) => $t->getName() . ':' . $t->getPriority(),
$this->transformers
)
]);
}
}

View File

@@ -10,6 +10,7 @@ use App\Framework\Core\ValueObjects\Duration;
use App\Framework\ErrorBoundaries\CircuitBreaker\BoundaryCircuitBreakerState;
use App\Framework\Logging\Logger;
use App\Framework\RateLimit\TokenBucket;
use App\Application\LiveComponents\LiveComponentState;
/**
* Factory for creating state managers
@@ -95,4 +96,51 @@ final readonly class StateManagerFactory
namespace: $namespace,
);
}
/**
* Create state manager for LiveComponents
*
* Provides persistent, type-safe state storage for LiveComponent states.
* Each component instance gets its own StateManager scoped to that component type.
*
* @template T of LiveComponentState
* @param class-string<T> $stateClass The LiveComponent state class (e.g., ShoppingCartState::class)
* @param string $componentName Component identifier for key prefix (e.g., 'shopping-cart')
* @param Duration|null $defaultTtl Default TTL for state persistence (default: 1 hour)
* @param string $namespace Optional namespace for multi-tenancy or environment separation
* @return CacheBasedStateManager<T>
*
* @example
* ```php
* // Create StateManager for ShoppingCartState
* $stateManager = $factory->createForLiveComponents(
* stateClass: ShoppingCartState::class,
* componentName: 'shopping-cart',
* defaultTtl: Duration::fromHours(24) // Keep cart for 24 hours
* );
*
* // Store user's cart state
* $cartState = ShoppingCartState::fromArray($clientData);
* $stateManager->setState("user-{$userId}", $cartState);
*
* // Retrieve and update cart
* $updatedCart = $stateManager->updateState(
* "user-{$userId}",
* fn($cart) => $cart->withAddedItem($productId, $name, $price, $quantity)
* );
* ```
*/
public function createForLiveComponents(
string $stateClass,
string $componentName,
?Duration $defaultTtl = null,
string $namespace = 'livecomponents',
): CacheBasedStateManager {
return $this->createCacheBased(
keyPrefix: "livecomponent_{$componentName}",
stateClass: $stateClass,
defaultTtl: $defaultTtl ?? Duration::fromHours(1),
namespace: $namespace,
);
}
}

View File

@@ -0,0 +1,83 @@
<?php
declare(strict_types=1);
namespace App\Framework\StateManagement;
/**
* State Transformer Interface
*
* Transforms state data before storage and after retrieval.
* Enables composable transformation pipeline (encryption, compression, validation, etc.).
*
* Framework Principles:
* - Interface-driven design
* - Composable transformations (middleware pattern)
* - Type-safe with SerializableState
* - Bidirectional transformation (in/out)
*
* Transformation Flow:
* - transformIn: Before storage (State → transformed data)
* - transformOut: After retrieval (transformed data → State)
*
* Example Transformers:
* - EncryptionTransformer: Encrypts/decrypts state data
* - CompressionTransformer: Compresses/decompresses state data
* - ValidationTransformer: Validates state integrity
* - HashingTransformer: Adds integrity hashes
*
* Usage:
* ```php
* $stateManager->addTransformer(new EncryptionTransformer($encryptor));
* $stateManager->addTransformer(new CompressionTransformer());
* ```
*/
interface StateTransformer
{
/**
* Transform state before storage
*
* Called when storing state (setState, updateState).
* Transforms SerializableState into storable format (typically string).
*
* Transformation order when multiple transformers:
* State → Transformer1 → Transformer2 → Transformer3 → Storage
*
* @param SerializableState $state The state object to transform
* @return mixed The transformed data (typically string for final transformer)
* @throws \Throwable if transformation fails
*/
public function transformIn(SerializableState $state): mixed;
/**
* Transform data after retrieval
*
* Called when retrieving state (getState, updateState).
* Transforms stored data back into SerializableState object.
*
* Transformation order when multiple transformers (reverse order):
* Storage → Transformer3 → Transformer2 → Transformer1 → State
*
* @param mixed $data The stored data to transform back
* @return SerializableState The restored state object
* @throws \Throwable if transformation fails
*/
public function transformOut(mixed $data): SerializableState;
/**
* Get transformer name (for debugging/logging)
*/
public function getName(): string;
/**
* Get transformer priority (higher = runs first on transformIn)
*
* Priority determines execution order:
* - High priority (100): Validation transformers
* - Medium priority (50): Encryption transformers
* - Low priority (10): Compression transformers
*
* @return int Priority value (default: 50)
*/
public function getPriority(): int;
}

View File

@@ -0,0 +1,96 @@
<?php
declare(strict_types=1);
namespace App\Framework\StateManagement\Transformers;
use App\Framework\LiveComponents\Serialization\EncryptedStateSerializer;
use App\Framework\StateManagement\SerializableState;
use App\Framework\StateManagement\StateTransformer;
/**
* Encryption State Transformer
*
* Encrypts state data before storage and decrypts after retrieval.
* Uses authenticated encryption (AES-256-GCM via libsodium).
*
* Framework Principles:
* - Readonly class with immutable dependencies
* - Type-safe with SerializableState
* - Explicit error handling
* - No inheritance
*
* Security Features:
* - Authenticated encryption with MAC verification
* - Unique nonce per encryption operation
* - Tampering detection via MAC
* - Secure key management via framework
*
* Performance:
* - ~5-10ms overhead per operation
* - Negligible for most use cases
* - Priority: 50 (medium - runs after validation, before compression)
*
* @see EncryptedStateSerializer For encryption implementation
* @see StateTransformer For transformer interface
*/
final readonly class EncryptionTransformer implements StateTransformer
{
public function __construct(
private EncryptedStateSerializer $serializer,
private string $stateClass
) {
}
/**
* Encrypt state before storage
*
* @param SerializableState $state The state to encrypt
* @return string Base64-encoded encrypted state data
*/
public function transformIn(SerializableState $state): mixed
{
return $this->serializer->serialize($state);
}
/**
* Decrypt state after retrieval
*
* @param mixed $data Base64-encoded encrypted state data
* @return SerializableState The decrypted state object
*/
public function transformOut(mixed $data): SerializableState
{
if (! is_string($data)) {
throw new \InvalidArgumentException(
'EncryptionTransformer expects string data, got: ' . get_debug_type($data)
);
}
$state = $this->serializer->deserialize($data, $this->stateClass);
if (! $state instanceof SerializableState) {
throw new \RuntimeException(
'Deserialized state must implement SerializableState interface'
);
}
return $state;
}
/**
* Get transformer name
*/
public function getName(): string
{
return 'encryption';
}
/**
* Get priority (medium - runs after validation, before compression)
*/
public function getPriority(): int
{
return 50;
}
}