C# – How should I create a combined interface for two logically independent modules

cobject-oriented-design

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:

Interface EnqueueAndLogMessageService {
   public void queAndLogMessage(Message message);
}

Class EnqueueAndLogMessageServiceImpl {
   EnqueueMessage enqueueMessageSvc;
   LogMessage logMessageSvc;

   public void queAndLogMessage(Message message) {
      enqueueMessageSvc.queMessage(message);
      logMessageSvc.logMessage(message);
   }
}