System is a high level module – either the full application or a major part of it. The root of the model hierarchy is a system, which may contain sub-systems. Systems appear in Tersus Studio as yellow rectangles. A system typically contains multiple displays and/or multiple processes.
Display is a component that appears on the end user’s screen (a GUI component). Typical displays are View, Button, Popup, Table, Label, etc. The model of a highly interactive application will contain many displays, while the model of a back-office server-side system will contain no displays or very few displays. Displays appear in Tersus Studio as blue rectangles (with various icons representing their types). Displays often contain lower level displays and/or processes (e.g. a Button contains the process that should take place when the button is pressed).
Process is a unit of activity. Currently all processes are Actions (“synchronous processes”) – each is a set of activities carried out sequentially without interrupt (e.g. some kind of calculation). In the future, the Tersus Platform will also support Operations (“asynchronous processes”) – processes that may receive inputs after starting. Processes appear in Tersus Studio as blue rectangles (some of them with various icons representing their types). Processes often contain lower level processes and/or data elements.
Data Structure and Data Item contain information used by the application. Data items are atomic building blocks (Text, Number, Date, etc.), while a data structure is composed of lower level data structures and/or data items. Data structures and data items appear in Tersus Studio as grey rectangles (usually with various icons representing their types).
Slots and Flows
Tersus implement Dataflow Programming
. Processes (and in certain cases also systems and displays) are able to receive and send out data through Slots
. The flow of data between processes, as well as the sequencing of processes, is governed by Flows
Slot is either a Trigger (an entry point through which a process receives input data when its starts executing), or an Exit (an outlet through which a process exposes data it generates while executing). A trigger appears as a green, inward-pointing triangle on the frame of a process. An exit appears as a gray, outward-pointing triangle on the frame of a process.
Flow defines the order of execution (when should a process be executed) and/or data exchange (when and how should data be passed). A flow appears as an arrow between two model elements (Source and Target), each a slot or a data element. Each flow can define: (a) order of execution (the process containing its target slot may not start before the flow); (b) data flow from its source to its target; or (c) both order of execution and data flow (for a fuller discussion of this subject, see “Order of Execution” below).
Metadata vs. Data
For the sake of simplicity, the above description intentionally ignored the distinction between models and the runtime objects they model. This distinction deserves a comment.
Models are metadata entities – they define an application’s functionality, but do not represent the actual details of any specific execution of the application. At runtime, the server reads the models and performs them according to the specific circumstances and the specific data of each execution.
Thus, if we want to be precise, the model hierarchy does not contain Displays, Processes and Data Items (“data objects” or “instances”), but rather Display Models, Process Models and Data Models (“metadata objects”). We should not say “a Button contains the process that should take place when the button is pressed”, but rather “a Button model contains the model of the process that should take place when the button is pressed".
The same model may be instantiated many times by the runtime engine (multiple data objects created according to each metadata object). This is similar to what happens with programming languages, where a variable in the program (metadata) differs from its instances created when executing the program (data).
Models vs. Elements
The modeling language makes extensive use of model reusability – the same model can be used in multiple places within the model hierarchy.
We can reuse a data model (Data Type) to define composite data types. For example, the built-in atomic data types Text, Number and Date are the major building blocks from which data structures (composite data types) are composed. Composite data types themselves can be used as elements of other composite data types (e.g. we may define a data structure called Company, and then define another data structure called Transaction, containing Company as one of its elements.
We also reuse a data type when data is transferred between processes. A slot (either a trigger or an exit) has a data type defining what data objects that can flow through it. For example, a Sum process has multiple triggers, all of type Number, and a single exit, also of type Number.
We reuse a process (e.g. some kind of calculation) when it needs to be executed in various circumstances. For example, you can have a process Get Customer Details that returns a customer’s name and address based on the customer’s id (performing a database query and then extracting the required data items from the retrieved database record). This process will be used when generating various reports, when displaying customer information on the screen, etc.
To support reusability, we distinguish between Model Name and Element Name.
Model Name is the shared name of a reusable model (e.g. the built-in atomic data type Number). The model name is similar to a type or a class in a programming language.
Element Name is the local name of a model element is a specific context (e.g. within a composite data model you may have two data elements of type Company, one called Seller and one called Buyer). An element name is similar to a variable name in a programming language.
In Tersus Studio, when a model element is created, its element name is by default identical to its model name (e.g. when you use a Company, the default element name is also Company). By pressing F2 you can change the local element name, the shared model name or both. If you only change the local element name, the two names will differ from each other, and both will be displayed (in the above example, the first data element will be displayed as Seller [Company], while the second data element will be displayed as Buyer [Company]). Think well before you rename a model (the shared name), because the change will affect all its usages (e.g. if you rename Company to Party, Seller [Company] and Buyer [Company] will change to Seller [Party], and Buyer [Party] respectively).
Order of Execution
Basically, the order of execution of elements of a process (its sub-processes and data elements) is determined by the flows between them: an element is only executed (a sub-process starts or a data element obtains its value) after all the elements that have flows to it.
Hence, an element that has no flows leading to it (typical examples are an initialization sub-process or a data constant) is executed immediately (if there are several such elements, the server is free to execute them in any order).
When there are flows leading to an element (i.e. the element or some of its sub-elements are the targets of a flow or several flows), then the element may not be executed before theses flows have been executed (or it becomes clear they will never be executed).
When a flow is executed, it passes data from its source to its target. Notice that in case the source is an empty slot, no data is passed through the flow (just a generic “activation indication”).
When repetitive sub-processes and data elements are involved, they are executed multiple times, each time a different instance. E.g. when there is a flow (or multiple flows) whose target is the trigger of a repetitive sub-process, then each time data arrives to the trigger, a new instance of the sub-process is created, receiving the triggering data object as input.
The mechanism that ensures these execution order rules is a “dependencies system” – an element is only executed after the elements on which it depends:
(R1) A target of a flow depends on its source.
(R2) When there are flows to a data element and to a sub-element of it, then the source of the flow to the sub-element depends on the source of the flow to the parent element.
These abstract rules can be better understood looking at a concrete example:
In the above example, the process All Are VIP initializes the Customer Table with two customer records after promoting both customers to status “VIP”.
The order of execution will be as follows:
1. All Are VIP contains 4 data constants which are not the target of any flow (“David”, “Regular”, “John”, “Bad”). These are activated first (as constants they already have their values).
2. The two data structures Customer 1 and Customer 2 each depend on two of these constants, so they will be executed next (with no specific order between them). Customer 1 will have its Name element set to “David” and its Status element set to “Regular”, while Customer 2 will have its Name element set to “John” and its Status element set to “Bad”.
3. The Make VIP sub-process is a repetitive element which is instantiated twice – once when the value of Customer 1 flows to its trigger, and once when the value of Customer 2 flows to it.
4. For each instance of Make VIP, the following happens:
4.1. The trigger Anyone and the data constant “VIP” are to be executed first, because none of them is a target of a flow within the context of Make VIP (the trigger has already obtained its value through the flow that caused the creation of the instance of Make VIP).
4.2. Yet, the target of the flow from the trigger is the whole data structure Customer, while the target of the flow from “VIP” is the Status element of this data structure. Therefore, according to the rule (R2) above, “VIP” depends on the trigger, and will only be activated after the trigger.
4.3. So the trigger Anyone is activated first, sending to Customer its input value (e.g., in the first instance, Customer/Name is set to “David” and Customer/Status is set to “Regular”).
4.4. Then the constant “VIP” is activated, and its value overrides the existing value of Customer/Status. Thus Customer/Status is changes from “Regular” to “VIP”.
4.5. Customer is now ready, so its value flows to the VIP exit of Make VIP.
4.6. The Problems error exit is not the target of any flow, so it is not activated.
4.7. This concludes (an instance of) Make VIP.
5. The Notify Error sub-process depends only on the Problems error exit, and since this exit is not activated for any instance of Make VIP, it is clear the flow to Notify Error will never be executed. Therefore, Notify Error is not executed (assuming its trigger is mandatory).
6. We finally get to the Show Customers display data element. Its repetitive Customer Table/Customer element depends on the VIP exit of Make VIP. Therefore, each time the exit sends out a value, an instance of Customer is created in Customer Table:
6.1. The first instance of Make VIP sends out the value [“David”, “VIP”], becoming the value of the first row in Customer Table.
6.2. The second instance of Make VIP sends out the value [“John”, “VIP”], becoming the value of the second row in Customer Table.
6.3. As explain in step 2 above, there is no specific order between Customer 1 and Customer 2, so it is possible for [“John”, “VIP”] to become the first row in the table, while [“David”, “VIP”] becomes the second row.
7. As there are no more elements in All Are VIP, this concludes the process.
As explained above, the Tersus Server executes an application according to the logic defined by its model. Put differently, the functionality of an application is the combination of the functionality of each model in the hierarchy of models defining it.
The functionality of a model is defined by its plug-in. A plug-in is the definition of a functional building-block that can be used to model an application. There are atomic plug-ins such as the built-in data types or mathematical calculations, and composite plug-ins such as composite data structures or composite processes.
A plug-in consists of the following attributes:
– The type of the plug-in (System, Display, Action or Data).
– The tier at which the plug-in is executed (Server, Client or both).
– For a server-side plug-in, the Java class implementing it (see below).
– The icon that is displayed for the plug-in in Tersus Studio.
– Documentation of the plug-in (an HTML file).
A server-side plug-in is a process that must take place on the server. Such a plug-in (a typical example is a database update) has a Java class implementing it. For example, the Tersus/Database/Insert plug-in, provided as part of the Tersus Common Library, is implemented by the class tersus.plugins.database.Insert, which performs the required database update. The Java code of a plug-in should conform to the interface mandated by the Tersus Platform (how to retrieve the values of data objects received through the triggers of a process, how to expose data objects through the exits of a process, how to handle errors, etc.).
When a plug-in is marked as either client-side or server-side, the Tersus Server has the freedom to choose where to execute it. There are certain constraints, of course. For example, if a model has a server-side plug-in, then all its elements are also executed on the server. These execution constraints imply corresponding constraints on modeling – you may not place a client-side model within a server-side model (e.g. you may not use display elements within a server-side process).
Data models are naturally of dual mode, living both on the client-side and on the server-side, because data object are transferred back and forth between processes running in both tiers.
The Tersus Common Library is a library of predefined models which serve as modeling building blocks.
The library is divided into several categories (such as Data Types, Database, Display, Math, etc.), and each category contains several models, called Template.
A template is a generic/typical model using a certain plug-in. When a modeler, working in Tersus Studio, drags a template from the template palette, the template is copied into the model hierarchy, and then, if required, the modeler may modify it to fit the specific needs at this place.
For example, the Add template (belonging to the Math category) has two numeric non-repetitive triggers, because its basic usage is to add two numbers. Yet, in any specific context the modeler may add triggers, remove triggers, or make some of the triggers repetitive – the template’s plug-in (Tersus/Math/Add) accepts any number of inputs, as explained in the documentation of the Add template.