I am developing a desktop application that will be run at each station at a call center. It listens for an incoming call, and whenever a call arrives, there are about 30 different classes that need to run various operations. Some of these operations are synchronous, such as setting a flag because they want to log only the first time per call that an event happens. Some of them are asynchronous, such as establishing a socket to another application and sending a start message. At the end of the call, the classes will also have to do some clean up, such as sending an end call message, awaiting a response, and then closing the socket.
Currently, I create all the classes in the composition root, and they live for the life of the application. Each time a call arrives, the state of all of these classes gets reset. Here is a simplified, but working, code sample:
class Program
{
static void Main(string[] args)
{
var messagePersistence = new MessagePersistence();
var socketConnection = new SocketConnection(messagePersistence);
var otherComponent = new OtherComponent();
ICallComponent[] Components = { messagePersistence, socketConnection, otherComponent };
TestCall testCall = new TestCall(Components, socketConnection);
testCall.Execute().Wait();
// Create another TestCall instance or run the same one again?
testCall.Execute().Wait();
Console.WriteLine("Press any key to end");
Console.ReadKey();
}
}
class TestCall
{
private IEnumerable<ICallComponent> _components;
private IDataReceiver<byte[]> _dataDestination;
public TestCall(IEnumerable<ICallComponent> components, IDataReceiver<byte[]> dataDestination)
{
_components = components;
_dataDestination = dataDestination;
}
public async Task Execute()
{
// Start the components
foreach (ICallComponent Component in _components)
{
await Component.StartAsync();
}
// This simulates the actual work, which would periodically pull bytes from some source and send them to _dataDestination
using (var Timer = new System.Timers.Timer(500))
{
Timer.Elapsed +=
async (sender, e) =>
{
await _dataDestination.ProcessData(new byte[] { 1, 2, 3 });
};
Timer.Start();
await Task.Delay(5000);
}
// Stop the components
foreach (ICallComponent Component in _components)
{
await Component.StopAsync();
}
}
}
interface ICallComponent
{
Task StartAsync();
Task StopAsync();
}
interface IDataReceiver<T>
{
Task ProcessData(T data);
}
class SocketConnection : ICallComponent, IDataReceiver<byte[]>
{
private IDataReceiver<string> _messageDestination;
public SocketConnection(IDataReceiver<string> messageDestination)
{
_messageDestination = messageDestination;
}
public async Task StartAsync()
{
Console.WriteLine("Making socket connection");
await Task.Delay(1);
Console.WriteLine("Executing initial connection handshake");
await Task.Delay(1);
Console.WriteLine("Socket is connected");
}
public async Task StopAsync()
{
Console.WriteLine("Executing close connection handshake");
await Task.Delay(1);
Console.WriteLine("Closing socket connection");
await Task.Delay(1);
Console.WriteLine("Socket is closed");
}
public async Task ProcessData(byte[] data)
{
// Simulate sending data on the socket, and sometimes getting a response that would trigger
// a string going to the destination
await _messageDestination.ProcessData("ABC");
}
}
class MessagePersistence : ICallComponent, IDataReceiver<string>
{
public async Task StartAsync()
{
Console.WriteLine("Creating call database record, start time={0}", DateTime.Now);
await Task.Delay(1);
Console.WriteLine("Database record created");
}
public async Task StopAsync()
{
Console.WriteLine("Finalizing call database record, end time={0}", DateTime.Now);
await Task.Delay(1);
Console.WriteLine("Database record finalized");
}
public async Task ProcessData(string data)
{
Console.WriteLine("Creating message database record, time={0}", DateTime.Now);
await Task.Delay(1);
}
}
class OtherComponent : ICallComponent
{
public async Task StartAsync() { await Task.Delay(1); }
public async Task StopAsync() { await Task.Delay(1); }
}
My problem is that this seems kind of fragile. The order in which the objects is started becomes very important, especially if the objects are related. Otherwise you can run into a situation where SocketConnection is feeding messages to MessagePersistence, which hasn't yet completed its startup. This prevents doing something like running the tasks in parallel.
I feel like it would be better to create these objects as needed, so they can never be in an invalid state. In other words, the call started event would call a factory method to asynchronously create objects. That way, the MessagePersistence dependency passed to SocketConnection will be guaranteed to be in a valid state for receiving messages before SocketConnection is even created.
So my thought would be to make an abstract factory like the following:
interface ICallComponentFactory
{
Task<IEnumerable<IAsyncDisposable>> CreateCallComponentsAsync();
}
interface IAsyncDisposable
{
Task DisposeAsync();
}
The call started event handler would call the factory method to get a collection of IAsyncDisposables, which it would save in a shared data class injected into it. The call ended event handler would also be injected with this shared data class and could just go through all of them and call DisposeAsync to do the cleanup.
Does this technique make sense? I'm used to a factory creating one instance of an object. Is it weird to have a factory that creates a bunch of objects and returns all of them in a collection? Or is that overkill and things are fine the way they are? Or maybe there is some cleaner way that I'm missing? I'm using C# 6.0 if it matters.
CallComponentSet
, very simple, just to hold that bunch ofICallComponents
. Then you can still have a clean-seemingCallComponentSetFactory
that only creates a single instance of something.