← Назад к вопросам

Какой самый сложный паттерн проектирования приходилось использовать?

1.0 Junior🔥 153 комментариев
#Архитектура и паттерны#Опыт и карьера

Комментарии (3)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Мой опыт работы со сложными паттернами проектирования

Если говорить о самом сложном паттерне, который приходилось применять в реальных проектах на PHP, то это определенно Event Sourcing (событийное проектирование) в сочетании с CQRS (Command Query Responsibility Segregation). Этот архитектурный подход требовал полного переосмысления того, как мы работаем с данными и бизнес-логикой.

Почему именно Event Sourcing + CQRS?

Сложность паттерна проявляется в нескольких аспектах:

  1. Концептуальная сложность — переход от классического CRUD к мышлению в терминах событий
  2. Инфраструктурная нагрузка — необходимость специализированных компонентов
  3. Сложность отладки — трассировка изменений через цепочку событий

Практический пример реализации

// Пример агрегата с Event Sourcing
class UserAggregate
{
    private $userId;
    private $email;
    private $events = [];
    private $version = 0;

    public static function create(string $userId, string $email): self
    {
        $instance = new self();
        $instance->recordThat(new UserCreated($userId, $email));
        return $instance;
    }

    public function changeEmail(string $newEmail): void
    {
        if (!filter_var($newEmail, FILTER_VALIDATE_EMAIL)) {
            throw new InvalidArgumentException('Invalid email');
        }
        
        $this->recordThat(new EmailChanged($this->userId, $newEmail));
    }

    private function recordThat(DomainEvent $event): void
    {
        $this->events[] = $event;
        $this->apply($event);
        $this->version++;
    }

    private function apply(DomainEvent $event): void
    {
        switch (get_class($event)) {
            case UserCreated::class:
                $this->userId = $event->getUserId();
                $this->email = $event->getEmail();
                break;
            case EmailChanged::class:
                $this->email = $event->getNewEmail();
                break;
        }
    }

    public function getUncommittedEvents(): array
    {
        return $this->events;
    }
}

Реальные сложности, с которыми столкнулся

1. Проектирование событий

// Каждое событие должно быть неизменяемым и сериализуемым
class EmailChanged implements DomainEvent
{
    private $userId;
    private $newEmail;
    private $occurredOn;

    public function __construct(string $userId, string $newEmail)
    {
        $this->userId = $userId;
        $this->newEmail = $newEmail;
        $this->occurredOn = new DateTimeImmutable();
    }
    
    // Все свойства только для чтения
}

2. Разделение команд и запросов

  • Command Handler обрабатывает изменение состояния
  • Query Handler только читает данные из оптимизированных проекций
// Command Handler
class ChangeEmailHandler
{
    private $eventStore;
    
    public function handle(ChangeEmailCommand $command): void
    {
        $events = $this->eventStore->load($command->getUserId());
        $user = UserAggregate::reconstituteFromHistory($events);
        
        $user->changeEmail($command->getNewEmail());
        
        $this->eventStore->append(
            $command->getUserId(),
            $user->getUncommittedEvents(),
            $user->getVersion()
        );
    }
}

3. Создание проекций (Projections)

// Проекция для отображения пользователей
class UserProjection implements Projection
{
    private $connection;
    
    public function applyEmailChanged(EmailChanged $event): void
    {
        $this->connection->update('user_read_models', [
            'email' => $event->getNewEmail(),
            'updated_at' => $event->getOccurredOn()
        ], ['user_id' => $event->getUserId()]);
    }
}

Преимущества, которые оправдали сложность

  1. Полный аудит — история всех изменений доступна по умолчанию
  2. Временные запросы — возможность восстановить состояние на любую дату
  3. Масштабируемость — чтение и запись можно масштабировать независимо
  4. Гибкость — новые проекции добавляются без изменения основной логики

Выводы и рекомендации

Event Sourcing + CQRS — мощный, но сложный паттерн, который не стоит применять везде. Он идеально подходит для:

  • Финансовых систем, где важен полный аудит
  • Сложных бизнес-процессов с множеством состояний
  • Систем, где важна временная аналитика

Для простых CRUD-приложений эта сложность избыточна. Ключевой урок: выбирать архитектурные паттерны нужно, исходя из реальных требований бизнеса, а не из желания использовать "крутые" технологии. Паттерн доказал свою ценность в проектах с высокой сложностью доменной логики, но требует значительных инвестиций в обучение команды и поддержку инфраструктуры.

Какой самый сложный паттерн проектирования приходилось использовать? | PrepBro