class SongController { public HandleMessage(user, requestData) { int currentSongId = this.GetCurrentlyPlayingSongId() ExecuteNewTask(() => { Song song = SongRepo.GetSong(currentSongId); FTPSend(this.ParseOutAddress(requestData), Song.AudioData); }); new SongView().Render(); //<---- Current Song Sent :) } } class SongController { public HandleMessage(user, requestData) { int currentSongId = this.GetCurrentlyPlayingSongId() ExecuteNewTask(() => { Song song = SongRepo.GetSong(currentSongId); FTPSend(this.ParseOutAddress(requestData), Song.AudioData); } new SongView().Render(); //<---- Current Song Sent :) } } class SongController { public HandleMessage(user, requestData) { int currentSongId = this.GetCurrentlyPlayingSongId() ExecuteNewTask(() => { Song song = SongRepo.GetSong(currentSongId); FTPSend(this.ParseOutAddress(requestData), Song.AudioData); }); new SongView().Render(); //<---- Current Song Sent :) } } I think you may over complicating this in some ways.
Lets think of the chat responses as your UI, each command is its own "web page", but instead of rendering html, it is rendering IRC responses (and sometimes songs, ect).
Now let's go back and reference MVC pattern, it almost always works well for a UI, and your "UI" is not one of the exceptions in my opinion. Think of each command as having its own controller, similar how in a traditional web app, each entity would have its own controller.
Bus |->Dispatcher |-----> SongController |-----> HelpController |-----> PollController |-----> ect I suspect some of the trouble you may be having is from trying to impose a relationship between a commands, where there is very little.
All the above dispatcher does, is figure out what the command is, then forward the request to the appropriate controller.
You will have a separate project for your domain models, with classes such as User. A third project will contain the classes for saving/retrieving data.
The Dispatcher can also have the the message come from a service bus if you want, though, that may be over kill depending on how you have things implemented
class MessageDispatcher { public void HandleMessage() { User currentUser = UserRepository.GetUser(this.GetUsername()); Channel channel = ChannelRepository.GetChannel(this.GetChannelId()) string commandChar = currentUser.CommandCharSetting; //usually '!', but sometimes '#' string command = this.ParseCommand(commandChar); if (command == null) //Command not found { command = channel.DefaultCommand; } if (command == "song") { SongController.HandleMessage(currentUser, requestData); } if (command == "help") { HelpController.HandleMessage(currentUser, requestData); //Note, the method signature here CAN be different, we don't want to try to force a relationship here, where none exists. Each controller is responsible for figuring out what to do with the command itself, it may or may not need a user, which we are only passing as an optimization } Next the controller will take care of the specifics of the command, including looking up any needed data from the repositories, or handling the buses/asych stuff like starting a task to send a song to the appropriate address.
class HelpController { public void HandleMessage(user, requestData) { HelpResponseModel model = new HelpResponseModel(); model.NumberOfPeopleHelped = StatsRepo.GetTotalPeopleHelped(); model.CommandChar = user.CommandCharSetting; StatsRepo.IncrementTotalPeopleHelped(); new HelpView().Render(model); //The song controller would look up the song and send the file instead } } class HelpView : IRCView { public void Render(HelpModel model) { this.WriteLine("{0} have asked for help! :)", model.NumberOfPeopleHelped); this.WriteLine("{0}help - get help", model.CommandChar); this.WriteLine("{0}poll - get opinions, model.CommandChar); // ect } } Your song controller will be very different:
class SongController { public HandleMessage(user, requestData) { int currentSongId = this.GetCurrentlyPlayingSongId() ExecuteNewTask(() => { Song song = SongRepo.GetSong(currentSongId); FTPSend(this.ParseOutAddress(requestData), Song.AudioData); } new SongView().Render(); //<---- Current Song Sent :) } } Obviously, this exact code will not work for you, but I hope you get the idea, and don't try to over complicate things. This may not have addressed performance concerns as much as you would have liked, but this design will make it easier to measure and replace details as needed.