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
- Team Code Isolation
- Make a list of team prefixes.
- Custom APIs are preferred over native browser features.
- Create a resilient Web design.
Benefits:
- Increasing the number of teams
- Using an alternative technology stack
- The pace of development and deployment has accelerated.
- It improves the maintainability of your web application.
- It is the face of frontend development in the future.
- The discipline of front-end architecture:
- Design
- Planning
- Oversight
- Using an Architectural Methodology
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.Priyadharshini K.R is a professional blogger with over 3 years of experience who works at Squash Apps. She is dedicated to providing high-quality content to help clients get more visibility on the search engine result pages. She works hard to boost her clients’ online presence through various content writing services. Hailing from Erode, she is passionate about helping people understand content marketing through easily digestible materials.