One of the most effective software engineering approaches involves separating the user interface (UI) or frontend from the business logic or backend, especially when it comes to developing embedded devices. This practice makes it far easier to code for a single specific functionality versus coding an overall product.
This is something we address in ICS' rapid-development solution GreenHouse by ICS, in which we encapsulate our software architecture best practices derived from hundreds of successful projects.
GreenHouse's approach to application separation uses an interchangeable communication API between individual services on our stack, from the UI through to the backend. Through these service abstractions, we're able to leverage testable interfaces that are used to test the application at every level.
In this blog you’ll learn:
What’s involved in separating the UI from business logic in your next Qt project
Some key benefits of this approach, including parallel development, modularity, enhanced testability and accelerated development
How we ensure Qt applications have proper architectural separation
Separating the UI from Business Logic
Separation of concerns, the fundamental principle behind application separation, has been taught and practiced in the software domain since Edsger W. Dijkstra coined it in his 1974 paper On the Role of Scientific Thought . The concept is simple: divide a computer program into sections that each address a separate and distinct concern of the application. This so-called "concern" can be something as simple as naming a class that is instantiated or as broad as platform specific details (Figure 1).
Figure 1: Separation of concerns allows developers to focus on code relevant to only that specific feature or layer .
Typically, separation of concerns boils down to creating well-defined interfaces between each layer, service or module in your application's codebase. With GUI applications, it's easy to see this separation as directly enforced by the concept of frontend vs backend development.
In contrast, a web server contains backend code that handles business logic, database access, user management and external communications (Figure 2). It uses different languages and frameworks to manage the application's state. As an application grows in size and complexity, this separation between the client and server, with some common API between them, becomes necessary to manage the overhead associated with different and mixed functionality.
Figure 2: Typical web application approach for high-level separation of concerns.
When it comes to Qt development, an example of separation of concerns comes from the Model-View-Delegate (MVD) concept. MVD, a slight tweak on the more common Model-View-Controller paradigm, is a design pattern used in Qt applications that isolates and decouples different components. MVD is an application of the separation of concerns. (For more on MVD read Exploring Model-View Design With Qt Quick and Avoid Pitfalls When Developing a Qt Model-View-Delegate Project.)
Benefits of This Approach
Among the benefits that come with application separation is parallel development, where developers can work semi-independently on different aspects of the same application. For example, QML developers would work on the UI only (screen layout, navigation, themes, components, etc.) and C++ developers would work on the backend.
Of course the backend for larger applications can get quite complex as it must handle application logic, database access, filesystem access, settings, authentication, hardware interactions, external communication dependencies, etc. However, the same concept of application separation can be extended to each of these tasks as well.
Software modularity, directly attributed to the separation of concerns principle, is yet another benefit of dividing your application into separate functional areas. Software modularity in the case of a Qt application could mean allowing new frontends to be swapped out for different use cases. Perhaps you have one frontend that is simply used as a tester for backend communication and application logic.
Or, flipping that on its head, you could develop a backend that's capable of mimicking property and state changes that influence the behavior and layout of the frontend — a test harness. Separating your application appropriately allows for modules to be upgraded, reused, and independently developed.
By hiding each module's implementation behind an interface, developers can work on different sections of the code without having to know the internals of other areas. They don't need to make any changes to other sections, and only go so far as to suggest modifications in the shared interface. Each application layer can be exposed in different ways to the other layers — meaning, developers are free to update things piecemeal instead of large, costly overhauls and/or loss of functionality.
Creating a Maintainable Application
One of the key goals for every software project is to create a maintainable application. Using the separation of concerns principle to create a modular application starts with separation between the UI and the business logic . In the Qt world, this means to write the application's UI code in QML because declarative languages like QML are well-positioned for defining UIs.
Ensuring UI and business logic separation accelerates development as layers can be developed in parallel, and it reduces the "brain burden" for a developer working on one part of the stack. There is less risk that a future technology change may disrupt project timelines. In addition, a more modular codebase gives architectural flexibility options, such as swapping out your backend for a test harness or mocked logic that tests the UI and visual application functionality.
ICS' Preferred Architecture
ICS has been a leader in custom software development for more than 30 years. Naturally, we've developed our own patterns and paradigms that accommodate our lessons learned. In the realm of application creation in the embedded and mobile space, we've developed an architecture that works well in the majority of cases. This architecture is one of the many benefits GreenHouse provides.
In our approach, we separate each service with a swappable communication layer (WebSockets or ZeroMQ) while generating stable APIs on both ends. This provides the necessary abstractions for defining each service uniquely (Figure 3).
Figure 3: GreenHouse Services Architecture, ICS' architecture for embedded code.
As Figure 3 indicates, our UI in QML communicates with our state machine service. The state machine handles complex rules, as determined by UX designers and developers, that efficiently determine transitions and handle changing views.
The backend handles all application logic and connections to external dependencies. It communicates changing values directly to the state machine which then decides how it should direct the UI. Along with the backend, a number of plugins can be used to manage disparate functionalities of the application. These are stubbed out by our code generator and provide access to generated objects that are used to move data up and down the stack.
Access to Testable Interfaces
Perhaps the greatest benefit this flexible architecture provides is access to testable interfaces between each service. Because these are well-defined and understood, the code we generate comes complete with googletest-based unit tests for each interface. Besides providing code coverage for our runtime, these tests provide access points for developers to insert custom unit tests for any object in the system.
It is our architectural modularity that exposes opportunities to exploit these testable interfaces. For example, with every generated application, GreenHouse creates a "Simulator" — a GUI that is used to automate system-level testing. This effectively replaces our backend and allows us to test our UI before we have the entire backend finished.
With the Simulator, we can fully exercise all areas of the application by changing the properties, signals and methods associated with each backend object. We can even script these changes and play them back. With this simulation framework in place, a GreenHouse application developer can initiate screen transitions, state changes and visibility controls,as well as change data that is presented to the UI (as if coming from a real backend).
ICS takes testing seriously. We ensure support for unit and system-level testing by respecting the separation of concerns principle and generating tests at all boundaries.
To learn more about separating the UI from business logic, register for our April 23 webinar on the topic. For more on the benefits of GreenHouse, check out GreenHouse by ICS Helps Software Engineers Turn UX into Reality and GreenHouse and Qt Design Studio Together Deliver a Full Solution.