This set of patterns is built around developing a consistent architecture for application clients that is compatible with a cloud-native application development style. It may seem odd that we devote an entire chapter of this pattern language to front-end patterns that may not depend on the cloud at all, but there are several really good reasons for this that are worth discussing.
The first reason clients are important for cloud-native applications is business value. One of the hardest parts of a cloud adoption, and the part that most IT teams fail at, is demonstrating real value to the business in tangible terms. One of the easiest ways to demonstrate value to the business is by improving the user experience (UX) of customers or associates to either produce more direct sales, simplify interactions and improve efficiency, or to enable value-add services that increase revenue. Most product managers have a long list of desired improvements to their user experience, but these are often hampered by issues below the user interface layer itself, which may include problems with both technology and process. Thus, demonstrating the value of cloud as part of improving user experience often helps sell the effort and expense of converting to cloud. What’s more, once the user experience code is on the cloud, it’s often easier to either move or reimplement since it is already both close to the user (reducing latency) and the front end is cleanly separated from the complexities of the application’s back-end. As a result, most companies tend to start with client-facing applications as their first forays into cloud technologies.
However, there is a second issue, even bigger but more nuanced, that companies must consider when rebuilding or refactoring existing applications, and that is the cost of training teams on new front ends. One of the most easily overlooked costs in adopting a new system is training cost and the costs to the business of lost productivity when users must become productive on a new system. As a result, training and productivity costs are an important consideration when deciding how to segment a larger monolithic application into smaller pieces for refactoring. Essentially, in a minimum viable product (MVP) approach, the word “viable” becomes much more important when considering front-end refactoring and reimplementation. In an article on the Thoughtworks Blog , Meaghan Waters points out three potential approaches that may be helpful:
- End user segmentation: This approach is useful when you have several disjointed (heterogenous) groups of users. If you can focus on the experience of a single user community at a time, then that gives you a potential way of segmenting a large monolithic application and moving it into the cloud as a series of smaller applications, each broken off from an original monolithic system.
- Process segmentation: This approach is useful when your users are more homogenous. In this case, you look for boundaries of business processes and rewrite each individual business process. Handoffs between users, even of the same general user group, are good indications of the boundaries of each process. Likewise, if a process comes to completion or transitions to a new stage in a point in time, this is also a good place to break up a larger interface into smaller aspects.
- “Competitive” system: With this less common approach, you replace one existing system bit by bit with a new competitive system, perhaps for new customer acquisition while existing customers use the current system. In this way, you can start new customers with a less complete system and let them gain more features over time.
Any of the three segmentation approaches can work; choose the one that fits the current application best. All three require you to build new front ends that fit the approach as part of an architectural process that also refactors the back-end business logic to correspond with the front end. Whenever you refactor the front end and back end simultaneously, be careful to keep the two ends separated, to keep business logic separated from UI logic, and to encapsulate that business logic in the back end. In a 2021 Twitter thread , Betsy Haibel illustrates a particular trap that is easy to fall into:
“Web Frontends and their backends are logically one product. You get a lot of feature requests that require changes to both. When this happens, developers have a choice. They can build the feature “the right way” and put the business logic in the backend and go through the moderate pain of shipping both changes, or they can put it all in the frontend. Over time, the weight of all these changes puts ever more business logic in the front end.”
Practically, what this means is that you should NEVER assign a front end and its associated back-end to separate teams. Both should be built by a single Stream Team that owns the saga that describes the feature as a whole. This applies to all of the patterns that we are describing. Teams should never be split vertically by role (front-end and back-end developer) but should instead be organized by feature split in any of the ways illustrated in the Waters article referenced above. The patterns in this section are all intended to be applied within that organizational framework.
One of the fundamental principles that sits behind all of the advice we will provide in these patterns is that there should be a distinct and firm boundary between UI code and business logic. While this advice is so old that giving it again here may seem almost quaint, the problem is that in practice, many teams fail to follow this principle. Perhaps one of the best examples of this architectural principle is Alistair Cockburn’s Hexagonal Architecture , sometimes called the “Ports and Adapters” architecture. In this approach, he separates the core business functions of an application from the four different styles of interaction that humans or other systems have with the application (see below). These are user interface, persistence, administration and notification. The concept is that the core business logic is isolated from these interactions by specialized “adapters” that operate on more abstract “ports” for each style of interaction.
If we only concern ourself with the user interface portion of this approach, then we realize that what this approach was presaging was the separation of the code of a Domain Microservice from the code that calls the Domain Microservice. In fact, the entire Microservices Architecture is an architectural and organizational mechanism for enforcing this separation.
This concept of an application core surrounded by different types of adapters for different purposes illustrates a key concept that the patterns in this section implement. The different UI styles we will introduce are all meant to be separated from the business logic they rely upon. In a sense, they are all “adapters” in a Hexagonal Architecture.
What this discussion illustrates is that there are a handful of basic principles about client architecture that both ties back to many of the other principles and patterns we have discussed so far in this book, and that also inform the patterns that we will introduce in this section. These principles are:
- You want to separate UI logic from business logic.
- You need a consistent view of your users regardless of how you interact with them. Ideally this consistent view should be supported by the domain services that your application design provides (requiring you to explicitly model the user, which is natural in many domains).
- You want to meet your users where they are, in a form that is best suited to the particular type of interaction. This requires realizing that users are not homogenous, nor do they always interact with an organization in the same way all the time. This means your application logic must be accessible in a variety of form factors and through multiple channels.
- You need to enable teams to work on complete end-to-end business processes; the same teams should be responsible for both the front-end and back-end parts of this business process (e.g., the user experience in all its forms and the domain services that implement the business process).
The patterns in this section all stem in one form or another from these basic principles. The patterns include the following:
- Multimodal Architecture is the root pattern of this section and describes the overall structure of applications that must work across multiple types of user interaction. This structure is important because most modern applications have multiple types of user interfaces that all must come together in a consistent user experience that may cross mobile applications, web applications, and various types of social media interaction.
- Interaction Model is a way of explicitly capturing and representing each particular style of user interaction. An Interaction Model is a way of representing an interaction with a user in a particular technology stack or channel. It allows for separation of user interface and business logic, while encouraging commonality across interactions of different types.
- Web Applications are often the default interaction model option for small, simple applications. This approach combines server-side rendering (using a template language) with simple forms-based navigation. It is well suited for interacting with users who are on less-than-ideal hardware platforms, and places few requirements on the user’s side beyond opening a URL in a Browser application.
- Micro Frontends are a way to avoid recommitting the mistakes of monoliths within a client-side application built using the Single Page Application architectural approach.
- Native Mobile Application describes the most customizable solution for consumer-facing client applications. While Web Applications are still enormously common, and are often the best solution for internal corporate applications, consumers now expect the bulk of their interactions with many companies to take place through a dedicated mobile application.
- Social Media Plugins are a way to interact with the now ubiquitous social media platforms. Given how much time users spend on social media platforms, organizations would be wise to ensure that they can provide services to their customers and employees through these platforms.
- Chatbots are a common and important user interface approach for specialized types of user interaction such as customer support. Chatbots are almost never the sole means of user interaction with a client, but are instead an important component of other client interaction styles such as mobile applications, web sites and social media plugins.
- Document Generators are a way of interacting with humans through a more than 500-year-old technology – the printed page – and its digital equivalent. Many business processes still require or use physical or digital documents for legal, regulatory compliance or other purposes. A Document Generator provides a way to create these documents and transmit or store them.
- Command Line Interface is a way of interacting with the user at an operating system command line. This is important because many simple types of actions the user may want to take in order to automate interaction with the system are more easily done at an OS command line than through other mechanisms.
- Public API is the mechanism for making your application services available to the world of developers outside of your own team. They are important wherever you have interactions with third parties such as suppliers, business partners or governmental entities or if you want to build an ecosystem of independent developers around your services.
The relationships between the patterns in this section are shown below: