• No results found

25The Simple Component Pattern

In document Reactive Data Handling (Page 30-32)

with Brian Hanafee and Jamie Allen

25The Simple Component Pattern

this purpose and it will interact with the rest of the system on behalf of the clients. We could break up responsibility even finer along these lines, but for now let us consider this aspect of representing clients within the client as one responsibility; the client interface will thus be our second dedicated component.

Once a job has been accepted and the client has been informed by way of an acknowledgement message our system must ensure that eventually the job will be exe- cuted. This can only be achieved by storing the incoming jobs on some persistent medium and we might be tempted to place this storage within the client interface component. But we can already anticipate that other parts of the system will have to access these jobs, for example in order to start their execution. This means that in addition to representing the clients this component would assume responsibility for making job descriptions accessible to the rest of the system, which is in violation of the single responsibility principle.

Another temptation might be to share the responsibility for the handling of job descriptions between the interested parties—at least the client interface and the job executor as we may surmise—but that will also greatly complicate each of these com- ponents, as they will now have to coordinate their actions, running counter to the Simple Component Pattern’s goal. It is much simpler to keep one responsibility within one component and avoid the communication and coordination overhead that comes with distributing it across multiple components. Besides these runtime con- cerns we also need to consider the implementation: sharing the responsibility means that one component needs to know about the inner workings of the other, so their development needs to be tightly coordinated as well. These are the reasons behind the second part of “do only one thing, but do it in full.”

This leads us to identify the storage of job descriptions as another segregated responsibility of the system and thereby as the third dedicated component. A valid interjection at this point is that the client interface component might well benefit from persisting the incoming jobs within its own responsibility, this would allow shorter response times for the job submission acknowledgement and it also makes the client interface independent from the job storage component in case of temporary unavailability. However, such a persistent queue would only have the purpose of even- tually delivering accepted jobs to the storage component, who then will take responsi- bility of them. Therefore these notions are not in conflict with each other, we might implement both if system requirements demand it.

Clients Coordination Storage Client interface Job scheduling Execution

Figure 12.2 Intermediate component separation with the Coordination component being broken up in three distinct responsibilities.

Taking stock, by now we have identified the client interface, the job storage, and the job executor as three dedicated components with non-overlapping responsibilities. What remains to be done is to figure out which jobs to run in what order, we call this part “job scheduling.” The current state of our system’s decomposition is shown in fig- ure 12.2; now we apply this pattern recursively until the problem is broken up into simple components.

Probably the most complex task in the whole service is to figure out the execution schedule for the accepted jobs, in particular when prioritization or fairness is to be implemented between different clients that share a common pool of resources—the corresponding allocation of computing shares are usually a matter of intense discus- sion between competing groups of people.2 The scheduling algorithm will need to

have access to job descriptions in order to extract scheduling requirements (maxi- mum running time, possible expiry deadline, which kind of resources are needed, etc.), so this is another client of the job storage component.

It takes a lot of effort—both for the implementation and at runtime—to plan the execution order of those jobs that are accepted for execution, and this task is inde- pendent from deciding which jobs to accept. Therefore it will be beneficial to sepa- rate the responsibility of job validation into its own component. This also has the advantage of removing the rejected tasks before they become a burden for the sched- uling algorithm. The overall responsibility of job scheduling now consists of two com- ponents, but its overall function should still be represented consistently to the rest of the system. For example, the executors need to be able to retrieve the next job to run at any given time independently of whether there is a scheduling run in progress or not. For this reason we place the external interactions in an overall scheduling com- ponent of which the validation and planning responsibilities are delegated to sub- components. The resulting split of responsibilities for the whole system is shown in figure 12.3.

12.1.3 The Pattern Revisited

The goal of this pattern is to implement the Single Responsibility Principle and we did that by considering the responsibilities of the overall system at the highest level—cli- ent interface, storage, scheduling, execution—and separating these into dedicated

2 The authors have some experience with such allocation between different groups of scientists competing for

data analysis resources in order to extract the insights they need for academic publications.

Clients Client interface Job scheduling Storage Execution Validation Planning

Figure 12.3 The resulting component separation

27

In document Reactive Data Handling (Page 30-32)