Software Architecture

MaXX Interactive Desktop Architecture

  Top ▶▶

Goals and Requirements

Architecture

The MaXX Desktop's global architecture is based on the following five(5) principles in order to ensure the above goals and requirements are met.

1.SOLID Principles

The SOLID principles were first conceptualized by Robert C. Martin in his 2000 paper, Design Principles and Design Patterns. These concepts were later built upon by Michael Feathers, who introduced us to the SOLID acronym. And in the last 20 years, these 5 principles have revolutionized the world of object-oriented programming, changing the way that we write software.

The SOLID design principles encourage developers to create more maintainable, understandable, and flexible software by shifting their focus on the functionality rather than the low-level details. Consequently, as an application grow in size, adeveloper can reduce its complexity and save his sanity.

The following 5 concepts make up our SOLID principles:

  1. Single Responsibility
  2. Open/Closed
  3. Liskov Substitution (not so much used anymore)
  4. Interface Segregation
  5. Dependency Inversion

While some of these words may sound daunting, they can be easily understood with some simple code examples. In the following sections, we'll take a deep dive into what each of these principles means, along with a quick Java example to illustrate each one.

2.Clean Architecture

Clean Architecture is a way of developing software, such that just by looking at the source code of a program, you should be able to tell what the program does. The programming language, hardware and the software libraries used to achieve the objective of the program should become irrelevant. The aim of Clean Architecture is to make the following sentence possible: ‘Hey, the arrangement of directories tells me this is a shopping cart app. I don’t know which programming language or software library is used. I need to go into the directories and find out.’ 

3.Multi-Layered Architecture

The architecture is inspired from multi-layered Enterprise class system where each layer defines precise responsibility and its application/service (a.k.a. component) exposes functionalities or behaviors. At the heart of the architecture are clear communications channels between components and their respective layers and well defined data-contracts exposed by each of them.

Overall, this design strategy will improve robustness of the Desktop experience, provide solid foundations to build upon, allow a clean separation of concern, modularity and overall re-usability. Another way to see MaXX's architecture design is to look at micro-services architecture, but from a Desktop application perspective where each layer of the architecture is composed of specialized  micro-services performing specific tasks.

Layered Architecture by responsibility

The MaXX Desktop architecture is divided into three (3) responsibility layers from which MaXX's application and service can be built.  Below are the layers in question with a short description for each:

image-1610806846406.png

The diagram below illustrates the current MaXX Desktop architecture

The MaXX Desktop Architecture is planning tree(3) means of inter-layer communication mechanisms ensuring security and maintaining separation of responsibility.

CLI or  Command Line Interface - allows a command-line driven client/server like interaction where the  Client CLI initiate a CLI session with the Server CLI counterpart and sends one or many CLI commands. The communication is obviously synchronous and based on a request/response semantic that is redirected through the POSIX standard I/Os of the CLI Client application.  Nothing prevents the Server CLI to emit an event during the processing of a CLI command. The CLI mechanism is great to provide a fire-and-forget interaction semantic.

HTTP or  REST Interface - allows a Web/REST driven client/server like interaction where the  HTTP Client  sends a request to a HTTP Service. The communication is again synchronous and based on a request/response semantic using HTTP/S and predefined data types. Nothing prevents the HTTP Service to emit an event during the processing of the request. This mechanism is great to provide a a full features API style integration.

Messaging - allows synchronous, asynchronous or event-driven interactions where a producer create a message and send it to a destination via a communication channel.  One or many consumers are at the receiving end of that channels allowing either point-to-point or publish-subscribe delivery mechanism. The payload of a message can either contain real data (passing by value) or provide a secured shared-memory  location where the data can be retrieved (passing by reference).

A message is an item of data that is sent to a specific destination. An event is a signal emitted by a component upon reaching a given state.  An event can be transported via a message, not the way around

image-1610806862698.png

The diagram below illustrates the inter-layer communication mechanisms.

 

4.Message-Driven

The Message-Driven architecture provides low dependency, no tight coupling and robust communication between components and services. MaXX Links Framework will provide all the necessary features and abstractions to supports modern multi-protocols synchronous/asynchronous messaging communications. Things such as: load balancing, tasks distribution, high availability, decoupling client-server with simple consumer/producer semantic are all possible (and in many cases rather simple to implement) through Messaging.

Messaging also introduce the ability to support a polyglot code base where components integration are performed via high performance messaging channels while supporting both local and distributed environment. It does not really matter anymore in which programming language the component/service is written, as long as it supports Messaging ØMQ and complies to predefined data-contracts.

Through MaXX Links, any C/C++/Python or Java application can be integrated into the MaXX Desktop environment or new modern features can be added to an existing application very easily.

The Messaging approach as been proven to yield excellent values by allowing ongoing improvement of components, total decoupling between producers and consumers and bring robust asynchronous and event-driven capability. As long as data-contracts are respected, changes documented+communicated, with versioning and backward compatibility supported, this is one of the best way to do inter-application communication.

The MaXX Interactive team bring more than 2 decades of real world expertise in High Performance Messaging Systems. That must count for something :)

5.Shared Memory

The use of Shared Memory for inter-process communication is not new, but it is not often considered due to its complexity and step learning curve.  Shared Memory does not apply for distributed computing. Although Shared Memory has been part of X Windows (X11) as an Extension (MIT-SHM) since the early days of X11 as a was a very efficient mechanism for sharing information between local X11 applications, many choose the easy road with excuses such as 'the penalty is small' over doing things right and the most efficiently possible. The general idea is to avoid the X11 taxes (network stack and round-trips to the X Server)  as much as possible,  when sharing large data-sets like images.

This practice is used throughout the MaXX Desktop, its applications and in the MaXX Vue thanks to MaXX Links.

Using Shared Memory in local Messaging is also a very important aspect of the architecture and help addressing the efficient and smart technical requirements. In this context, Shared Memory is used to avoid unnecessary copy/duplication of information when sending or receiving messages to and from another application. Instead of passing the information by value into the message's payload, only a secured Shared Memory locator is part of the payload (passing by reference).

We utilize this 'passing by reference' strategy quite heavily in GPES (General Purpose Execution Service)  as a very efficient way to consume computation results without the net-impact. In some cases, all the work/computation, memory allocation and display are all performed by the GPU. 

 

Sources
https://blog.flexiple.com/clean-architecture-build-software-like-an-artisan/
https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html

https://blog.cleancoder.com/uncle-bob/2020/10/18/Solid-Relevance.html

 

  Top ▶▶

Layered Architecture Details

◀◀ Top ▶▶

Layered Architecture by responsibility

As we previously saw, the MaXX Desktop layered architecture is divided into three (3) responsibility layers from which a MaXX aware application or service can be built. 

Here are the layers in question with a list of components (applications/services).

User Experience & Presentation

This layer is performing visual-oriented tasks like displaying User Interfaces and/or capturing user's input. Components and/or applications on that layer are communicating with the Desktop Support layer for computation and resources access via MaXX Links, a high performance messaging  library for inter-process communication.

User Experience & Presentation Components

Component Name
Responsibility
5Dwm  Enhanced Motif Window Manager.
Toolchest Desktop Application Menus and Launcher.
IconCatalog Visual and Interactive Application catalog using vector based icons.
File Manager - fm Visual and Interactive File Manager using vector based icons.
Desktop Desktop/Workspace manager.
 
Desktop Support

This is layer provides desktop computation support while also providing an abstraction-layer to various Back-end Services, where most of the actions is taking place. 

The exposed computations falls into  the following category:

Desktop Support Components

Component Name
Responsibility
MaXX Launcher  Smart Application and Service Launcher.
MaXX Scope Smart Application and Service Orchestration for multi-core systems with CPU cores affinity & partitioning in mind.
MaXX Session User Desktop Session Manager with snapshot capability.
MaXX GPES General Purpose Execution Service - Centralize and Unified Task Execution Environment.
 
Back-end Services

This layer is where low-level system work is performed. For better security, this layer can only communicate with components/services from the Desktop Support layer.  Among the functionalities exposed by this layer are: hardware and application monitoring (MaXX Monitor), file-system accesses(MaXX File Service) and configuration management (MaXX Settings).

Back-end Services Components

Component Name
Responsibility
MaXX Settings  System Settings and User Preferences Management Service.
MaXX Monitor Centralized Hardware and Application monitoring with metric aggregation.
MaXX File Service High Performance and Multi-threaded File System Service.

 

updated - 2021-01-19

Work in progress...

 

◀◀ Top ▶▶

Software Patterns & Design Strategies

◀◀ Top ▶▶

Introduction

This document will describe some of the software design patterns that are used throughout the MaXX Desktop Architecture, how they provide reliable solutions to common requiring problems and most importantly, shed some light on some selected strategies for building different types of desktop applications.

As per the Architecture document, we now know that MaXX Desktop was designed using a multi-layered and message-driven architecture which follows the SOLID principles and Clean Architecture. Each layer of the architecture hosts applications or services with specific characteristics, behaviors and responsibilities.

We see here a great opportunity for defining reusable and common design strategies for building high performance visual applications, dependable desktop support and back-end services. By promoting proper use of design patterns we can bring up the code quality, maintain a strong, robust and yet flexible architecture, increase predictability and lowers risks associated to changes. All this translates into better user experience and a software system that can evolve over time without suffering from a middle-age crisis every two-three years...

A few words on Clean Architecture first. Our intent is to follow what makes sense for the MaXX Desktop and to provide concrete scenario in order to avoid ambiguity.

So good news folks, this document will try to address do just that, and maybe pick your curiosity and learn a different way to write code.

image-1611851879030.png

Design Patterns

This section will go over some of the design patterns used in MaXX Desktop and how we put them together to build reusable components.

Legend

Throughout the document, we will use the following arrows to illustrate the relationship type between objects.  For example,  AB, reads A is using B as a dependency type relationship. The arrow marks the direction of the dependency. 

The legend below will help the reader to better understand the intent and relationship between objects.

image-1611848368117.png

image-1611851879030.png

Observer

The Observer pattern is one of the twenty-three well-known "Gang of Four" design patterns describing how to solve recurring design challenges in order to design flexible and reusable object-oriented software. An Observer simply observe objects, named the Observable (or subject in generic term), by maintains a list of those and notifies them automatically of any state changes. This behavior is usually implemented from an Interface rather than inherited. It is mainly used for implementing local or distributed event handling systems, in "event driven" software.

image-1611841853494.png
Diagram illustrates the Observer/Observable in context with ModelView and View.

Observable

The Observable pattern facilitates event-driven behaviors where Observers stands ready to react from an Observable state change in a non-blocking and asynchronously way. The Observable emits a state change notification to all its subscribed Observers. Upon reception of such notification, the Observer can react accordingly to that state change. This behavior is usually inherited instead of been implemented from an Interface.

image-1611851879030.png

View

The View design pattern is one of the most popular one and easy to understand. But sadly the View pattern as been poorly used throughout the years, and on an epic scale. The View is simply a device specific delivery mechanism that displays the content of a ModelView, captures user's input, and send those inputs to a Controller. The View must implement the Observer pattern in order to receive data modification notifications from the ModelView.
 

ModelView

The ViewModel pattern is defined as a simple Observable values container used by the View. The ModelView should not contain business logic and the data transformation responsibility is passed to the Presenter. The ModelView only contains simple values like Strings, flags and others which are already transformed values ready to be displayed by the View. The ModelView must extend the Observable pattern  in order to emit data modification notifications to its View. Those notifications are triggered when the Presenter sets the ModelView data.

image-1611842003438.pngDiagram illustrates the use of the ModelView, View, Presenter and Controller Patterns.

Presenter

The responsibility that characterized the Presenter pattern is to reformat a ResponseModel object received from an Interactor into a ModelView. Upon reception, the Presenter transforms the received ResponseModel into a viewable representation as a ViewModel.
 

Controller

The Controller pattern definition in our architecture, is an object that handles inputs from a View, converts them into RequestModel and send them to the Interactor via a Boundary. The inputs are usually events generated from the View. In term of responsibility and features, that it. Nothing else.

image-1611851879030.png

ResponseModel

The ResponseModel pattern is an object that represents the output that is sent to the user of the system, usually in response to the input or due to other triggers such as a scheduled time or an UI event happening. The ResponseModel is a DTO (Data Transfer Object) containing values such as text and numbers.
  

RequestModel

The RequestModel pattern is an object that represents the input data from a View. The RequestModel can be describe as a generic representation of the input data required for a computation or function call, which usually contains simple data types like numbers and text. The RequestModel is also a DTO.

image-1611775139886.pngDiagram illustrates the interactions between Interactor, Boundary, Presenter and Controller Patterns.

Boundary

Our Architecture is designed in layers and follows the Clean Architecture and SOLID Principles, so that peripherals, services, computational resources, and data provider components such as MaXX Settings can be swapped as requirements change. For this reason, the core components of the Desktop applications, i.e. Entities and Interactors, never talk directly to those components. Rather, Interfaces called Boundaries are made so that calls are made across them. One side of the Boundary makes calls and expects a form of response that is agreed upon. The other side of the Boundary receives the calls and returns those responses. Both sides usually do not know who is on the other side. They just act upon the requests and responses. Such design also allows components to be tested individually by using mock / fake components across the boundaries.

Let’s talk about boundary types.

Input Boundary
This is the Boundary between the input system and the Interactor. The input system does not deal directly with the Interactor. Instead an Interface is offered along with a set of method calls that receives a request. These calls promise that the input will reach the Interactor properly and that the use case will be executed.

Output Boundary
This is the Boundary between the output system and the Interactor. This makes sure that the Interactor does not know how the response will be shown to the user. The Interactor passes along the data inside the response. But formatting the response is upon the output system on the other side of the Boundary.
 

Interactor

Interactor is a design pattern that could be describe as a specialization of the Command pattern with the specific responsibility of fulfilling a specific use-case. The Interactor receives a RequestModel from its Input Boundary Interface, then sets things in motion like an orchestra director, coordinate the execution of a use case. There must be one Interactor per use case in a properly designed system/application. It is not uncommon to see the use of the Service pattern in conjunction with the Interactor. This allows an even better separation of responsibilities.

image-1611851879030.png

Entity

The Entity pattern focus is on the domain data, its validation rules and business logic that creates an output in response to an input. After receiving input from the user, the Interactor uses different Entities in the system to achieve the output that is to be sent to the user. The Entity, like the Interactor is using a Boundary interface to access its data source through a Gateway. Remember that the Interactor itself should NEVER directly contain the logic that transforms input into output.
 

Gateway

The Interactor or Entity will often need to interact with an external system, or a Desktop Support or Back-end service, for that purpose we encourage using the Gateway pattern. A Gateway has to cross boundaries as well, but toward another system or service, and it is fair to call then Boundary. However it would add ambiguity to the intent, therefor, Gateway it is. Gateway is a specialization of Boundary which acts as a Reverse-Proxy and instead of hard-wiring the code specific to an external service access inside an Entity, a Boundary Interface is made. The Entity calls the methods over a Gateway's boundary interface and the components on the other side will use the specific service. This approach makes the code very modular and plug-and-play.

image-1611847150722.png

Diagram illustrates the interactions between Entity and an external service via a Gateway.


image-1611851879030.png

Design Strategy

Here are some more complete examples on how we put together well defined patterns and build reusable design strategies for the MaXX Desktop that are robust, easy to test, and flexible. Below, we demonstrates one strategy per actual architectural layer and some re-usability scenarios.

Here are the assumptions from which the strategies are put together.

 
Inter Application Communication

One very apparent advantage of proper architecture and design pattern selection is that we can see right away some similarities in the strategies.  A first use case is the "inter-application communication" where the same recipe is use over and over. This is good!  Now we can focus on building that strategy right with re-usability and flexibility in mind for both how UX and Desktop Support applications are communicating to the outside world.  The only variant in both use cases is the "consumer" in front of the Gateway.

We could even push one step further and adapt that strategy for Back-end Services data access mechanism by replacing the MaXX Links component with either a database like access for filesystem, MaXX Monitor metrics and MaXX Settings CLI. Write once, use everywhere :)

image-1611856559149.png

Boundaries

A second use case is with the "Boundaries" where the same recipe is used repetitively, with only one small variant for UX. This reusable strategy will serve as a massive improvement over the old MVC pattern (which we do not use) and as INGRES for both Desktop Support and Back-end Services. Suddenly the entire Desktop code base is considerably reduces in complexity and size thanks to reusable strategies and a clean architecture.

image-1611858313183.png

User Experience (UX)

From what we now know, most of the UX applications (except the window manager and a few very specialized use cases) are much simpler to build. For example, the User Preferences Panels will be reusing over 50% of their EGRESS code, which leaves us with the View, ModelView, Presenter, Interactor an Entry.

Here's how we see one good reusable strategy for visual application on the UX layer.

image-1611854758611.png

Desktop Support

The Desktop Support services are even simpler to build. Al of our attention should be directed to the Interactors which are use cases execution orchestration. The INGRESS components are quite similar with small variant in the messages routing and EGRESS are identical.

Here's how a good reusable strategy for Desktop Support service looks like.