Привет всем, я Букай Чен~
Рост микросервисов и необходимость масштабируемости, гибкости и удобства обслуживания в современной архитектуре программного обеспечения побудили разработчиков использовать различные шаблоны проектирования.
Одним из шаблонов, который в последние годы привлек к себе большое внимание, является шаблон разделения ответственности за запросы команд (CQRS). CQRS особенно подходит для систем, где существует четкое различие между командами (изменение состояния) и запросами (состояние чтения). В этой статье мы углубимся в CQRS и увидим, как его реализовать с помощью микросервисов Spring.
Узнайте о CQRS
Разделение ответственности за запросы команд (CQRS) — это архитектурный шаблон, который предлагает отделить операции изменения данных (команды) от операций извлечения данных (запросов). Такое разделение позволяет разрабатывать специализированные модели запроса и обновления данных, тем самым повышая ясность и масштабируемость приложений.
Основная цель CQRS — упростить задачи, гарантируя, что каждая задача отвечает за одну операцию (команду или запрос, но никогда за обе).
CQRS не является совершенно новой концепцией. Его корни уходят в CQS (разделение командных запросов), принцип, популяризированный Бертраном Мейером, создателем языка программирования Eiffel. В то время как CQS в первую очередь касается методов, заявляя, что методы должны выполнять команды или отвечать на запросы, CQRS распространяет этот принцип на архитектурный уровень приложения, предполагая, что команды и запросы обрабатываются различными архитектурными компонентами.
Рост архитектуры микросервисов усилил потребность в CQRS. В распределенных системах, где службы часто должны быть автономными и сильно развязанными, CQRS обеспечивает четкий путь. Подпишитесь на общедоступный аккаунт: Колонка технологий Ма Юаня, отвечайте, используя ключевые слова: BAT, и получайте реальные вопросы для интервью от крупных компаний! Каждый микросервис может использовать шаблон CQRS, гарантируя, что его внутренние механизмы обработки команд и запросов будут абстрагированы от других сервисов. Это также хорошо согласуется с доменно-ориентированным дизайном (DDD), где события домена могут запускать команды в различных микросервисах.
Хотя CQRS предлагает множество преимуществ, он также сталкивается с проблемами:
Экосистема Spring имеет богатый набор инструментов и платформ, которые идеально подходят для реализации шаблона CQRS в среде микросервисов.
Первым шагом является настройка базового проекта Spring Boot. Если вы новичок в Spring Boot, вы можете легко инициализировать свой проект с помощью Spring Initializr. Базовые зависимости включают Spring Web, Spring Data JPA и любой коннектор базы данных, который вам нравится.
В системе CQRS на основе Spring команды представляют собой намерение изменить какое-то состояние, а обработчики команд обрабатывают эти команды.
Пример команды:
public class CreateUserCommand {
private final String userId;
private final String username;
// Constructor, getters, and other methods...
}
Для каждой команды определен соответствующий обработчик команд. Этот обработчик содержит реальную логику обработки команды:
@Service
public class CreateUserCommandHandler implements CommandHandler<CreateUserCommand> {
@Autowired
private UserRepository userRepository;
@Override
public void handle(CreateUserCommand command) {
User user = new User(command.getUserId(), command.getUsername());
userRepository.save(user);
}
}
В контексте доменно-ориентированного проектирования (DDD) изменения состояния обычно происходят в агрегатах. Эти агрегаты гарантируют, что все правила домена будут соблюдены, прежде чем любые изменения будут сохранены.
Аналогично, запросы представляют собой запросы на чтение некоторого состояния, а обработчики запросов обрабатывают эти запросы.
Пример запроса:
public class GetUserByIdQuery {
private final String userId;
// Constructor, getters, and other methods...
}
Соответствующий обработчик запроса:
@Service
public class GetUserByIdQueryHandler implements QueryHandler<GetUserByIdQuery, User> {
@Autowired
private UserRepository userRepository;
@Override
public User handle(GetUserByIdQuery query) {
return userRepository.findById(query.getUserId()).orElse(null);
}
}
Хотя CQRS предоставляет механизм изоляции, источник событий можно использовать для упрощения поддержания состояния между командами и запросами. Фреймворк Axon — это популярный фреймворк, который помогает реализовать CQRS и источник событий с помощью Spring.
В случае Axon события публикуются после обработки команды. Эти события можно сохранить, а затем использовать для воссоздания состояния агрегата. Это также помогает синхронизировать сторону запроса со стороной команды.
Учитывая распределенный характер микросервисов, часто бывает полезно реализовать асинхронную связь между сервисами. Apache Kafka можно интегрировать в экосистему Spring, чтобы создать мощную событийно-ориентированную архитектуру, которая особенно полезна при настройке CQRS.
События, генерируемые командной стороной, могут быть отправлены в тему Kafka, а сторона запроса может использовать эти события для обновления своего собственного хранилища данных. Это обеспечивает разделение между командной стороной и стороной запроса, что делает систему более отказоустойчивой и масштабируемой.
В то время как CQRS фокусируется на разделении обязанностей по командам и запросам, источник событий гарантирует, что каждое изменение состояния приложения фиксируется в объекте события и сохраняется в том порядке, в котором они применяются к одному и тому же агрегату. Этот метод позволяет восстанавливать прошлые состояния и особенно полезен в сочетании с CQRS.
Источник событий — это сохранение событий предметной области, а не самого состояния. Эти события фиксируют переходы между состояниями. Воспроизведя их, можно восстановить текущее состояние агрегата.
Например, вместо хранения текущего баланса банковского счета вы можете хранить все транзакции (такие как депозиты и снятие средств). Текущий баланс получается путем воспроизведения этих событий.
CQRS и источник событий дополняют друг друга следующим образом:
Как упоминалось ранее, платформа Axon обеспечивает простой способ реализации CQRS и источников событий в приложениях Spring:
Агрегации и обработка событий. В Axon агрегаты отвечают за обработку команд и генерацию событий. После обработки команд они применяют события, вызывающие изменения состояния.
@Aggregate
public class Account {
@AggregateIdentifier
private String accountId;
private int balance;
@CommandHandler
public void handle(WithdrawMoneyCommand cmd) {
if (cmd.getAmount() > balance) {
throw new InsufficientFundsException();
}
apply(new MoneyWithdrawnEvent(cmd.getAccountId(), cmd.getAmount()));
}
@EventSourcingHandler
public void on(MoneyWithdrawnEvent evt) {
this.balance -= evt.getAmount();
}
}
Хранение событий: Axon предоставляет механизм хранения и извлечения событий. Эти события можно воспроизвести, чтобы восстановить состояние агрегата.
Проекции: Проекции в Axon обеспечивают сторону запросов CQRS. Они прослушивают события и обновляют оптимизированные для чтения представления. Таким образом, ваша модель запроса всегда обновляется с учетом последних изменений.
Хотя CQRS и источник событий могут дать огромные преимущества, они также сопряжены со сложностями. Осознание этих проблем обеспечит более разумную и плавную реализацию.
Архитектурная сложность: CQRS и источники событий добавляют в систему дополнительные уровни и компоненты, такие как хранилища событий, шины команд и событий, а также механизмы синхронизации.
Кривая обучения: для команд, впервые знакомых с этими режимами, предусмотрен этап обучения. Концептуальный переход от традиционных систем на основе CRUD может оказаться сложной задачей.
Окончательная согласованность. Учитывая изолированный характер моделей команд и запросов, немедленной согласованностью часто жертвуют ради окончательной согласованности. Это означает, что может пройти задержка, прежде чем изменения, сделанные на стороне команды, отразятся на стороне запроса.
Порядок событий. Обеспечить обработку событий в том порядке, в котором они были созданы, особенно в распределенных системах, может быть непросто, но это имеет решающее значение для поддержания согласованного состояния.
Со временем структура или семантика событий могут измениться, что приводит к следующим проблемам:
Рекомендации по хранению. Поскольку все события сохраняются, хранилище событий может быстро расти, что приводит к увеличению затрат на хранение и потенциальным проблемам с производительностью.
Продолжительность воспроизведения. Восстановление состояния системы путем воспроизведения долгосрочных исторических событий может занять много времени, что повлияет на время восстановления и инициализации системы.
Интеграция систем, использующих CQRS и источники событий, с внешними системами, которые не следуют этим шаблонам, может оказаться сложной задачей, особенно когда речь идет о синхронизации данных и управлении транзакциями.
Решение о степени детализации. Решение о степени детализации применения CQRS и источников событий имеет решающее значение. Их реализация на микроуровне может привести к чрезмерному усложнению, тогда как слишком широкое внедрение может уменьшить выгоды.
Сложность предметной области. Эти шаблоны могут быть излишними для простых предметных областей. Они больше подходят для сложных областей, и их преимущества перевешивают затраты на внедрение и обслуживание.
Хотя существуют такие инструменты, как Axon, и платформы, поддерживающие CQRS и источники событий, они не всегда подходят для каждого сценария. Может потребоваться индивидуальная реализация, что увеличивает сложность и продолжительность проекта.
CQRS предоставляет уникальный способ масштабирования и организации микросервисов. В сочетании с экосистемой Spring он предоставляет мощный набор инструментов для создания надежных, масштабируемых и удобных в обслуживании систем. Однако, как и в случае со всеми архитектурными решениями, вам необходимо взвесить все «за» и «против» и убедиться, что оно подходит для вашего конкретного случая использования.