Picture of Front end architecture Introduction: Reusable UI components are simple to design with today’s front-end frameworks and libraries. It’s a positive step toward creating maintainable front-end applications. Unfortunately, I’ve discovered that developing reusable components isn’t always adequate. As requirements changed or new requirements arose, my projects became unmaintainable.

Architecture at a high level

We can use a variety of architectural patterns in back-end development. For example, domain-driven development (DDD) and separation of concerns are two concepts that are currently in use (SoC). These two ideas are extremely beneficial to front-end development. In DDD, you strive to group similar features as much as feasible and disconnect them from other groups as much as possible (e.g., modules). While with SoC, we may split logic, views, and data models, for example (e.g., using the MVC or MVVM design pattern). Micro Frontend applications are expected to undertake more heavy lifting in the future. Therefore, we need a dependable, manageable, scalable design because users interact with the front end. Therefore, my preferred architecture is modular and domain-driven. Note that my viewpoint may change in the future, but this is my favorite method. When users interact with our app, the entire application routing directs them to the appropriate module. Every module is enclosed entirely within itself. However, because users expect to utilize only one program rather than several separate ones, there will be some coupling. Its connection is made based on certain features or business logic. The process can share several characteristics between modules. The process can place this logic in the application layer. It implies that each module can communicate with the complex application layer. An excellent example is a setup requiring the client-side API to connect to our server-side rendering or API gateway. We can follow the steps outlined below when examining a project’s structure. First, the app directory contains all the code for the application layer. Next, in the modules directory, each module has its guide. Finally, the components directory contains reusable UI components (such as tables) that do not rely on business logic. Our static base component (e.g., photos) and helper functions are stored in the remaining directories of lib. The functions of helpers can be quite basic. They can assist you in converting things to a specific format or working with objects. However, the lib directory may contain more complicated code. Working with schemas or graphs is no exception (for example, techniques to check for loops in directed graphs).

Filling out the application’s information

We’ve gotten off to a solid start with the high-level and project structure. However, we need more information on numerous elements to build this front-end design. Let’s start with a more thorough architectural diagram, like the one below. I’ve zoomed in on the application layer, as well as a module, in this diagram. Let’s start with the application layer, which is the heart of our front-end application. There are two pieces to the application layer: a store and a client-side API. The store is our application’s global state. This state contains data that multiple modules can access at the same time. Even if the data is no longer required on the screen, it will remain in the database. As you can see, every update request sent to the store can be routed through a logic chain. This is referred to as middleware. This is a pattern that is utilized in Redux, for example. The logging of incoming store requests is a simple example of middleware. Occasionally, data from an external service must be included in an incoming request for the store. To handle this call, we use a Promise in Redux. This might be a public third-party API, or it could be our back-end service. It is sufficient for a single-purpose REST call to use the browser’s fetch API. Constructing an API client definition is good if you wish to utilize the same API for several calls. A basic API client handles external requests, answers, and errors. You can also make it so that it informs you about the request’s status (e.g., loading). On the other hand, more complicated API clients can manage a lot more. Some APIs use a web socket or even a GraphQL API to communicate. We can change all outgoing requests through middleware in more complicated API clients (e.g., add authentication headers). Afterware can change the answer (e.g., changing the data structure). We save the modified response in the client’s cache, which functions similarly to our application store. What’s the difference? We can put any clean code in your application store while the cache simply processes incoming API data. Many front-end applications will communicate with a specific back-end service. Whether it’s a single monolith back-end or an API gateway built on top of a Kubernetes cluster with multiple microservices. However, there are instances when we need to connect to other external services. We can construct a lot of API clients using this approach. A cache, middleware, and afterware can be installed on each API client. Each API client should be able to communicate with different areas of our program. Two of the app’s directories should be recognizable to you by now: API and store. These contain everything relating to the previously mentioned use cases. Static definitions and configurations (e.g., constants) are stored in the config file and are used throughout the application. For JavaScript objects, a schema describes a certain data structure. This applies to both TypeScript and JavaScript. The schemas directory contains all of the application’s generic schemas. Pubsub is a wonderful example of a feature that can be used to extend our front-fundamental end’s component architecture. We can use pubsub to communicate across modules or to manage scheduled jobs. It’s kept in the app directory because it’s important to the application’s core. Finally, there’s the index.js file. All functions and constants from the app directory can be added to this file. The file’s functions serve as an entry point into the application logic.

A module’s architecture

Only the modules remain once we’ve outlined our application layer. The internals of a module is already shown in the full architecture diagram. When an application’s routing directs to a certain module, the module decides how to proceed with the routing. The routing of the module decides which page should be displayed. A page comprises many UI components that the user sees on the screen. In this context, a page is similar to a UI component. It’s a significant UI element. On the other hand, other domain models can interact with components (and actions) but not pages. Nested routing is the only way for pages from various element types to interact with each other. This signifies that the module route is contained within a page from another module. Actions are how components communicate with the application layer. These actions can take a variety of forms. They can be simple JavaScript functions, React Hooks, or Redux functions. You may have minor utility functions that are exclusive to a module. You can either place them in the actions directory or build a specific utils directory for a module in this case. We can have a static source code structure  (e.g., constants or schema definitions) that is solely relevant for our domain models, just like the application layer. That source code structure is then placed in the config or schema directories. We can have query and mutation definitions when dealing with GraphQL. These should go in the gql folder (or a directory with a similar purpose). Add an interfaces.js file while working with an application store for this module. This file explains how to get data from the storage. The index.js is the same as the app directory’s index.js. All the components, actions, and constants available to others are described here.

Communication between modules

Not every domain model requires all of the above directories and files. Some architectural styles, for example, don’t require pages because they merely include components and actions. A ‘files’ module is a perfect example. This module can browse and upload files by combining components and actions. For example, a drag-and-drop area for files transfers the result to blob storage. This may be a reusable component. However, the actual uploading of files is contingent on the service we may employ. We develop a contained module by merging the UI component and the actual upload activity. We turn components into modules when we integrate them with business logic. But how many other modules use the files module’s functional components or actions? A module’s index.js file specifies which components, actions, and constants are available to other modules. As a result, we might use the files module’s file drop-zone or upload action. However, there are occasions when we must pick what we expose to other modules. Will it be an action, or will it be combined into a component? Consider the case of a user drop-down menu. Independent team component library may develop an action that provides us with all of the users from which we can choose from various modules. However, we must now construct a particular drop-down in all other modules. A generic drop-down component might not require much effort. However, in a form, this component might not work. Creating a single User Dropdown functional component that we can utilize would be worthwhile. When something around users changes, we only have to change one component library. As a result, we must sometimes decide whether to expose actions or components. The pubsub is a more complex pattern we can employ between component modifiers. It is impossible to exchange component modifiers using this pattern. However, we can share data. How it works is depicted in the diagram above. This sophisticated pattern should only be used if you wish to go the tiny front-end way or if you have a specific need for it.

Anatomy of UI components

One last level of detail is the architecture of a UI component library, which is currently missing. This was also mentioned in a previous blog article. Looking at this anatomy, you’ll see that some of the notions we use on a larger size are still present. Our users’ first point of contact is the front end. As the number of features in our front-end projects grows, so will the number of defects. On the other hand, our users want no glitches and new features to arrive quickly. This is unthinkable. However, we can only try to do this as much as feasible by utilizing good architecture.

Core concepts:

  • Be technologically self-sufficient
Each team should select and enhance the stack independently of the others. Custom elements hide implementation details while giving others a neutral interface.
  • Team Code Isolation
Even if teams use the same framework, never share a runtime. Create a self-contained, self-contained application. Do not rely on global variables or shared states.
  • Make a list of team prefixes.
When isolation isn’t practicable, use naming conventions. To avoid collisions and clarify ownership, namespace CSS, Local Storage, Events, and Cookies.
  • Custom APIs are preferred over native browser features.
Instead of creating a worldwide PubSub system, communicate via browser events. If a cross-team API is necessary, strive to keep it as basic as feasible.
  • Create a resilient Web design.
Even if JavaScript is unable to execute, the features should be helpful. Use universal rendering and progressive enhancement to increase perceived performance.

Benefits:

  • Increasing the number of teams
My team is made up of developers with a variety of backgrounds and expertise. Some are React experts, while others are Vue.js or Angular experts. Some programmers prefer JavaScript, while others prefer TypeScript. This appeared to be a roadblock at first. The only option was to establish common ground, even if some developers would have to learn new technologies and lose their experience. As a result, we sought a solution and chose the micro frontend architectural approach. In terms of the end outcome, micro frontends are likewise beneficial. This is what our team has discovered after fully adopting this strategy. In reality, having numerous small teams that can utilize whatever technology they choose implies they’ll be less constrained and hence more driven to generate higher-quality code.
  • Using an alternative technology stack
Because micro frontends are made up of small, independent components, each can be built using a different technological stack. This is an incredible feat of strength. First, the initial team can be divided into multiple small groups depending on competence in a certain tech stack while still adhering to the single responsibility concept. Second, because many tech stacks will be used on the same project, hiring new developers will be easy.
  • The pace of development and deployment has accelerated.
