CQRS i Event Sourcing w PHP
Command Query Responsibility Segregation (CQRS) oraz Event Sourcing to wzorce architektoniczne, które umożliwiają efektywne zarządzanie danymi i zwiększają skalowalność aplikacji. W tym artykule przedstawimy ich podstawowe założenia oraz sposób implementacji w PHP.
CQRS – Podział odpowiedzialności
CQRS polega na rozdzieleniu operacji odczytu (Query) i zapisu (Command). Dzięki temu można niezależnie skalować obie części oraz stosować inne modele danych dla odczytu i zapisu.
Przykład CQRS w PHP
Komendy (Commands) i ich obsługa:
class CreateUserCommand {
public function __construct(
public string $userId,
public string $name,
public string $email
) {}
}
class CreateUserHandler {
public function handle(CreateUserCommand $command) {
// Tutaj zapisujemy dane użytkownika
echo "Użytkownik {$command->name} został utworzony.";
}
}
Zapytania (Queries) i ich obsługa:
class GetUserQuery {
public function __construct(public string $userId) {}
}
class GetUserHandler {
private array $users = [
'1' => ['name' => 'Jan Kowalski', 'email' => 'jan@example.com']
];
public function handle(GetUserQuery $query) {
return $this->users[$query->userId] ?? null;
}
}
Event Sourcing – Śledzenie zmian w systemie
Event Sourcing to technika, w której zamiast zapisywać aktualny stan obiektu, przechowujemy sekwencję zdarzeń, które doprowadziły do tego stanu.
Przykład Event Sourcing w PHP
Definicja zdarzeń:
interface Event {}
class UserCreatedEvent implements Event {
public function __construct(
public string $userId,
public string $name,
public string $email
) {}
}
Rejestrowanie zdarzeń:
class EventStore {
private array $events = [];
public function append(Event $event) {
$this->events[] = $event;
}
public function getEvents(): array {
return $this->events;
}
}
Odtwarzanie stanu obiektu z historii zdarzeń:
class User {
public function __construct(
private string $userId,
private string $name,
private string $email
) {}
public static function reconstitute(array $events): ?self {
$user = null;
foreach ($events as $event) {
if ($event instanceof UserCreatedEvent) {
$user = new self($event->userId, $event->name, $event->email);
}
}
return $user;
}
}
Najprostsza implementacja Event Sourcingu w PHP
$events = [
'init' => function($a, &$r) {$r = $a;},
'add' => function($a, &$r) {$r += $a;},
'subtract' => function($a, &$r) {$r -= $a;},
];
$store = [
['init', 0],
['add', 1],
['add', 2],
['subtract', 5],
//...
];
file_put_contents('store.json', json_encode($store));
$store = json_decode(file_get_contents('store.json'), true);
$result = null;
foreach ($store as $array)
{
$name = $array[0];
$value = $array[1];
$callback = $events[$name];
$params = [$value, &$result];
call_user_func_array($callback, $params);
}
var_dump($result);
Zalety i wady CQRS oraz Event Sourcing
Zalety:
- Skalowalność – Możliwość osobnego skalowania operacji zapisu i odczytu.
- Lepsza organizacja kodu – Jasny podział na odpowiedzialności.
- Historia zmian – Event Sourcing pozwala na dokładne śledzenie zmian w systemie.
- Łatwiejsza obsługa złożonych domen biznesowych – Szczególnie w aplikacjach wymagających wersjonowania danych.
Wady:
- Złożoność – Wprowadzenie CQRS i Event Sourcingu wymaga dodatkowego kodu i narzędzi do obsługi zdarzeń.
- Koszty przechowywania danych – Event Sourcing wymaga przechowywania pełnej historii zmian.
- Trudniejsza nauka i wdrożenie – Wymaga zmiany sposobu myślenia o danych w systemie.
Kiedy stosować CQRS i Event Sourcing?
Kiedy są wskazane?
- W systemach o wysokiej skalowalności, gdzie odczyt i zapis wymagają niezależnej optymalizacji.
- Gdy potrzebne jest pełne śledzenie zmian w systemie (np. aplikacje finansowe, systemy audytowe).
- W złożonych domenach biznesowych, gdzie logika aplikacji jest trudna do odwzorowania w klasycznych modelach relacyjnych.
Kiedy mogą prowadzić do overengineeringu?
- W prostych aplikacjach CRUD, gdzie klasyczna baza danych wystarcza do obsługi operacji.
- Gdy złożoność systemu nie uzasadnia dodatkowej warstwy abstrakcji.
- Jeśli głównym celem jest szybkie dostarczenie rozwiązania, a nie długoterminowa skalowalność.
Podsumowanie
CQRS i Event Sourcing to potężne wzorce architektoniczne, które pomagają w budowaniu skalowalnych i wydajnych systemów. Dzięki CQRS możemy oddzielić operacje odczytu od zapisu, a Event Sourcing umożliwia pełne śledzenie zmian w systemie. W PHP można je zaimplementować za pomocą prostych klas i interfejsów, jak pokazano w powyższych przykładach. Należy jednak rozważyć ich zastosowanie, aby uniknąć nadmiernej komplikacji w projektach, które ich nie wymagają.