Skip to main content
Version: 2.1

Best practices

Naming conventions

In general, all bricks shall be named using Title Case, with capital letters for all words and spaces between words.

Functions and Actions

Function Names use Title Case, with capital letters for all words and spaces between words.

inputs and outputs are in lowercase, with spaces between words.

Data models

Model Names use Title Case, with capital letters for all words and spaces between words.

properties and relations are in lowercase, with spaces between words.

UI components

Component Name use Title Case, with capital letters for all words and spaces between words.

Properties use Title Case, with capital letters for all words and spaces between words.

UI Components

UI Components have two main purposes:

  • Allow the creation of reusable components
  • Make it easier to layout elements

For example, using a UI Component to store the content of a form allows to easily align the different text fields and form elements. The 'form component' can then be dropped on a screen and resized in a coherent manner, without needing to control each form element individually.

Reusing UI Components: exposing children object properties

It is quite rare that we want to reuse a UI Component as is in multiple locations. In most cases, we want to parameterise in one or both of two ways:

  1. We want to make it themable
  2. We want to have different texts (or any other property) in the different instances of the UI Component

Customizing the theme

If a UI Component is to be reused by multiple projects, the best practice is to add a custom property to the UI Component (e.g. "OK button theme class"), and map it to the relevant UI Elements using the f(x) of their Theme Class property.

Within a single project, another option (to be used with care) is to establish a convention when naming the theme classes, and use that convention within the UI Component.

If only very specific properties must be settable, the recommendation is to add as many custom properties as needed (e.g. "OK Button Font Size"), and map those to the appropriate UI Element within the UI Component.

Customizing texts (or other properties)

When text or other properties of the UI Component's internal UI Elements must be set, use the following approach:

  1. Add a custom property in the Context of the UI Component (e.g. "Form Title")
  2. Map that property to the appropriate UI Component, using the f(x) of the relevant property.

Exposing internal events

Beyond properties, the UI Component's context can define Events.

Those events can be triggered from within the UI Component using Dispatch Event actions.

Within the parent Screen or UI Component, the application can respond to the events by configuring the f(x) on the UI Component's events.

Data

Upon creating a data model, it is recommended to also create a set of helper logic bricks to make it easier to manipulate that data. The standard programming terminology is:

  • Constructors are used to construct objects from a model and the relevant parameters to should be set. We may typically have multiple constructors to accommodate different criteria.
  • Getters are used to retrieve information from an object.
  • Setters are used to modify information within an object.

We recommend the following practices and naming conventions to make it easy to manipulate data.

Summary

Given the following business model:

DataModel

It is good practice to create the following bricks:

  • Constructor: a Create Geolocation action, taking "Longitude", "Latitude" and "Altitude" as inputs, and outputting a persisted "Geolocation" object.
  • Getter: a Geolocation's Attributes function, taking a "Geolocation" object as input, and outputting "Longitude", "Latitude" and "Altitude"
  • Setters: a Set Geolocation's Attributes action, taking a "Longitude", "Latitude", "Attribute" and "Geolocation" as input, and outputting a "Geolocation" object.

Even though you will often create multiple specialised variants of those bricks, those three base bricks provide a very good starting point. The sections below dive into those elements in a bit more detail.

Constructors

Constructors are logic bricks that typically contain a Create Local Object brick, as well as a few Set Object Property and Persist bricks.

Naming convention:

Constructors should be named as: Create <<name of business model>> from <<name of input parameters>>

Examples:

  • Create Geolocation
  • Create Geolocation from City Name
  • Create Temperature from °C

Here is a potential Constructor action for creating a Geolocation object from a Longitude and Latitude:

Constructor

As the number of actions and parameters grows, you may also want to stack the bricks instead (with the drawback of having the Control Flow getting a bit in the way):

Constructor stack

Getters

Getters allow retrieving information from the object. Those can be simple properties (like Latitude in our example above), or higher-level (and often higher-valued as well) information.

Naming convention

A function that provides all (or most) attributes at once should be named as: <<name of business model>>'s Attributes.

Getters of individual or (subset of) attributes should be named as: <<name of business model>>'s <<property name>>.

When transforming the output using a different representation or unit, this convention applies: <<name of business model>> as <<unit>>.

Examples:

  • Geolocation's Attributes
  • Geolocation's Latitude
  • Geolocation's Position (might for instance provide only Longitude and Latitude, but not Altitude)
  • Geolocation as String (provides a location with the following format: 46.5172214°N, 6.5621934°E)
  • Geolocation as Address (provides a location with the following format: PSE D, 1015 Ecublens, Switzerland)
  • Temperature as °F (provides a temperature as Fahrenheit)

