Unpredicted hurdles in dealing with integration, variability, and interoperability in software development can be overcome via higher-order process engineering. Even in its simplicity-oriented, tamed form, this approach fosters a powerful plug&play discipline, where processes and services are created, stored, passed, and moved around like data.
The following illustrations show how the higher-order feature works by describing the execution of a higher-order process. The process is situated in the Chainreaction domain. The green arrows depict the control-flow of the execution, blue arcs represent a read operation (data-flow), as well as orange connections write operations. The pictures are separated into two parts denoting the control-flow (lower part) and the data variables (upper part), respectively.
Fig. 1 introduces a simple process, which creates two game strategies (realized as process models themselves) for the game Chainreaction and stores them to the execution context. Afterwards the process starts a game where both game strategy process instances are passed just like data as higher-order parameters and compete (see activity
start game). Each game strategy evaluates the benefit of making a specific game move and returns it as an integer. The execution environment (encapsulated by the activity
start game) is responsible for choosing the best rated move and handling the interaction with the game.
In the first image the beginning of the process execution is depicted. Currently, the constructor activity labeled
ExampleGS has been executed, i.e., it has created a new process instance of the game strategy
ExampleGS and stores it to the context variable
player 1 GS.
Fig. 2 shows that the process instance stored to the context variable is a concrete game strategy implementation. It implements the graph interface
GameStrategy, which ensures compatibility between all game strategies.
The strategy evaluates the worthiness of a cell as follows (see Chainreaction for details of the game):
- for a corner cell a bonus of
- for an edge cell a bonus of
- a critical cell which is endangered (has critical neighbor cells) gets a bonus of
- for a cell that is endangered but not critical a malus of
Fig. 3 depicts:
- the situation after executing constructor activity
Standard GSwhich creates a new process instance of a second game strategy implementation process.
- The process instance is stored into the context variable
player 2 GS.
In Fig. 4 the implementation of the newly created game strategy process instance is shown. The
Standard GS strategy evaluates the worthiness of a cell as follows (see Chainreaction for details of the game):
- A bonus of
1is added for each atom that the player has gained by doing the respective move.
- A bonus of
1is added for each atom that the oponent looses when the player does the corresponding move.
start game receives two game strategies as input parameters. Fig. 5 shows that the game strategy process instances stored earlier (depicted via the icons labeled
stGS in the context variables) are passed to the activity as parameters. This is where the actual process passing takes place as actual parameters to the subprocess represented by the activity
start game. Hence, process instances are treated as first-class citizens.
Now the execution dives one hierarchy level deeper into the sub process beneath the activity
start game. The respective sub process is shown in Fig. 6. At first, the formal input parameters are evaluated, i.e., at runtime the passed arguments from one hierarchy level above (i.e. the process from the images Fig.1 – Fig. 5) are stored to context variables. In this example execution the
Example GS from the input parameter
player 1 GS is stored to context variable with the same name
player 1 GS the
Standard GS analoguously via input parameter
player 2 GS to the equally named context variable.
The process manages the game turns where the players do their moves alternatingly until a winner is found. If it is the first player’s turn (see Fig. 7), the activity
player 1 GS takes the game strategy process instance stored in the respective context variable and stores it to the context variable
current GS. This is currently the game strategy
ExampleGS of the first player. The context variable
current GS now holds a reference to the corresponding process instance highlighted via the small icon
For each player’s turn the process iterates through the cells of the board (activity
iterate cells). In each loop iteration the process instance of the current game strategy (read from the context variable
current GS) is executed as shown in Fig. 8. For the first player this is the game strategy
ExampleGS. Hence, the activity
evaluate cell uses the process instance currently stored in context variable
current GS and executes it in a plug&play manner. Compatibility is guaranteed via the common graph interface
Fig. 9 depicts that after the worthiness of each cell has been evaluated:
- the best rated cell is chosen (cf. activity
select best cell),
- the move is actually carried out (cf. activity
place atom), as well as
- the next move is initiated and its the turn of player 2 now.
For the second player, the respective game strategy process instance
Standard GS — stored in context variable
player 2 GS — has been put to context variable
current GS replacing the game strategy from the first player.
Fig. 10 shows, that this time the very same activity
evalute cell reads in the current game strategy
Standard GS from the context variable
current GS that did execute the game strategy of the first player before (in the same execution!). Hence, the new implementation has been plugged in and played at runtime, which is:
- easy to understand,
- simple to apply,
- concise and powerful in terms of necessary activities as well as reuse, and
- safe regarding compatibility.
The (technical) key to our approach is the introduction of type-safe stacked second-order execution contexts (see below) that allow for higher-order process modeling. Tamed by our underlying strict service-oriented notion of abstraction, this approach is tailored also to be used by application experts with little technical knowledge: users can select, modify, construct and then pass (component) processes during process execution as if they were data.
We base on the eXtreme Model-Driven Design (XMDD) paradigm and its incarnation the jABC3 which allows for complex hierarchical graph structures by means of business activities that represents a complete sub-graph. Newly, in our enhancements to XMDD (subsumed by HOPE), we support complete processes as first class objects in the (execution) context of a process leading to second-order process modeling. The main technical idea is to allow virtual process model calls in form of business activities representing sub-models, introducing polymorphic executable process models. This is realized by considering process model instances, denoted by process instance, as first-class objects and therefore introducing a second-order context for exchange between the sub-graphs of a service graph. For higher-order modeling, we go one step further towards higher-order functions known from functional programming, as a process instance may have a context with further process instances and so forth. This is realized via a local context for every hierarchy level, that is implicitly stacked. Hence, our context is still second-order. Furthermore we offer activities for the creation of new process instances denoted by constructor activity.
Hence, we go one step further to a tamed variant of higher-order process modeling, which avoids many of the well-known problems of full higher-order as it is based on a very strong notion of type correctness (cf. ). Sub-process instances may be used as input and output parameters of a process — called Service Logic Graphs (SLG) — and also be stored in the execution context of every hierarchy level. The actual parameters of a graph type are process instances with its own runtime execution context, which is second-order. Hence, we slice our higher-order models into second-order contexts, which are stacked implicitly. This results in a ‘tamed’ notion of higher-order which allows us to statically type every parameter and context variable.
The compatibility between process model implementations — the service graphs is ensured via so called interface graphs. The development environment ensures that derived service graphs fullfil the requirements of their interface graph. The graphs have fully defined interface descriptions and hide their internals. This supports loose coupling and reusability as well as the organization of process models in process libraries and is technically achieved by:
- constraining the context of processes to local access from and to the current hierarchy level,
- canalizing communication between hierarchy levels via the input/output behavior of an SLG,
- storing and retrieving the instance of a process model to/from the context, in order to be able to exchange the process implementation of an interface activity, and
- documenting service and interface graphs and their input and output parameters in a similar fashion as, e.g. Javadoc for Java.
The documentation is presented in the GUI of the development environment jABC as tooltips in order to ease communication between the modeler of different hierarchy and therefore abstraction levels. The interface and service graphs are organized in SLG libraries and are published to an application expert for use without any knowledge about the details of the underlying structure. This may even be used for communication of requirements, as an application expert may, e.g., model and document a dummy service or interface graph, that is then implemented by someone else. The realization of the hierarchy levels can be done separately, by different responsible people or groups. Every graph (interface and service graphs) may be equipped with an icon (e.g. a raster or vector graphic) which is used for its representation as it is used as a business activity. This eases the understanding of a model with a lot of sub-processes and serves as a recognition feature that appeared to be very effective in cooperation with industrial partners.
It is only a matter of design on which level of abstraction polymorphism is introduced. This has different dimensions on all hierarchy levels, e.g.:
- variants of a process step in a high-level process can be implemented differently introducing product-lines
- a high-level process may focus on the structure as the complete implementation details, e.g. whether user input comes from a terminal, a GUI, a web page, or a mobile device, are hidden from the application expert completely
- the level of abstraction can be changed by choosing implementations that operate on different layers of a multi-layered application like, e.g., the presentation layer and the business logic layer.
A business activity representing a service or interface graph SIB is denoted by interface graph SIB resp. service graph SIB, or graph SIB if any of both is meant. In general, their interface description consists of a list of inputs, the origin of the process instance, a list of outputs for each outgoing branch, and a reference to its interface or service graph. Process instances as well as outputs are read from resp. written to the context. The corresponding values are represented by context variables at runtime. A process instance may be initialized and put to the context in the graph itself (via constructor SIBs) or before the process has been started. Input parameters may be provided statically for primitive types as well as a selection of Java types supported by the jABC, e.g., strings, enumerations, and file handles. That means that a modeler can design new SLGs and decide which input values are statically entered and which are retrieved from shared resources on-the-fly at modeling-time.