Get Even More Visitors To Your Blog, Upgrade To A Business Listing >>

How I approach and structure Enterprise frontend applications after 4 years of using Next.js

Posted on Sep 9 • Originally published at josemukorivo.com In the fast-paced world of frontend development, staying ahead of the curve is essential for building successful enterprise applications. After four years of using Next.js and a powerful toolkit that includes Tailwind CSS, TypeScript, TurboRepo, ESLint, React Query, and more, I have gained valuable insights and best practices to share with fellow developers. In this blog post, we will explore how to architect and structure frontend applications for large-scale enterprises, maximizing performance, maintainability, and scalability.NB This article expresses personal viewpoints, and the methods I advocate may not be suitable for your specific situations.When it comes to architecting frontend solutions for enterprise-scale applications, having a well-defined set of principles can be the North Star that keeps your development efforts on course. In this section, I'll share the guiding principles that have emerged from my experience with Next.js in enterprise environments.Principle: Divide and ConquerIn the sprawling landscape of enterprise applications, Code can quickly become an unruly beast. Embrace modularity and componentization to break down your frontend into manageable pieces. Think of Components as Lego blocks, each serving a specific purpose. This not only enhances code reusability but also simplifies maintenance and collaboration within your development team. Consider not only the segmentation of your Application into smaller components, but also the possibility of breaking it down into smaller standalone applications. This is an area where tools like Turbo repo excel.Principle: Keep Your Codebase NeatTo maintain code sanity, adhere to the Separation of Concerns (SoC) principle. Ensure that your components focus on their respective responsibilities, whether it's rendering UI, handling business logic, or managing state. This segregation not only makes code easier to understand but also facilitates testing and debugging.Principle: Plan for GrowthEnterprise applications aren't static; they evolve. Design your frontend architecture with scalability in mind. This means selecting patterns and tools that can accommodate increased traffic, data volume, and feature complexity. Next.js's scalability-friendly design can be a valuable ally in this endeavor.Principle: Craft with CareCode is your product's foundation. Prioritize maintainability and code quality from day one. Enforce coding standards, conduct code reviews, and invest in automated testing. A well-maintained codebase is not only easier to work with but also less prone to bugs and regressions. At my work I recently developed a component library and a basic style guide to enforce standards on our frontend applications. Don't mind the docs they are not yet done😂.Principle: Inclusive from the StartAccessibility is a non-negotiable aspect of modern web development. Make it a default practice from the beginning. Ensure your application is usable by all, regardless of disabilities. Leverage Next.js's support for accessibility standards and tools to create inclusive user experiences. I use tools like Radix UI for some components that require accessibility like the tabs, dropdowns, etc.Principle: Speed MattersEnterprise users expect snappy experiences. Prioritize performance at every turn. Optimize assets, minimize unnecessary requests, and leverage Next.js's performance features like automatic code splitting, streaming with suspense and image optimization. A fast application not only pleases users but also positively impacts SEO.Principle: Guard Your CastleSecurity should be woven into the fabric of your frontend architecture. Protect against common vulnerabilities like Cross-Site Scripting (XSS) and Cross-Site Request Forgery (CSRF). Stay vigilant with security updates and best practices, and consider Next.js's built-in security features as an extra layer of defense.Principle: Think GloballyIn our interconnected world, thinking globally is essential. Implement internationalization (i18n) and localization (l10n) from the outset to cater to a diverse user base. Next.js provides excellent support for these features, making it easier to create multilingual applications.These guiding principles form the bedrock of an effective enterprise frontend architecture when working with Next.js. They act as a compass, ensuring that your development efforts align with the demands of large-scale applications, making them robust, maintainable, and user-friendly. In the following sections, we'll delve deeper into how these principles can be translated into actionable strategies and best practices.In React, organizing your project with a well-thought-out folder structure is crucial for maintainability and scalability. A common approach is to arrange files based on their functionality and purpose. Here's the sample folder structure I typically use for my applications:src/components: This directory contains your UI components. It's further subdivided into ui for generic UI components and shared for components that might be reused across different parts of your application.src/modules: This directory houses your application's different views or pages. Each module might have its own folder, containing subdirectories for API calls, components and utility functions.src/pages: If you are using Next.js this folder should only be used as the entry point to your application. No business logic should reside here. Components in in the pages folder should only render pages from the modules folder.src/modules/ProductsPage: This module is related to products, and it contains subdirectories for API calls, components (like ProductItem and ProductsStatistics), and utility functions (filterProductsByType).src/lib: This folder may contain utility functions that can be converted later into packages that is used across multiple parts of your application. It's different from src/utils which may contain utility functions that do not make sense to convert into packages later.src/styles: This directory holds global styles (global.css) and possibly other style-related files.src/public: This folder contains static assets that don't go through the build process. It might include images, fonts, and the index.html file.src/consts, src/types: These directories likely contain constants and TypeScript type definitions respectively.src/hooks: This directory may house custom hooks that are used throughout your application.eslintrc.js: This is a configuration file for ESLint, a popular JavaScript linting tool. It's used to enforce coding conventions and catch potential errors in your code.The tsconfig file is configured such that if you for example want to import a Button component you can do it like so import { Button } from '@/components/ui'. Below is a snippet of how to configure that from tsconfig.json.The conventions I follow are inspired by this guide here. I highly recommend you read it and code snippets below are coming from that guide.Although determining the optimal name can be challenging, try to enhance code readability and maintain consistency for future developers by adhering to established conventions:Singular, capitalized with const assertion and optionally satisfies type (if there is one).Camel casefilterProductsByType, formatCurrencyA name starts with the capital letter T TRequest, TFooBar (similar to .Net internal implementation). Avoid (popular convention) naming generics with one character T, K etc., the more variables we introduce, the easier it is to mistake them.In application development, it's common practice to leverage third-party tools to avoid unnecessary duplication of work. Here are some of the packages I utilize when building scalable applications.React Query is highly beneficial in managing data fetching and synchronization in complex enterprise applications. It provides a unified approach to fetching data from APIs, caching, and handling mutations. In enterprise settings, applications often need to interact with multiple APIs and services. React Query can streamline this process by centralizing data management and reducing boilerplate code.React Context is instrumental in managing global state across various components without the need for prop drilling. This is particularly valuable in enterprise applications where shared state, such as user authentication or preferences, needs to be accessible throughout the application. I generally reserve the use of React Context or other state management tools as a last resort. It's advisable to minimize reliance on global state. Instead, aim to keep your state closer to where it's specifically required.Cypress is an excellent tool for end-to-end (E2E) testing. In enterprise applications, ensuring that critical workflows and features function correctly across different screens and components is paramount. Cypress is by far my favourite tool. Whenever my tests passes it gives me confidence that the code I introduced did not break the application. As enterprise applications evolve, it's crucial to conduct regression testing to catch any unintended side effects of new code changes. Cypress facilitates this by automating the testing process.React Testing Library is essential for unit and integration testing of React components. In enterprise applications, verifying that individual components work as expected is crucial for a robust application. React Testing Library allows for thorough testing of each component in isolation, as well as in conjunction with others.NextAuth.js simplifies the implementation of authentication and authorization in Next.js applications. In enterprise settings, secure user management is non-negotiable. Enterprises often employ Single Sign-On (SSO) solutions to streamline user authentication across multiple applications. NextAuth.js supports various SSO providers, making it an excellent fit for enterprise authentication needs. NextAuth.js also offers the flexibility to implement custom authentication flows.I have a blog here that shows you how to customise default User model in NextAuth.js with TypeScript using module augmentation.This is also my favourite tool. Turbo Repo is a valuable tool for managing monorepos. In large enterprise applications, codebases can be extensive, with various modules, services, and shared code. Turbo Repo aids in organizing, versioning, and deploying these codebases efficiently. In enterprise settings, code sharing across different teams and projects is common. Turbo Repo enables effective code sharing, allowing teams to collaborate on shared libraries and components.Storybook allows developers to isolate UI components and showcase them in a controlled environment. This makes it easy to demonstrate how individual components look and behave without the need to navigate through the entire application. In large enterprise applications, different developers or teams may be responsible for different parts of the UI. Storybook provides a centralized platform for showcasing and discussing UI components, fostering efficient collaboration and ensuring a consistent design language. Here is a sample component library I developed and documented using storybook.(It's still a work in progress btw)In an enterprise context, these tools collectively provide a comprehensive toolkit for building, testing, and maintaining large-scale applications, addressing critical aspects like data management, state handling, testing, authentication, and code organization.When I develop reusable components like inputs, dialogs, etc I try to follow some best practices.Let's try some best practices for developing a Button component together and you will see that its more than just the visual design.Ensure that your button component is designed to be reusable across different parts of your application. It should be flexible enough to accommodate various use cases.Provide props for common customization options like size, color, variant (e.g., primary, secondary), and disabled state. This allows developers to easily adapt the button to fit different UI contexts.Implement proper accessibility features such as aria-label, aria-disabled, and focus management. This ensures that users of assistive technologies can interact with the button effectively.Use semantic HTML elements (e.g., ) for your button component. This enhances accessibility and SEO, and ensures proper behavior across different devices.All these best practices we are following enforces us to write predictable code. If you develop a custom button component, make it work and behave like a button. You will see from the example component we will write together that I try to include all props a button can take by extending the native button.If the button can potentially lead to an error state (e.g., submitting a form), provide a way to handle and communicate these errors to the user.Write unit tests to verify that the button component behaves as expected in different scenarios. Test cases should cover various props and event handlers.Document the usage of the button component, including available props, event handlers, and any specific use cases. Provide examples and code snippets to guide developers. This is where storybook shines.Test the button component in different browsers to ensure consistent behavior and appearance.Versioning and Changelog:If the button component is part of a shared library, implement versioning and maintain a changelog to keep developers informed of updates and changes.For my components I usually have files like these. Button.tsx, Button.stories.tsx, Docs.mdx, Button.test.ts. If you are using CSS you may have something like Button.module.csscomponents/ui/Button.tsxThis is the main component and the cn function merges the classes and handles conflicts. It's a wrapper around the tw-merge library.components/ui/Button.stories.tsxThis file has the button stories for storybook.components/ui/Docs.mdxThe stories file is OK for documenting how the component works but a markdown file can have more extensive documentation.The conventions I used to develop the Button component is the same convention I try to follow for all my components.Have a design system of some sort, weather its an open source solution or you spin up your own.Make TypeScript your friend. Use types to your advantage, use it to enforce how you want people to consume your components. A good example on this is on our Button component. It has 2 props leftIcon and rightIcon. We have used typescript to make sure that only one of these is set otherwise it errors to the developer.Document your code and components. Use tools like storybook.Have some sort of a style guide to make sure that you speak the same language with your team.Write dump code. Keep your codebase straightforward and focused. Each piece of code should have a single, clear purpose.Understand how things work under the hood. I published an article here after I found out how React checks if two values are the same.We've explored some of the methods and tools I utilize. Although I haven't covered all the tools at my disposal, I suggest identifying what suits your particular requirements. It's advisable to stick to technologies you're skilled in, rather than adopting something solely for its novelty.In the end, clients are most concerned with the final product, not the specific technologies you employ. Whether it's React, Vue, or another tool, prioritize the use of tools and workflows that enable quick deployment for the benefit of your users.Templates let you quickly answer FAQs or store snippets for re-use. Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment's permalink. Hide child comments as well Confirm For further actions, you may consider blocking this person and/or reporting abuse 0ro - Aug 31 Mysterio - Aug 21 Ed Stephinson - Aug 30 Ashish Kumar - Aug 18 Once suspended, josemukorivo will not be able to comment or publish posts until their suspension is removed. Once unsuspended, josemukorivo will be able to comment and publish posts again. Once unpublished, all posts by josemukorivo will become hidden and only accessible to themselves. If josemukorivo is not suspended, they can still re-publish their posts from their dashboard. Note: Once unpublished, this post will become invisible to the public and only accessible to Joseph Mukorivo. They can still re-publish the post if they are not suspended. Thanks for keeping DEV Community safe. Here is what you can do to flag josemukorivo: josemukorivo consistently posts content that violates DEV Community's code of conduct because it is harassing, offensive or spammy. Unflagging josemukorivo will restore default visibility to their posts. DEV Community — A constructive and inclusive social network for software developers. With you every step of your journey. Built on Forem — the open source software that powers DEV and other inclusive communities.Made with love and Ruby on Rails. DEV Community © 2016 - 2023. We're a place where coders share, stay up-to-date and grow their careers.



This post first appeared on VedVyas Articles, please read the originial post: here

Share the post

How I approach and structure Enterprise frontend applications after 4 years of using Next.js

×

Subscribe to Vedvyas Articles

Get updates delivered right to your inbox!

Thank you for your subscription

×