pic2: PostgreSQL Application, first Component
The proposed UI is centered around the concept of ‘Component’ - the smallest visual element on the canvas (shown as a circle). There is also an ‘Application’ entity that could include several components, although it is quite normal to have single-component application. Each component includes several configuration parameters, as well as their validation and transformation logic. Upon clicking on a component a configuration form appears where one can specify all the parameters the component consists of, as well as their descriptions. The reasons for dividing an application into several components are:
- avoid very tall forms which have to be scrolled down (like in Juju) - they seem awkward
- allow visual components to be composed within other components (nested) to enable composition. For example you could place a heat resource within the top level template or within a nested resource.
To distinguish components with valid data from the ones with invalid data a color coding is used: green outline for valid ones, red for invalid.
pic3: PostgreSQL Application, second Component
While ‘PostgreSQL Instance’ and ‘Apache Instance’ components are located within their applications (thus the values one can specify for those instances are subject to the appropriate application’s constraints), it is not a must-requirement for, e.g. an Apache application to use its own instance - it can also use some ‘Generic Instance’, which has less constraints and is less useful.
pic4: Selecting Generic Instance in a Selector
The components may be connected with each other or with other applications. For this purpose the ‘Connector’ entity (small circle on the rim of component) exist. Initially, when a single component is added to a canvas, its only connector is empty. Once the user clicks it, a list menu appears, where all possible targets for a connection can be specified (another option here is to filter components in the left side panel). After selecting the target component type the source component can be connected either to one of the compatible existing components on the canvas (they will be highlighted) or to the component-less canvas space which will lead the creation of new component of that type on that place. After the link has been established, another empty connector appears on the rim of the component - if it can be still connected to other entities. For an existing link, a number of identical components (e.g., PostgreSQL Instances) can be set - a spinner menu appears after clicking on the instantiated connector. When a multi-component application is added to the canvas, it may have some predefined connections.
pic5: Specifying number of instances in connector
All connections are direct by intent: this way they denote a clear dependency between components, which allows to infer, e.g. some initial values in the ‘Apache App’ component’s form from the values of the ‘Apache Instance’ component or the ‘PostgreSQL DB Server’ application. One may say that ‘Apache App’ -> ‘Apache Instance’ dependency is a bad example because it is the instance parameters that rely on the type of application they support - i.e. choice of valid images and flavors, etc. Well, it would be true if ‘Apache App’ relied on the ‘Generic App’ component, because ‘Generic App’ component has no inherent knowledge of flavor and image limitations. But the ‘Apache Instance’ does have that inherent knowledge - because it is defined inside the ‘Apache Web Server’ app. On the other hand, if we used ‘Generic Instance’ for ‘Apache App’, it would be connected the opposite way, ‘Generic Instance’ -> ‘Apache App’ - in order to allow the ‘Generic Instance’ to infer knowledge of its limitations from the ‘Apache App’. But the second solution seems less favorable to me, because the ‘Generic Instance’ would have to be really generic, i.e. include logic for every possible constraint use case.
In order to maximize UI responsiveness, every component form should be validated client-side, server-side validation will be run once user finishes with canvas and sends the data to the server. For most cases, the data that is valid on client will be valid on also on server, while the security requirement of server-side validation will be kept.