Как лучше всего убедиться, что команда CreateCar не работает, если автомобиль уже существует? Естественно, я мог бы сначала проверить хранилище, но это не помешает условиям гонки ...
Магии нет.
Если вы собираетесь избежать колоссальных записей, вам нужно либо получить блокировку хранилища данных, либо хранилище данных с семантикой compare and swap
.
С блокировкой у вас есть гарантия, что между вашим чтением данных в хранилище и вашей последующей записью не произойдет конфликтующих обновлений.
lock = lock_for_id id
lock.acquire
Try:
Option[Car] root = repository.load id
switch root {
case None:
Car car = createCar ...
repository.store car
case Some(car):
// deal with the fact that the car has already been created
}
Finally:
lock.release
Вы хотели бы иметь блокировку для каждого агрегата, но создание блокировок имеет те же условия, что и создание агрегатов. Таким образом, вы, скорее всего, получите что-то вроде грубозернистого замка для ограничения доступа к операции.
С помощью функции сравнения и обмена вы подталкиваете управление конфликтами к хранилищу данных. Вместо отправки в магазин PUT , вы отправляете условный PUT .
Option[Car] root = repository.load id
switch root {
case None:
Car car = createCar ...
repository.replace car None
case Some(car):
// deal with the fact that the car has already been created
}
Нам больше не нужны блокировки, потому что мы точно описываем для магазина предварительное условие (например, If-None-Match: *), которое должно быть удовлетворено.
Семантика сравнения и обмена обычно поддерживается хранилищами событий; «добавление» новых событий в поток выполняется путем создания запроса, который идентифицирует ожидаемую позицию указателя хвоста, со специально закодированными значениями для определения случаев, когда ожидается создание потока (например, Event Store поддерживает ExpectedVersion.NoStream семантическая).