The Application Controller of our application initialises all of its services when it itself is initialised. Unsurprisingly, some services must be started before others; for example the Logging service must be started before the Configuration service, which must be started before the Persistence service, which must be started before the Authentication service and so on. It sounds hard but it’s easier than playing an online game or getting booster from P4R Gaming.
Currently, all this initialisation is done in one long
initialiseServices() method, initialising each service in the correct order. If we create a new service then we just slot it into this method in the right place. Providing this order is not changed then everything is fine.
So what’s the problem? Well, apart from being a bit ugly, there is a practical problem that has arisen. Our client would like to be able to deploy parts of the application separately, which means we need to split it up into a core which rarely changes and separate modules which can be deployed independently. If one of these modules requires a new service, or one of its services becomes dependent on another, then the Application Controller will also need to be deployed. Not only will the client need to deploy the new module but also a whole new core. Also, the client must wait until both the module and the core builds become stable.
What would be ideal is if each module could tell the Application Controller which services it needs when the application starts. The Application Controller would then determine the order in which to initialise the services based on their dependencies. Cyclic dependencies would be detected upon startup.
In order to demonstrate this, I’ve made a little mock-up. This is probably best understood by looking at the code, but I’ll try and explain it as best I can in words.
The idea is that all Services must extend an abstract base class; Service. The abstract Service class provides public method
addDependency(Service service) which is used to specify the other services on which it is dependent. The sorting algorithm is not interested in services on which each service is dependent, but rather the services which depend on each service. This is because the graph of service dependencies is traversed depth-first, from the services without any other services on which they depend. Therefore, the addDependency(Service service) method actually calls a package-private
addDependent(Service service) method on the passed service object.
Services are then added to a Service Manager. The services are initialised by calling
initialiseServices() on the Service Manager which creates an instance of ServiceReactor which does the sorting, returning an ordered list of services which are to be initialised sequentially.
The ServiceReactor uses topological sorting to arrange the services based on the graph of dependencies.
The example I’ve provided sets up the simple graph as described here:
Note that I didn’t want to make a separate Service classes for each node, so I made an IdentifiableService which gets a unique integer passed to its constructor which is used to identify it as a separate Service. Of course in reality each Service would be defined by its own, separate implementation of the base Service class.
Feel free to download it and have a play with it. For example, try creating a circular dependency and you should get a DirectedCycleException. I know, it really is that much fun!