Here is a sample getter that retrieves a single property:

Getter

Many functions need to retrieve more than one property of an object. As such, it is often more convenient to define a single function that provides all attributes. It is important to note that there is no harm in leaving outputs unconnected within a function. Retrieving all properties at once:

Get all properties

Setters

Setters are used to modify the attributes of an object.

Naming convention

Setter's naming convention is similar to that of constructors and getters, with conventions of the form:

  • Set <<name of business model>>'s <<attribute>>
  • Set <<name of business model>> from <<high-level object>>

Examples:

  • Set Geolocation's Attributes (when setting all attributes at once)
  • Set Geolocation's Position (when setting both the Latitude and Longitude)
  • Set Geolocation from Address
  • Set Temperature from Fahrenheit

Here is a sample setter:

Sample setter

Pitfalls

Draw still isn't feature complete. This section therefore lists the main limitations and possible workarounds.

Positioning and resizing

At the moment, the positioning and resizing capability is very powerful, but also very tedious. It is typically not practical to build a fully responsive application that works on a large range of screen sizes. The following tips may help:

  • Percentage sizes will work in most cases
  • Chain objects to one another using f(x), e.g. to allow
  • Use UI Components to group objects together. Note that while it makes it easier to position objects, UI Components also isolate internal context and events (see above), so they do require a bit of thinking.
  • If you need very different layouts on phones and laptops, build separate apps

Refactoring

Refactoring typically occur on different levels:

  • Reusing a part of a function in another function
  • Updating data models
  • Updating the UI

Reusing functions

It is good practice to separate UI interactions from logic. Starting with this idea in mind reduces the probability of having to redo a function because we initially put all operations within an On Click f(x).

In terms of data interactions, preparing and managing constructors, getters and setters separately greatly reduce the amount of rework that is required.

Data models

When refactoring a Data Model, the following approach is recommended:

Adding a property:

  • Nothing to do here, but don't forget to update the Constructors, Getters and Setters accordingly.

Removing a property

  • In the data model editor, rename the property to "my property OLD"
  • Look for usage of the property using Cypher queries - UsefulCypherQueries.html
  • Update all locations accordingly
  • Once all usages have been removed, delete the property. This will delete all instances of that property in the DB.

Changing a property's type, or refactoring a property into a separate business model is done by combining the approaches above, i.e. by adding the new version and removing the old usage.

UI updates

UI Components and Themes are the best way to encapsulate and reuse information.

Scrolling

Lists are scrollable by default. Screens however are not, as they are meant to support apps, which usually have scrolling in specific locations, and not globally for the whole app. If you need a scrollable pane, you must for the moment use UI Components. The simplest approach is the following:

  • Screen contains 1 full screen UI Component, and nothing more
  • Full screen UI Component contains any additional UI Component

Local vs Global objects

By default, the Create Local Object brick doesn't persist the object on the Orchestrator.

Importantly as well, files (typically those coming from Take Camera Picture) must be persisted explicitly.

The recommendation is to always persist all objects by default within the constructor. There are obviously cases where objects should not be stored in the Orchestrator, some of which are documented in the Olympe Best Practices, but those arise in slightly more advanced scenarios.

When building constructors that don't include the Persist operation, we recommend making it explicit by putting 'Local' in the constructor's name: Create Local <<name of business model>>.

Using multiple users and permissions

Draw offers the ability to create multiple users for the application. By default, objects created within the application by one particular user are only visible to that user. Roles and permissions however need to be implemented using Code framework, and be exposed in Draw via custom bricks.

Working with multiple browser tabs

It is often very convenient to open multiple tabs or windows to avoid having to switch from one editor to another. Draw stores the current location, such that we can reload the page without changing screens. However, this is a global setting at the moment, so if you wish to work in different browser tabs, it is recommended to use multiple browsers (e.g. Firefox, Chrome and Safari) such that page refreshes work as one would expect.

Main logic

Since there is no main loop, we recommend putting general functions (e.g. registration of a listener, starting a Timer, ...) in the screen's On Load function.

Export

We recommend exporting the different bricks (screens, UI components, functions, etc.) individually, in order to make the export granular.

Dependencies

At the moment, Draw supports importing projects into one another. This allows creating one project acting as a library of reusable bricks, which can then be reused across multiple other projects. Versions are not fully functional at the moment, so updates to the library project are immediately reflected in the importing projects as well.

Similarly, double-clicking on an imported function or UI Component will open that function or UI Component for edition in the other project.