Is there an alternative to the Notification pattern for multiple messages and success/failure?
I have a class, OperationResult, that I use to return a Success
boolean and a list of "error" messages. These messages are sometimes unexpected errors but more often ordinary cases that often happen. Sometimes we return single error messages but other times we return several. I'm hoping to find a better way.
This seems to be more or less the Notification pattern advocated by Fowler. The consumers then do something reasonable with the success state and the errors, most often displaying errors to the user but sometimes continuing on in the case of non-fatal errors.
I thus have lots of service methods (not web service methods) that look something like this:
private ThingRepository _repository;
public OperationResult Update(MyThing thing)
{
var result = new OperationResult() { Success = true };
if (thing.Id == null) {
result.AddError("Could not find ID of the thing update.");
return result;
}
OtherThing original = _repository.GetOtherThing(thing.Id);
if (original == null) return result;
if (AnyPropertyDiffers(thing, original))
{
result.Merge(UpdateThing(thing, original));
}
if (result.Success) result.Merge(UpdateThingChildren(thing));
if (!result.HasChanges) return result;
OperationResult recalcResult = _repository.Recalculate(thing);
if (recalcResult.Success) return result;
result.AddErrors(recalcResult.Errors);
return result;
}
private OperationResult UpdateThing(MyThing ) {...}
private OperationResult UpdateThingChildren(MyThing) {...}
private bool AnyPropertyDiffers(MyThing, OtherThing) {...}
As you can imagine, UpdateThing
, UpdateThingChildren
, and ThingRepository.Recalculate
all have similar OperationResult
merging/manipulating code interleaved with their business logic.
Is there an alternative to so much code munging around my returned object? I'd like my code to just focus on the business logic without having to be so particular about manipulating an OperationResult
.
I'm hoping to instead have code that looks something like the following, something that better expresses its business logic with less message-handling cruft:
public ??? Update(MyThing thing, ???)
{
if (thing.Id == null) return ???;
OtherThing original = _repository.GetOtherThing(thing.originalId);
if (original == null) return ???;
if (AnyPropertyDiffers(thing, original))
{
UpdateThing(thing, original));
}
UpdateThingChildren(thing);
_repository.Recalculate(thing);
return ???;
}
Any ideas?
Note: throwing exceptions isn't really appropriate here as the messages aren't exceptional.
I would argue that your service is not doing one thing. Its responsible for validating input and then if validation succeeds updating things. And yes I agree that user needs as many information about errors (violations, name not provided, description to long, date end before date start etc) as you can produce on single request and with that the exceptions are not the way to go.
In my projects I tend to separate concerns of validation and update so the service that does the update has little to none chance of failure. Also I like the strategy pattern to do both validation and update - user requests a change the generic service accepts request of validation/update, calls specific validator/updater which in turn calls generic service to validate/update some dependencies. The generic service will merge results and decide upon success or failure of the operation. The obvious benefit is that violation messages merging is done once in some generic class and specific validator/updater can focus on single entity. On the other hand you might want to validate some database uniqueness or existence of objects on the database this exposes two concerns: extra queries to database (light queries that use Exist
to minimize output but its a trip to the database) and the latency between validation and update (in that time database can change and your uniqueness or existence verification can change (this time is relatively small but it can happen). This pattern also minimizes duplication of UpdateThingChildren
- when you have simple many to many relation the child can be updated from either one of connected entities.