Another key point is that using micro frontends has greatly enhanced our frontend development process as a team. The fundamental reason is that, rather than having a large team dealing with inescapable communication overhead, we’re now part of smaller independent teams working on many projects simultaneously, regardless of implementation specifics.
  • It improves the maintainability of your web application.
If you’ve ever worked with large apps, you know how difficult they can be to manage, especially if they’re monolithic and destined to grow large. Micro frontends, on the other hand, use a divide-and-conquer strategy. As a result of using this architecture for your web application, you will be able to make every business need easier to test and maintain.
  • It is the face of frontend development in the future.
Micro frontends have been used by 24 percent of developers, according to the 2020 State of Microservices Report. As a result, an increasing number of businesses are seeing the value of this strategy, and several major frontend application models are expected to follow suit shortly. To put it another way, micro frontends could be the next stage in frontend development.
  • The discipline of front-end architecture:
An architect is a person who creates, plans, and supervises the construction of structures. A frontend architect accomplishes the same thing. However, the end product is a static website. Similarly to how a front-end architect spends more time designing schematics than pouring concrete implementation, the front-end architect is more concerned with developing tools and processes than writing production source code. Let’s look at this definition and see our function as front-end architects.
  • Design
Consider a structure with no discernible front-end architecture matter. The builders in charge of the project were left to make all of the crucial decisions. A stone wall was built, a brick wall was built, a wood wall was built, and a fourth wall was deleted since it was trendy. Although professional designers are still in charge of the website’s overall appearance and feel, the frontend architect matter is in charge of the frontend approach and design system philosophy. The architect establishes a clear picture of what the end product, the code, will look like by establishing a system that all frontend developers will work within. The project has a standard against which to test code once a frontend architect establishes the vision. How could we tell if our code matched the requirement if we didn’t have a design for the final product? A well-designed system will have checks and balances to ensure that all code added to the system contributes value rather than merely bloat.
  • Planning
The planning stage entails laying out the development workflow with a defined design in mind. What steps will a developer take to write a line of code and see it through to completion? In its most basic form, this strategy is FTPing into a server, altering a file, and saving it. Version control, task runners, CSS processors, documentation tools, test suites, and server automation will all be used in most projects. The purpose of a frontend architect is to create a well-oiled machine that allows for rapid and easy setup, gives helpful feedback in the form of linting, tests, and documentation, and minimizes human error when doing repetitive activities.
  • Oversight
It’s never a case of “set it and forget it” in the front-end world. There is no such thing as a perfect or comprehensive design or plan. Clients’ (and developers’) needs may vary and evolve, and a procedure that worked well at one project period may need to be reconsidered later to increase efficiency or minimize errors. The capacity to make those adjustments regularly is a fundamental skill of a frontend architect. Modern build tools simplify altering workflows and distributing those changes to all team members. Some have wondered if being a frontend architect entails taking on a management post and never having to write another line of code. As an architect, I can personally attest to the fact that not only do I create more code, but I also get to write in a wider range of languages and for a wider range of tools. It’s not that I’m writing less code; my target audience has shifted. The end user is the audience of a frontend developer, whereas the developers themselves are the audience of a frontend architect.
  • Using an Architectural Methodology
The Like other disciplines before it, the front-end world has had to wage a prioritization battle. While we can’t picture somebody starting a skyscraper without consulting an architect first, it is exactly how most huge web projects begin. There is a slew of excuses: we don’t have the funds. We are unable to do so due to a lack of time. We’ll make those decisions after we’ve completed all of the designs. Or, even worse, you have no excuse; you were assigned to the project months after the design was completed and development was well underway. You only have a few months to make a stack of HTML appear to you as a stack of PSDs thrown over the wall. This is not a good technique for building a scalable and long-lasting static website. We feel that a few important frontend decisions need to be taken at the start of a project as frontend architects. These choices are too complex to apply later in the development process, or the cost of making the wrong choice is far too high. Our job is to help shape the visual design, platform development, and infrastructure setup to best match the needs of our envisioned architecture once those decisions have been made. Without the help of a frontend architect, projects may be forced to choose between rewriting designs, platforms, or infrastructure or forcing frontend devs to make do. From personal experience, I can tell you that betting on the former is usually a bad bet.

Conclusion:

Whether or not you employ a micro front-end pattern depends on your business case. Micro front-end architecture is not required if you have a small project and team. On the other hand, significant projects with distributed teams and a large number of requests profit greatly from developing micro-frontend pattern applications. As a result, many large firms adopt micro-frontend pattern architecture extensively nowadays, and you should also consider it.