I'm having trouble coming up with a good way to structure the interfaces for two modules that are logically independent but whose implementations may be combined for the purposes of performance or efficiency.
My specific situation is the replacement of the separate queuing and logging modules in a message routing application with a combined database-backed implementation (let's call it DbQueueLog
). It's easy enough for DbQueueLog
to implement both the IQueue
and ILogger
interfaces so clients of the old queuing and logging modules can use it seamlessly. The challenge is that the most efficient and performant implementation of DbQueueLog
involves combining the EnqueueMessage(Message m)
and LogMessage(Message m, List<LogParams> p)
methods into an EnqueueAndLogMessage(Message m, List<LogParams> p)
method that minimizes the number of cross-process database calls and the amount of data written to disk. I could create a new IQueueLog
interface with these new methods, but I'm uncomfortable with what that would mean if a future iteration of the application moved back to separate queuing and logging modules.
Are there any design approaches to this situation that would allow me to build the efficient, performant DbQueueLog
implementation now without permanently coupling together the application's queuing and logging modules?
Edit: The application is built on Windows using C# in case any there are any platform-specific techniques that might be available.
Best Answer
It sounds like EnqueueAndLogMessage is a pretty typical Service. My suggestion is to expose an interface to EnqueueAndLogMessage. Then, in the concrete implementation of the service, use two instance variables for your EnqueueMessage and LogMessage interfaces. This will allow you to provide one service which handles both the queing and logging capabilities, without tying you to the implementation of either. If you no longer need the combined service, it's as simple as calling a different service.
Psuedo-code: