The principle of code reuse, or “don’t repeat yourself”, is a leading benefit of Beacon Platform. Not only does code reuse improve consistency throughout the organization, it provides an enhancement to both reliability and maintainability. In support of this, Beacon has introduced the Fragments Framework to our Glint application development toolset that enables faster and easier creation of scalable and reusable code modules.
What are Fragments?
Fragments are reusable UI (user interface) building blocks with well-defined inputs and outputs that communicate via a publish/subscribe mechanism (with @send/@receive API decorators). The goals are to simplify reuse and state management, while enforcing an explicit separation of functionality.
To start with, each Fragment should be as granular as possible, and expand only within the defined scope. Following this practice also improves testability, from initial development through ongoing maintenance and regression tests. Fragments can be completely standalone or embedded within other Fragments for more complex relationships. In Beacon’s architecture, Fragments are used to encapsulate components of the view, along with associated model and controller behavior.
In this screenshot, each colored box represents a Fragment, that is, a UI building block containing an encapsulated model, view, and controller. By leveraging the MVC design pattern, the internal representation of the data is separated from its presentation to the user, and any manipulation and validation logic.
- Using a Gromit dependency graph object, the MODEL holds the Fragment’s state, containing the data and rules, independent of the UI.
- The VIEW is what the user sees, whether that is a chart, table, or interactive form.
- And the CONTROLLER is how the user interacts with the MODEL, accepting inputs and converting them to the appropriate commands.
While each Fragment maintains its own MVC instances, they can notify the larger application of any updates through an action-based communication mechanism.
How Fragments communicate
Fragments are designed to be reusable across applications, and often reusable multiple times within an application. Each Fragment starts with a globally unique `key` value that it uses to publish its actions, with each `action` type clearly describing an event and payload. Certain actions, such as a `GromitNodeUpdate`, can also emit an `id` as a shorthand identifier for the change. An event could be clicking on a checkbox, and the payload would be its boolean state.
The Fragment `key` is a special keyword argument to allow any subscribers to filter incoming communication based on a Fragment’s identity. Other Fragment keyword arguments can be passed into Fragments to customize the view and/or behavior.
For example, one Fragment may send an action of BookTrade, with appropriate details of instrument and market. When the appropriate receiving Fragment sees this event, it performs the defined actions and responds with TradeBooked. Multiple Fragments may watch for this event, with one updating the user’s screen and another with RecordTrade to update the history in the database.
All communication between Fragments is done via the publish/subscribe model, with no direct references or connections between Fragments. This reinforces the independence of Fragments, and helps to build better unit testability and maintainability.
Communication between Fragments is achieved by sending and receiving actions in the form of lightweight Python dataclasses. Traditional (non-Fragment based) Glint applications can also communicate with Fragments via their models and controllers by using the same @send/@receive API.
The following Fragment code asserts if a specific market is open. The `id` specified here allows any subscribers to listen to changes on the Gromit node by filtering on MarketEnvSelectorIds.IS_MARKET_OPEN. When this Gromit node below recalculates, as a result of its dependencies, an action is published with the payload being the current calculated result.
self.CurrentDate() == self.MarketDataBaseDate()
and self.ns[“/Env/Market”].MarketDataDate() < self.MarketDataBaseDate()
Interested Fragments can filter the actions they care about by a Fragment `key`, `action`, or `id`. For example, this Gromit node below subscribes to the MarketEnvSelectorIds.IS_MARKET_OPEN action. When that action is published, this Gromit node will then be set to the action’s payload, overwriting the default `True` that is returned.
Combining Fragments into blocks
Applications often want to use multiple Fragments together. Customizable groups known as BlockFragments provide an easy way for applications to consistently use the same set of functionality. For example, a toolbar can be defined as a BlockFragment and include the appropriate set of controls. As new toolbar features are developed and added to the block, all relevant applications will automatically get the new elements, without requiring any additional development.
Eventually, large applications can be made up almost entirely of Fragments, each of which encompasses their own UI, with encapsulated model and controller components. Each Fragment can be reused, substituted, tested, and maintained as a discrete component. Related Fragments can be gathered into blocks, and then blocks assembled into an application.
Sometimes you may want to subscribe to a different but related action. For example, traders in Asia, Europe, and North America may want to watch different market openings. These types of preference-like changes are readily enabled via Fragment settings. Settings can be set on a domain, application, or even user level, providing varying degrees of granularity to suit all situations. When exposed via the settings modal, users can modify the values at runtime which are persisted to the database for future sessions.
Additionally, with a very small amount of configuration you are able to swap out Fragments, no matter how deeply nested they may be within an app. This can be done at either the application or domain level, allowing you to customise existing apps and make them more bespoke to fit your needs.
Testing and debugging Fragments
Loosely-coupled architectures like the Fragments Framework are great for code reuse, but without supporting tools, they can pose challenges during testing and debugging. Beacon provides an array of helpers to conduct tests, capture errors and exceptions, and visualize Fragment communications. These tools enable you to quickly and easily assert specific actions, record activity, display error and traceback details, and view selected publishers and subscribers.
Above, our suite of FragmentDevTools is designed to help you better visualize the communication happening behind the scenes. This is achieved by exposing a view on all actions sent from within the models and controllers that make up your app.
From the Developer Tools window, you can also turn on the Fragments Inspector shown above. When enabled, borders appear around all the Fragments within the active application, allowing you to see the composition of the UI more clearly. Each Fragment also displays a floating info icon that, when hovered over, displays a popover window with useful information about each Fragment.
Enhancing buy and build
Glint and the Fragments Framework are key parts of Beacon’s buy and build strategy, providing a wide range of standard models and functionality, while giving clients the tools they need to easily add their own proprietary code. Fragments make assembling applications an almost trivial effort, while supporting highly-scalable, modular apps with multiple views and dynamic state changes. Backwards compatibility with non-Fragment code means that you can gradually work Fragments into your architecture without having to rewrite existing functions. Coupled with Beacon’s dependency graphs, these tools make it easy to write, test, deploy, and reuse code, and take full advantage of the performance promises of cloud computing.