I'm working on a booking application and am in the process of porting some legacy code to a more SOLID and testable architecture. Right now I'm working on the process of canceling bookings. Over the years, we have changed the steps involved here. The core has been the same (update booking entity status, make the timeslot open again, etc), but we've been adding and removing some other steps, and we do some steps depending on where the cancellation is made from (support UI, customer UI etc).
My current solution involves the decorator pattern for dynamically adding extra steps to the cancellation process. In order to hide the concrete classes responsible for those steps, as well as the fact that this pattern is used at all from client code, I've wrapped this up in a builder. I now wonder if there are some pitfalls I should look out for here.
public class OrderCancellationProcessBuilder
{
private readonly ITimeProvider _timeProvider;
private IOrderProcessor _cancellationProcessor;
public OrderCancellationProcessBuilder(ITimeProvider timeProvider, string cancelationMessage)
{
_timeProvider = timeProvider;
_cancellationProcessor = new CancelBookingsAndRevokePayments(cancelationMessage);
}
public IOrderProcessor Build()
{
return _cancellationProcessor;
}
public OrderCancellationProcessBuilder SendConfirmationEmails(IConfirmationEmailSender emailSender)
{
_cancellationProcessor = new SendConfirmationEmails(
_cancellationProcessor,
emailSender,
_timeProvider);
return this;
}
public OrderCancellationProcessBuilder RemovePendingMessages(IPendingMessagesService pendingMessagesService)
{
_cancellationProcessor = new RemovePendingMessages(_cancellationProcessor, pendingMessagesService);
return this;
}
}
The order processor can later on be consumed by the client code by something like someOrder.Accept(cancellationProcessor);
. SendConfirmationEmails
and others are the decorators for the order processor. The decoration steps are being hidden behind the builder pattern in a 1:1 way (although this could of course change).
If I were to voice any concern I might have at this stage, it is that it might be overly complicated. But then again, I think this way of thinking might come partly from the fact that the current code LOOKS pretty simple until you look into things and realize how much global state and new
ing of dependencies there are. If this looks complicated, it might just be because it IS complicated, and this pattern just makes it more visible.
I really think something like this solution could work to make it a lot easier to add/remove steps without breaking existing code, but I wonder if there's a simpler way