Nx Monorepo - Using Atomic Design to Organize Component Libraries
Learn how to organize your Nx Monorepo for Component Libraries using Atomic Design Principles
The Nrwl Team directs Architects and contributors in their monorepository for enterprise systems in the book Enterprise Monorepo Angular Patterns, by Nitin Vericherla & Victor Savkin. Angular Architects expand on the organization of this structure with Domain Driven Design. In practice, these guidelines felt to miss the mark on a few things pertaining to Angular components with organizing library types around component libraries and design systems.
The beauty of contributing to a monorepository eases of sharing components across multiple projects. The tech debt teams typically incur reduces dramatically for component updates.
In order to better organize the base guidelines provided by both Nrwl and Angular Architects, Atomic Design structures the organization of the web in a language ubiquitous to relevant parties, giving understanding to build experiences from a base set of presentational components to components that mutate and update state.
While the book Atomic Design doesn't necessarily touch an Angular context, I will guide how to better organize a combination of Nrwl's suggestions for component libraries, features, module boundaries with tagging, Angular Architects Domain Driven Design, and Atom Design.
Goals
- A Common Design Language
- Atomic Design - Organizing The Elements
- Module Boundaries
Free Your Developers
Nx Monorepo Starter alleviates developers from re-inventing deployments on popular Google Cloud Services, standardizes on libraries, and saves time for scaffolding projects.
DownloadA Common Design Language
At the base of an web experience, brands expect their website to project different color schemes and language to their demographic. This includes consistency universal icons, consistent styled elements, and page layouts to navigate and accomplish different workflows.
As architects, developers, designers, and subject matter experts share a common goal, allow the user of the software to accomplish their goal quickly. Nothing speaks more to the confidence of a consistent experience. Having an unfamiliar disjointed experience increases the exit rate for pages, as users race to the close button. So each of the stakeholders per this experience need to speak the same language and become familiar with universally known concepts, in this case, Atomic Design borrows from chemistry.
As teams start building experiences, each person can identify the pieces of UI that compose different elements and components. There should be no surprises in composition of components, and each of these should be branded to the different themes, creating a common design language.
Love Web Development?
Angular, Google Cloud, C#, Node, NestJs?
Atomic Design - Organizing Elements
Keeping with the chemistry metaphor, web page elements can be broken down into smaller units of HTML code. While the last two layers deviate from the chemistry metaphor, these layers house the components and elements in their instances. We'll walk through each layer.
Atoms
At the base level, Atoms cannot be broken down any further. This works in conjunction with the design system as all other components will be composed of Atoms. In the case of the web, imagine these as HTML buttons
, sections
, header
, or footer
. Within each of these HTML elements, the design should cover the states in which exist for CSS purposes. For example, a button
has different states such as hovered
, enabled
, disabled
, pressed
, or primary
/ secondary
. Events should be emitted for the consuming components to handle. In the case of a button
, a click
event would need to be emitted.
In the context of an Nx Monorepo, these components exist in the shared
directory that all other component libraries can access.
<button [ngClass]="cssClass" [disabled]="disabled">{{ text }}</button>
Guidelines for Atoms
- Does not call data services and no business logic
- Rely
@Input()
properties for variations - Use a SCAM approach for each element. This will allow tree shaking and smaller bundle sizes.
- Does NOT consume other libraries in the monorepo
- Shared for all UI projects
- Design / Branding Library
Molecules
A composition of atoms forms a molecule. Molecules align with presentational components in Angular. Think of Angular Material components such as Form Fields or drop downs.
These components display differently with configuration but aren't tied to back end services. The configuration services inject into components for formatting and theming in case there's a need for a blanketed type styling .
Examples of these molecules include @ngserveio/material-forms and @ngserveio/navigation. Each of these libraries wrap different atom and molecules elements from Angular Material into a reusable components.
In the monorepo, molecules should be logically organized. If all molecules exist in one project, consider using a SCAM approach. For instance, Angular Material combines its components into single modules. If you do happen to organize into one project, be aware that any changes will cascade as nx affected
recognizes changes in the project graph.
Guidelines for Molecules
- Does not call Data Services
- Can be a mix of Atoms and other Molecules
- Uses UI Services and Configuration
- SCAM approach for Tree Shaking and Bundle Size
- Shared for all UI projects
Organisms
The next level, Organisms start to take form. These act alongside data services for retrieving and mutating data from events such as changing routes, clicking a button inside the organism, or listening to other events.
Organisms parallel smart components. They act upon external and internal events, communicating and responding.
An example of an organism includes an autocomplete that returns the results from a data service. As the user types, events emit and the data service listing the results below the textbox.
Organisms align best in feature libraries, although there may be cases sharing across all domains makes sense.
Guidelines for Organisms
- Composed of Molecules and Atoms
- Responsible for handling Molecule and Atom Events
- Communicates with Data Services for reading and writing data
- Can exist as a Page and be used as part of a Template
- Shared within a domain of the monorepo
Templates
Deviating from the chemistry metaphor, the next layer focuses on the content layout. Templates consist of organisms, atoms, and molecules. It acts as the skeleton for a page.
Templates align in the monorepo at a shared level or specific to a domain. These libraries separate between all other libraries and consumers utilize the template libraries components as top level component on a route.
const routes: Route[] = [
{
path: '',
component: TemplateComponent,
children: [
{ path: 'faqs', component: FAQComponent },
{ path: 'contact-us', component: ContactUsComponent }
]
}
]
Guidelines for Templates
- Composed Organisms, Molecules, and Atoms
- Responsible for events from the composed components
- In the HTML markup, should have one or many
<router-outlet>
components. - Shared within the monorepo at a shared or domain level.
Pages
The most specific element of Atomic Design are Pages. The composition of template, organism, molecules, and atoms function together. The Page manages the actions of the components contained inside it. Dependent on the routing, pages could exist as organisms working independently with its own data source.
In the monorepo, Pages suit best in a feature module and is referenced in routes. Below the ContactUsComponent and FAQComponent are specific page components.
const routes: Route[] = [
{
path: '',
component: TemplateComponent,
children: [
{ path: 'faqs', component: FAQComponent },
{ path: 'contact-us', component: ContactUsComponent }
]
}
];
Guidelines for Pages
- Consumes data services
- Can be composed of Atoms, Organisms, and Molecules
- Exists in the defined routes.
- Not shared as this is the most specific tied to a feature
Module Boundaries
Abiding by the guidelines above, Nx equips developers with safeguards to keep standards in place. Those safeguards emerge as module boundaries. These help prevent a mix of a data-access
library, e.g. one that communicates with REST services, to import a component library. Since Atomic Design helps layer this, boundaries should be layered like below.
In the scope of Atomic Design, Atom libraries should know nothing of other atoms, molecules, organisms, molecules, templates, or pages. As these libraries layer upon one another, the bottom layers should know nothing of the top layers.
Conclusion
Atomic Design provides a framework to which all stakeholders speak a ubiquitous language for building user interfaces.
Atoms - Branding elements that cannot be broken down further and exist in their own design/brand library
Molecules - a composition of atoms and molecules that exist in a logically organized library like forms or navigation.
Organisms - data driven components and a composition of molecules and atoms.
Templates - composed of Atoms, molecules, and organisms that are the house for pages. Includes router outlets in its templates. Exist in either a shared library or domain.
Pages - Essentially organisms existing within a template, driven by data services. Lives in a feature library.