Если вы работаете с одной базой данных которая поддерживает транзакции вы даже не задумываетесь о консистентности — база все делает за вас. Если же у вас несколько баз, распределенная система или даже к примеру MongoDB до 4 версии — все не так радужно.
Рассмотрим пример — мы хотим сохранить файл в хранилище и добавить ссылку на него в два документа. Конечно же мы хотим атомарности — либо файл сохранен и добавлен в документы либо ни то ни другое (тут и далее используется cats-effects IO):
saveDataToFile(data) // (1)
.flatMap { file =>
addFileRef(documentId, file) // (2)
.flatMap { result =>
addFileRef(fileRegistry, file) // (3)
.flatMap { result =>
??? // (4, 5, ...)
}
.handleErrorWith { error =>
// revert (2)
removeFileRef(documentId, file).attempt >> IO.raiseError(error)
}
}
.handleErrorWith { error =>
// revert (1)
removeFile(file).attempt >> IO.raiseError(error)
}
}
Уже непросто? Легко представить как количество операций растет и образуется Pyramid of doom.
Но мы же программисты! Давайте обобщим проблему и напишем код, который позволит избежать ненужной сложности и возможных ошибок.