User Guide


What is Solvent-LastMile?

LastMile lets business application developers leverage components and code snippets created by other developers to quickly compose applications. In other words LastMile helps business application developers solve the application "last-mile" problem, ie how to quickly create something useful on top of a robust technology stack without getting bugged down in detail.

LastMile is a product meant to allow developers to quickly build business applications. Such applications typically don't have complicated architecture, just a quick way to glue a set of functionality together.

Note that LastMile is built on top of the ConfigNode product, which it self is built on top of the Solvent WebApps product platform. This means the LastMile documentation only covers topics specific to LastMile. Users should consult the ConfigNode and Solvent WebApps product documentations for how to use the platform as a whole.

Have ideas, suggests or thoughts? post to the LastMile GitHub repo.

Component Composition Language.

At the heart of LastMile is the notion of a component composition language. This is simply a schema (JSON) that describes how a given component can be integrated into an application, ie how to compose applications using the component.

Another way to think of it is that a composition language is a developer workflow definition, ie it specifies how an application can be created with a component or set of components.

It is a simple yet powerful concept because it allows the developer who knows the component best, to provide direct guidiance on how to use a component.

Custom Developer Workflow.

A compsition language can be thought of as a specific workflow design for creating an application. While the underlaying frameworks (Vue/React/Angular..etc), have precise and rigid workflows, LastMile allows composition language designs to put different spins on how one should use these frameworks to create applications.

Think of LastMile as a platform that lets a composition language designer to redefine the target framework (Vue/React/Angular..etc) without requiring the developer of the given language to rigidly follow the rules of the target framework, yet the resulting code will conform to the rules of the target framework.

A component developer decides how they wish to design the language for composing their components, there are no hard-and-fast rules for doing this. Every component set will have a different language that describes its possible compositions based on what the component does and how it works.

What a composition language should try to do is capture both the workflow and the resulting code artifacts to produce a given result.

The expectation is that over time, best practices will emerge from the community of component developers and users for how best to design composition languages.

Composition Primitives

With a schema based composition language, a developer can turn any combination of code snippets into primitives for compositions including actual components such as Vue/React/Angular/WebComponents...etc. Any set of code snippets that can be combined into solid functionality, can be thought of as composition primitives.

For instance a site such as bootsnipp host code snippets, such snippet can serve as primitives for a composition.

Usecase Analysis

In order to develop a composition language, a component or snippet developer needs to think thoroughly about how users will ultimately want to integrate the component. The more comprehensive this analysis is, the more robust the resulting composition language is likely to be.

In essence as a component or snippet developer, you basically have to think of as many integration scenarious as possible, and then implement those scenarious with your composition primitives.

This approach is a step or two above an API developer who merely needs to create a robust interface and let the implementor worry about how they will implement the API. With composition language design, the component developer needs to go much further into the implementation process. One could call this forward integration, analogous to a similar process in supply chains.

It means more work for the component developer since they have to do comprehensive use-case analysis in order to develop a set of composition scenarios, however, the benefit is a significant productivity boost for every other application developer who wishes to use the component thus making the component much more valuable.

Authoring A Composition Language

To author a composition language in LastMile, you'll simply author a schema as described in the ConfigNode documentation.

Once a language is authored, it can be imported by users into LastMile as described the ConfigNode documentation.

Composition Languages

They can be designed in whatever way suitable, both for full application structures or for individual components.

How a component developer distributes their components and the composition language is up to them.

Authoring Compositions

Authoring compositions is the act of a user using a composition language schema to create an application.

To author a composition in LastMile, you'll simply create object graphs as described in the ConfigNode documentation.

The composition language/schema will guide the user and auto-fill all code snippets required to compose the application. The user of course can then complete the task of writing the necessary code and/or providing the necessary configuration values to build their application.

The options provided are entirely up the the composition language designer and ultimately comes down to how sophisticated the language is meant to be.

Compiling Compositions.

When a component user/developer creates a composition via the aide of a composition language, the resulting composition would need to be compiled into some resultant code artifact.

Just as there are no hard-and-fast rules for creating composition languages, there are no such rules for creating compilers. The behaviour of a compiler would be entirely based on the language of course and it is up to the component developer to implement the compiler.

A compiler can be implemented either as client side logic, maybe via a loader that processes JSON or it can be server side.

The most crude compiler would be a simple object traversal algorithm that extracts markup from an object graph and concatenate it to be outputed as HTML.

The more sophisticated the composition language, the more sophisticated the compilation process would be.

Here's a very crude example of the previous composition being compiled into an actual VueJS application.

An apps compilation can be done at request time or it can be integrated as part of a standard build process, it is up to implementors to decide.

