Может ли микросервис иметь свою собственную базу данных и хранить в ней данные?
Определенно, микросервис может иметь свою собственную базу данных. Но давайте использовать термины из ES / CQRS. База данных может представлять Хранилище событий (журнал только для добавления событий immutabale) и Чтение модели - некоторую базу данных, используемую для ответа на запросы, которая заполняется прослеживающимися событиями.
Таким образом, микросервис может иметь собственную модель чтения, заполненную событиями других микросервисов.
Или микросервис может обрабатывать команды и сохранять события в общем хранилище событий.
Или микросервис может обрабатывать команды и сохранять события в собственном хранилище событий.
Выбор за вами, и он зависит от степени разделения, которую вы хотите достичь между микросервисами.
Я бы поместил все события, которые обычно потребляются вместе, в одно хранилище событий. Это означает, что я должен иметь возможность запрашивать эти события и иметь в результате один упорядоченный поток.
когда я выполняю команду в микросервисе и мне нужны данные для проверки, нужно ли мне вызывать ReadModel или как?
Команда выполняется Агрегатом, который имеет свое собственное состояние. Это состояние создается путем обработки всех событий для этого агрегата, и это состояние следует использовать для проверки команды.
Вы не можете / не должны общаться с моделями чтения в обработчике команд , главным образом потому, что эти модели чтения не согласуются с агрегатным состоянием. Совокупное состояние соответствует.
Вы можете запросить чтение модели перед отправкой команды (чтобы убедиться, что она может быть отправлена). Но в обработчике команд вам нужно полагаться только на агрегатное состояние.
Известен случай регистрации пользователя с требованием уникального имени. В качестве основной проверки, в вашем коде пользовательского интерфейса вы можете запросить модель чтения и сообщить пользователю, что введенное имя занято. Если имя не занято, пользовательский интерфейс позволяет пользователю выполнить команду. Я предполагаю, что ваш Совокупный корень - пользователь.
Но при обработке этой команды ({id:123, type:CREATE_USER, name:somename}
) вы не можете проверить, выбрано ли "somename", потому что агрегатное состояние для пользователя 123 не содержит списка принятых имен. Вы можете запросить модель чтения AllUsernames, но она может быть устаревшей за миллисекунды, и какой-то другой пользователь уже может принять это «имя». Таким образом, в этом сценарии вы найдете дублирование при добавлении имен для чтения модели. И в этот момент вы можете выполнить некоторые действия по компенсации - обычно введите команду, чтобы приостановить пользователя с дублированным именем и попросить его перерегистрировать или как-то изменить его имя.
Это может показаться странным, но если у вас действительно распределенная система с несколькими репликами списка пользователей, у вас возникнет та же проблема, так почему бы просто не принять тот факт, что данные всегда не полностью согласованы, а просто разобраться с это?