Errors in Go
Go methods can return multiple values. One of these values can be an error. The zero value of an error is nil. After the call of each method, you check the error value with nil. If it is not equal to nil, then an error has occurred.
How to Implement a Rollback Function?
A function can call multiple methods in it. When an error occurs in one of the methods, you will need to reverse the methods called before the error. Reverting the output of the methods is done by implementing a rollback function and calling it every time an error occurs.
The concept of the rollback function is reverse-engineering. First, extract information from the called methods. This information is the success status of each method. The state may be a boolean variable with a value equal to true when the method succeeds and false when it fails; it also may be a string with the result of the method.
Then, collect the information and abstract it into a model. Finally, pass this model to the rollback function and review each status; if it is a success status, reverse the method and if it is a failure status, ignore it.
In this way, there are no side effects to the function when an error occurs. Moreover, the program will be maintainable.
Example
//Modeling
type CreateHotelStatusModel struct {
HotelId string `json:"hotelId"`
FloorId string `json:"floorId"`
RoomId string `json:"roomId"`
IsFirstFloorCreated bool `json:"isFirstFloorCreated"`
IsFirstRoomCreated bool `json:"isFirstRoomCreated"`
}
func CreateHotel(hotelName string) error {
var hotelStatusModel CreateHotelStatusModel
//create hotel DB record
hotelId, createHotelDBErr := CreateHotelDBRecord(hotelName)
//Information extraction
hotelStatusModel.HotelId = hotelId
if createHotelDBErr != nil {
createHotelRollback(&hotelStatusModel)
return createHotelDBErr
}
//create first floor
floorId, isFloorCreated, createFloorErr := CreateFirstFloor(hotelId)
hotelStatusModel.FloorId = floorId
hotelStatusModel.IsFirstFloorCreated = isFloorCreated
if createFloorErr != nil {
createHotelRollback(&hotelStatusModel)
return createFloorErr
}
//create first room
roomId, isRoomCreated, createRoomErr := CreateFirstRoom(floorId)
hotelStatusModel.RoomId = roomId
hotelStatusModel.IsFirstRoomCreated = isRoomCreated
if createRoomErr != nil {
createHotelRollback(&hotelStatusModel)
return createRoomErr
}
DispatchEvent("success","topic")
return nil
}
//Review
func createHotelRollback(statusModel *CreateHotelStatusModel) {
if statusModel.HotelId != "" {
DeleteHotelDBRecord(statusModel.HotelId)
}
if statusModel.IsFirstFloorCreated {
DeleteFloor(statusModel.FloorId)
}
if statusModel.IsFirstRoomCreated {
DeleteRoom(statusModel.RoomId)
}
DispatchEvent("failure","topic")
}
Top comments (2)
Why not simply add everything on a database transaction?? and dispatch the event only if it fails that transaction.
That can be a solution if you are only integrating with database, however, if you are integrating with third parties services, you will need to reverse each.