To simplify the domain let's say we have a system that takes in orders from customers. There's an API endpoint that validates the input, and if valid pushes the order onto a messaging queue to be picked up by an event handler to process the orders. So a simple (.NET) API controller might look like this:
[HttpPost]
public Task<IActionResult> CreateOrder(CreateOrderRequest request)
{
ValidateInput(request);
// Some other checks
_eventPublisher.Publish(createOrderEvent);
}
Another system exists that we want to interact with which cares about all orders that are created. Due to this system being new a feature flag should be introduced that can be enabled or disabled on deployed environments. Simple enough, we could just introduce a flag in configuration. We could introduce a simple if check here in the controller, but we may want to check for this in multiple places (like the event handler).
So I created a service type to communicate with the external system and introduced a factory for the feature flag. The factory returns the real service if the feature is turned on, or a fake one if it is not. So here's what that looks like:
public interface IExternalOrderSystemService
{
Task CreateOrder(CreateExternalOrderRequest request);
Task ConfirmOrder(ConfirmExternalOrderRequest request); // Used for when payment is approved
}
public class ExternalOrderSystemServiceFactory
{
// Dependencies
public IExternalOrderSystemService CreateService()
{
if (_settings.Enabled)
return new RealExternalOrderSystemService();
return new FakeExternalOrderSystemService();
}
}
// Update to the controller
public Task<IActionResult> CreateOrder(CreateOrderRequest request)
{
ValidateInput(request);
// Some other checks, creation of external request and event
var externalOrderService = _factory.CreateService();
externalOrderService.CreateOrder(externalRequest);
_eventPublisher.Publish(createOrderEvent);
}
But now there's an additional change which is where I'm getting a bit stuck. Now, if the feature flag is enabled the order should not be pumped into the event publisher at all, but only the external system should be called. If the feature is disabled then the order should go through the publisher as it does today. Not only that, if the order is of a specific type (let's just say Order Type A to keep things simple) then the order should go through the event publisher even if the feature is enabled.
if (_settings.Enabled && request.OrderType != OrderType.A)
// Send to external system via HTTP
else
// Send order through message bus pipeline
So what I began to do was start creating a type that can essentially route the order through one method or another. I'm not great with names... I called it an order distributor. The issue that I'm having is that the logic for determining whether it's "enabled" or not is duplicated between the service factory and this new distributor. I have to keep it in both because of the ConfirmOrder method that is called later on in the process. Both of these types must know about the feature and order type check for toggling.
public class OrderDistributor
{
public Task Send(CreateOrderRequest request)
{
if (_settings.Enabled && request.OrderType != OrderType.A)
{
var service = _factory.CreateService();
// Map to external request
service.CreateOrder(externalRequest);
}
else
{
// Create event
_eventPublisher.Publish(orderEvent);
}
}
}
So my thought for solving this would be to create some sort of togglign strategy class that can be consumed by both and called in both areas. By doing so I keep the logic in one place.
public interface IOrderToggleStrategy
{
bool IsEnabled(OrderType orderType);
}
But I'm starting to wonder whether this all makes sense. Is it starting to look like spaghetti? This is as of right now the best design that I've been able to come up with, but I'm open to alternative ideas and for suggestions from anyone who has solved a similar problem in the past.