Project Graph in Nx

Introduction

In this blog post, we will explore the benefits of using the project graph tool provided by Nx to visualize applications and libraries in a monorepo. Enterprise applications consist of multiple apps and libs, which can quickly become complex and hard to navigate. The project graph feature in Nx comes to the rescue by offering a visual representation of dependencies, making it convenient for team members to work with the codebase, even if they joined the project at a later stage.

Ensuring Code Quality with Project Graph

The project graph not only helps in visualizing the monorepo but also aids in maintaining code quality. It enforces linting rules defined in the eslint configuration file, ensuring that any new library added to the workspace adheres to the specified rules. To maintain order and prevent chaos, you can refer to a good initial set of rules below (github link here).

Overview of the Monorepo

nx graph command outputs graph below. Our monorepo contains two main applications: trucks and admin. Additionally, each application has associated e2e test projects, totaling four apps. We also have two libraries: shared-common-ui and trucks-feature-trucks. Let’s dive into more details about these libraries.

Understanding Library Tags

shared-common-ui library contains the BannerComponent, which is shared and used by both applications. The library is appropriately tagged with scope:shared and type:ui making it self-explanatory. The tags array is defined in the project.json file generated by Nx schematics. Specifying tags helps enforce module boundaries and brings clarity to the development process. Nx suggests this convention based on their project delivery experience.

Tags scope:shared, type:ui, and others mentioned in this post adhere to time-tested conventions. Consistency is key in any software development endeavor. By adopting these conventional tags, you ensure that every team member understands the purpose and context of different libraries. However, they are arbitrary strings. And you can come up with your own tags if you’d like.

Code Organization and Naming Conventions

In trucks-feature-trucks library, we are implementing the feature of displaying a list of trucks for the user. Following the convention of grouping libraries per scope, we named it trucks-feature-trucks. The name may not be the best, but adhering to the convention makes it more comprehensible.

Properly Tagging Libraries

Container/smart components like TrucksComponent belong to libraries tagged with type:feature

TrucksComponent is going to have a child component named TruckDetailsComponent. This child component serves as a presentation/dumb component, it communicates with its parent via Inputs/Outputs. Such presentation/dumb components are placed in libraries tagged with type:ui.

When making http calls to retrieve data, it’s essential to separate data access logic from components. This code should be placed in lib(s) tagged with type:data-access. These libraries are responsible for handling data retrieval and manipulation, ensuring a clear separation of concerns within the application.

For commonly used methods that format strings (e.g., capitalize First Last name, format currency, or handle dates in a specific way), creating libraries tagged as type:util is recommended. Methods within type:util libraries solve common issues and are excellent candidates for reuse. To express this intention clearly, consider adding the tag scope:shared to these libraries. This way, developers can easily identify and utilize shared utility functions, further promoting code efficiency and consistency.

Benefits of Conventional Mapping

Following this conventional way of mapping requested features into well-defined units of code brings several advantages. It aids in maintenance, code ownership, testing, accurate estimations, and faster delivery with higher confidence. As a result, it boosts the return on investment for the project.

Conclusion

In conclusion, utilizing the project graph tool provided by Nx simplifies the management of monorepo projects significantly. It not only offers a visual representation of the codebase but also ensures adherence to linting rules, maintaining code quality. By following consistent naming and tagging conventions, developers can enhance the scalability, organization, and maintainability of the application, leading to a more productive and successful development process.