Rapid Application Development Kits.

All the topics discussed so far boil down to a mechnism for Rapid Application Development (RAD). In this section we'll discuss the idea of RAD Kits, ie the packaging of a schema, its compiler and other artifacts (code or otherwise) so it can be used for rapid application development on the LastMile platform.

A RAD Kit is a package made up of the following elements.

  • Composition Language
  • Composition Language Compiler
  • Supporting artificats (code & static assets)

As with the idea of a composition language, there are no hard-n-fast rules for designing RAD Kits, however based on work that has been done with LastMile, some core ideas seem broadly useful and support for them has been baked into the platform.

Schema Extensions

Schema Extensions as described in ConfigNode documentation are an integral part of creating RAD Kits.

Node Resolution and Rendering

LastMile is based on an object graph based system, in particular the components that are modelled (ex a Vuetify Toolbar) need to be rendered into actual code (ie Vue component tag for instance). A RAD Kit developer needs to implement a resolution algorithm that can render all nodes that make up the application based on their LastMile object graph representation.

The resolution and rendering approach is up to the RAD Kit developer but the RAD Kits available in the LastMile repository provide a guide as to how to go about creating such solutions in LastMile.

Event Handling

Node rendering often may need to be customized as part of a RAD Kit implementation, one example of where rendering needs to be customized can be seen from the layout differences of applications in LastMile and the resulting framework code they generate.

For instance in the Repository example of a Vuetify Tab control implementation, Tab labels are directly specified, there is no extra field call label te represent that data (ie no extra metadata); however the Vuetify code requires tab labels to be syntactically specified in a way that doesn't match the LastMile layout, a RAD Kit implementation needs to account for this in order to render correct code.

Notice in the example below the tab label is specified directly, notice also that the view of the tab is nested within the tab, this obviously differs from how a tab control is constructed in the target framework (Vuetify in this case). The composition compiler is responsible for transforming this object model into correct target framework code.

The easiest RAD Kit implementation is one where the composition language simply mirrors the underlaying code it generates. Better RAD Kits however are those where the developer creates a true composition language that is intuitive but may not model the target framework code that will have to be ultimately generated.

Event handlers during resolution and rendering of object graphs into the target platform artifacts are the way to customize rendering. It is possible to have event handlers invoked for pretty much every step of a node's rendering.

Compilation Directives

schemaExtension.settings.compilationDirectives is where all callback code is set for the Vuetify reference implementation RAD Kit. The resolution algorithm will look up callback code in that field and invoke them if available.

Available Callbacks

  • beforeRender
  • afterRender
  • beforeStartTagRender
  • afterStartTagRender
  • beforeEndTagRender
  • afterEndTagRender
  • beforeChildrenRender
  • afterChildrenRender
  • beforeChildRender
  • afterChildRender

Custom Node Renderers

The Vuetify reference RAD Kit shows an example of core rendering algorithm. This example also shows how the core rendering algorithm can defer to custom renderers to override various steps of the rendering process for a node.

  • startTagRenderer
  • endTagRenderer
  • settingsRenderer
  • childrenRenderer

In addition to callback calls, certain additional directives can be set.

dontRender

dontRender tells the compilation routine not to output the nodes named in that list whenever they are present in the current compilation context, ie even if the field is a renderable node.

dontRenderTypes

dontRenderTypes tells the compilation routine not to output node types in that list whenever they are present in the current compilation context. The node type is either solvent_object_schema or solvent_object_type, with solvent_object_type taking precedence when both are present.

literalContent

literalContent tells the compilation routine to output the fields in that list as they are (ie assuming they are strings) whenever they are present in the current compilation context. For instance a field representing a button's label can be rendered as such during the compilation of the button node. It could also be custom markup.

Prototyping

LastMile relies on modern component based frameworks such as React/Vue/Angular..etc to form the basis for RAD Kits. LastMile allows component developers who use these frameworks to create RAD solutions. The idea of prototyping in this particular context is a way to allow widgets to be turned into types that can then be used just like any other core component type to build applications.

Widgets

A widget is basically any composition of components that can be thought of as serving a certain type of use case. For instance a toolbar that has a search box incorporated into it, where the search box is a separate component can be tought of as a widget, the composition of these two separate components constitutes a use case that is rather common.

Prototyping in LastMile is simply a way to generate a schema (ie composition language) from such implementations so others can reuse and extend the widget for their own applications, ie you turn the widget into a composite component that can be used like any other component.

Application Templates

The set of application templates for Vuetify are implemented as prototypes. In other words the application templates are really just a set of components pre-configured (sidebar, toolbar...), the developer can then customize those initial configurations and of course add more components to complete the application.