As an example, suppose I have the following class:
public class FruitBasket {
private List<Apple> apples;
private List<Orange> oranges;
// getters and setters...
}
Now suppose I also have a method somewhere which gets the FruitBasket
.
public FruitBasket getFruitBasket() {
//...
}
Now further suppose that the getFruitBasket
method aggregates data from two different sources, which are accessed via a proxy. For example, there is a server AppleTree
to get objects of type Apple
, and a server OrangeTree
to get objects of type Orange
, and both are accessed via a proxy called OrchardGate
. This is the reason why I want to write a single getFruitBasket
method rather than getApples
and getOranges
, to minimise the latency when calling from my application to the OrchardGate
.
In the case where Apple
objects and Orange
objects were retrieved successfully, there is no problem, I can just return the FruitBasket
. In the case where there was a problem accessing or inside the OrchardGate
, or in both the AppleTree
and OrangeTree
, I can also handle this by throwing a descendant of RuntimeException
(or even Exception
, if I add it to the getFruitBasket
throws
clause as appropriate).
However, what happens in the partial success case? What happens if I can access the AppleTree
server fine, but I can't access the OrangeTree
server due to some transport issue between the OrchardGate
and OrangeTree
?
As far as I can see there are only four options, and all are absolutely horrible:
- I could throw an exception, meaning that even though the
Apple
objects were received successfully, theFruitBasket
would not be returned due to the lack ofOrange
objects. - I could ignore the error and just return an empty list of
Orange
objects. This would mean that the client would not be able to see an error when looking for theOrange
objects, instead it would just look as if noOrange
objects existed on theOrangeTree
server. - I could add a field to
FruitBasket
callederrorCodes
which contained a list of the errors encountered when accessing theFruitBasket
. Then I could add an error code to that list calledPATH_FLOODED
to signify the error I had encountered. However, this fielderrorCodes
does not belong on theFruitBasket
domain object at all. This field is not relevant to aFruitBasket
, but only in the transaction to retrieve theFruitBasket
. - I could throw an exception, but attach the incomplete
FruitBasket
to the exception. This is also horrible because exceptions should only contain error information - they should not contain any fruit baskets.
I have described this problem in Java but I imagine this problem extends to multiple languages. I was quite surprised to not find a discussion of it already. Is there any standard pattern for writing methods which can return a partial success? Or anything I have missed?
abstract Class
like Fruit and use aList<Fruit> fruits
within your FruitBasket . Then createclass Apple
andclass Orange
which areextends Fruit
so that even if you put Orange and Apple in one List you are able to differentiate both usinginstance of
operation. – DefalcateApple
andOrange
is because of the English phrase "it's apples and oranges" meaning it's two completely different types of thing. However, even if you were able to do this, it wouldn't solve the problem, because the problem is how to represent a partial success case! – Mucoproteinboolean
and then if the accessing to the Servers was successful then set theboolean
value totrue
otherwise tofalse
and before you trying to access theFruitBasket
call thehasApples()
orhasOranges()
methods to make sure that these objects exists otherwise fire exceptions/errors – Defalcateboolean
value is set. Where is thisboolean
located? In the second, you callhasApples
orhasOranges
. The problem here is that each call would need to go over the remote connection to theOrchardGate
. This would defeat the purpose of having an aggregate method - in fact it would be slower than calling agetApples
andgetOranges
separately (three calls rather than two). – MucoproteinloadFruitBasket()
which loads theApple
andOrange
Objects from server. Because you have to access the servers, to get theapples
andorange
s you can set theboolean
right then to true when the accessing was successful or to false wehen it was a failure. Then before using thegetFruitBasket()
you can ask the Class which had loaded theFruitBasket
if ithasApples()
orhasOranges()
. with this you don't have to access the servers so many times and can display diffreent erros. – DefalcateFruitBasketLoader
class which retrievesFruitBasketData
from theOrchardGate
. The caller exchanges only domain objects with theFruitBasketLoader
, so it provides an abstraction of the messy interface in terms of the desired API......whereasFruitBasketData
does not have follow the domain model as it is abstracted away from the caller...... – MucoproteinOrangeSkin
andOrangeSegments
came from different servers. This is a change in the implementation but would also seemingly require us to refactor our API as we would have to replacehasOranges
withhasOrangeSkin
andhasOrangeSegments
in order to report errors correctly. This kind of thing is usually a sign of bad design....but it's too late at night for me to fully analyse right now! – Mucoprotein