Failures
Having an overview of errors, let's discuss the meaning of failures.
What is a failure?
Errors prevent the achievement of the service goal due to unsatisfied validations or exceptions.
At the same time, failures prevent the achievement of the service goal due to business reasons.
That is why is so vital to use meaningful names for services.
For instance, when the aim is to order a bus ticket, call it - OrderBusTicket
.
So when the service is invoked, but the ticket is not ordered for some logical business reason, it should be treated as a failure.
Why?
Business reason is not an error, since the input is valid, the environment is stable, but the service goal is still not achieved.
Please, read the following code snippet carefully to get the idea.
class OrderBusTicket
# ...
def result
return error("Bus is nil") if bus.nil?
return error("Bus `#{bus.number}` is NOT valid") unless bus.has_valid_number?
return failure("Bus has no available seats") if bus.full?
return failure("Bus trip is postponed") if Trip.for_bus(bus).postponed?
ticket = BusTerminal.issue_ticket(bus)
success(ticket: ticket)
rescue => exception
error("Ticket order for bus `#{name}` is not completed due to `#{exception.message}`")
end
end
And this is how it can be triggerred:
result = OrderBusTicket.result(bus: bus)
if result.failure?
# `result.message` is a string-like object with failure reason.
result.message
end
When the bus is nil
we can not even try to order a ticket.
When the bus does not have a valid number, we can not even try to order a ticket.
When an exception is raised, we can not complete the ticket order due to the technical issue.
Hence errors are returned for those cases.
But once all the validation preconditions are met and exceptions are avoided, the ticket can still not be ordered for business reasons.
This is the place when failures come into play.
The lack of available seats or the trip delay are examples of business reasons.
Just accept the fact that at the given moment in time, there is no possibility to order a ticket for that specific bus.
But the crucial point is that a failure is a strong and reliable explainer of why the desired effect hasn't happened.
That is the key difference between errors and failures.
Errors give you no service goal resolution at all.
While failures provide a stable negative service goal resolution that is a strong foundation for the subsequent decision-making.