docs(core): structure docs on creating libraries (#2948)
* docs(core): structure docs on creating libraries * docs(docs): update structure docs Co-authored-by: Isaac Mann <isaacplmann+git@gmail.com>
This commit is contained in:
parent
680541b89f
commit
2c7bcbdd74
@ -788,6 +788,21 @@
|
||||
"name": "Structure",
|
||||
"id": "structure",
|
||||
"itemList": [
|
||||
{
|
||||
"name": "Creating Libraries",
|
||||
"id": "creating-libraries",
|
||||
"file": "shared/workspace/creating-libraries"
|
||||
},
|
||||
{
|
||||
"name": "Library Types",
|
||||
"id": "library-types",
|
||||
"file": "shared/workspace/library-types"
|
||||
},
|
||||
{
|
||||
"name": "Grouping Libraries",
|
||||
"id": "grouping-libraries",
|
||||
"file": "shared/workspace/grouping-libraries"
|
||||
},
|
||||
{
|
||||
"name": "Using Tags",
|
||||
"id": "monorepo-tags",
|
||||
@ -1650,6 +1665,21 @@
|
||||
"name": "Structure",
|
||||
"id": "structure",
|
||||
"itemList": [
|
||||
{
|
||||
"name": "Creating Libraries",
|
||||
"id": "creating-libraries",
|
||||
"file": "shared/workspace/creating-libraries"
|
||||
},
|
||||
{
|
||||
"name": "Library Types",
|
||||
"id": "library-types",
|
||||
"file": "shared/workspace/library-types"
|
||||
},
|
||||
{
|
||||
"name": "Grouping Libraries",
|
||||
"id": "grouping-libraries",
|
||||
"file": "shared/workspace/grouping-libraries"
|
||||
},
|
||||
{
|
||||
"name": "Using Tags",
|
||||
"id": "monorepo-tags",
|
||||
|
||||
33
docs/shared/workspace/creating-libraries.md
Normal file
33
docs/shared/workspace/creating-libraries.md
Normal file
@ -0,0 +1,33 @@
|
||||
# Creating Libraries
|
||||
|
||||
Like a lot of decisions in programming, deciding to make a new Nx library or not is all about trade-offs. Each organization will decide on their own conventions, but here are some trade-offs to bear in mind as you have the conversation.
|
||||
|
||||
## Should I Make a New Library?
|
||||
|
||||
There are three main benefits to breaking your code up into more libraries.
|
||||
|
||||
### 1. Faster Commands
|
||||
|
||||
The more granular your libraries are, the more effective `nx affected` and Nx's computation cache will be. For example, if `libraryA` contains 10 tests, but only 5 of them were affected by a particular code change, all 10 tests will be run by `nx affected --target=test`. If you can predict which 5 tests are usually run together, you can split all the related code into a separate library to allow the two groups of 5 tests to be executed independently.
|
||||
|
||||
### 2. Visualizing Architecture
|
||||
|
||||
The `nx dep-graph` command generates a graph of how apps and libraries depend on each other. If most of your code lives in a few giant libraries, this visualization doesn't provide much value.
|
||||
|
||||
### 3. Enforcing Constraints
|
||||
|
||||
You can enforce constraints on how different types of libraries depend on each other [using tags](/{{framework}}/workspace/structure/monorepo-tags). Following pre-determined conventions on what kind of code can go in different types of libraries allows your tagging system to enforce good architectural patterns.
|
||||
|
||||
Also, each library defines its own API, which allows for encapsulating logic that other parts of codebase can not access. You can even use a [CODEOWNERS file](https://help.github.com/en/github/creating-cloning-and-archiving-repositories/about-code-owners) to assign ownership of a certain library to a user or team.
|
||||
|
||||
## Should I Add to an Existing Library?
|
||||
|
||||
Limiting the number of libraries by keeping code in an existing library also has benefits.
|
||||
|
||||
### 1. Consolidating Code
|
||||
|
||||
Related code should be close together. If a developer can accomplish a task without moving between multiple different folders, it helps them work faster and make less mistakes. Every new library adds some folders and configuration files that are not directly contributing to business value. Nx helps reduce the cost of adding a new library, but it isn't zero.
|
||||
|
||||
### 2. Removing Constraints
|
||||
|
||||
Especially for rapidly evolving code, the standard architectural constraints may just get in the way of experimentation and exploration. It may be worthwhile to develop for a while in a single library in order to allow a real architecture to emerge and then refactoring into multiple libraries once the pace of change has slowed down.
|
||||
57
docs/shared/workspace/grouping-libraries.md
Normal file
57
docs/shared/workspace/grouping-libraries.md
Normal file
@ -0,0 +1,57 @@
|
||||
# Grouping Libraries
|
||||
|
||||
Libraries should be grouped by _scope_. A library's scope is either application to which it belongs or (for larger applications) a section within that application.
|
||||
|
||||
## Move Schematic
|
||||
|
||||
Don't be too anxious about choosing the exact right folder structure from the beginning. Libraries can be moved or renamed using the [`@nrwl/workspace:move` schematic](/{{framework}}/plugins/workspace/schematics/move).
|
||||
|
||||
For instance, if a library under the `booking` folder is now being shared by multiple apps, you can move it to the shared folder like this:
|
||||
|
||||
```bash
|
||||
nx g move --project booking-some-library shared/some-library
|
||||
```
|
||||
|
||||
## Example Workspace
|
||||
|
||||
Let's use Nrwl Airlines as an example organization. This organization has two apps, `booking` and `check-in`. In the Nx workspace, libraries related to `booking` are grouped under a `libs/booking` folder, libraries related to `check-in` are grouped under a `libs/check-in` folder and libraries used in both applications are placed in `libs/shared`. You can also have nested grouping folders, (i.e. `libs/shared/seatmap`).
|
||||
|
||||
The purpose of these folders is to help with organizing by scope. We recommend grouping libraries together which are (usually) updated together. It helps minimize the amount of time a developer spends navigating the folder tree to find the right file.
|
||||
|
||||
```txt
|
||||
apps/
|
||||
booking/
|
||||
check-in/
|
||||
libs/
|
||||
booking/ <---- grouping folder
|
||||
feature-shell/ <---- library
|
||||
|
||||
check-in/
|
||||
feature-shell/
|
||||
|
||||
shared/ <---- grouping folder
|
||||
data-access/ <---- library
|
||||
|
||||
seatmap/ <---- grouping folder
|
||||
data-access/ <---- library
|
||||
feature-seatmap/ <---- library
|
||||
```
|
||||
|
||||
## Sharing Libraries
|
||||
|
||||
One of the main advantages of using a monorepo is that there is more visibility into code that can be reused across many different applications. Shared libraries are a great way to save developers time and effort by reusing a solution to a common problem.
|
||||
|
||||
Let’s consider our reference monorepo. The `shared-data-access` library contains the code needed to communicate with the back-end (for example, the URL prefix). We know that this would be the same for all libs; therefore, we should place this in the shared lib and properly document it so that all projects can use it instead of writing their own versions.
|
||||
|
||||
```txt
|
||||
libs/
|
||||
booking/
|
||||
data-access/ <---- app-specific library
|
||||
|
||||
shared/
|
||||
data-access/ <---- shared library
|
||||
|
||||
seatmap/
|
||||
data-access/ <---- shared library
|
||||
feature-seatmap/ <---- shared library
|
||||
```
|
||||
110
docs/shared/workspace/library-types.md
Normal file
110
docs/shared/workspace/library-types.md
Normal file
@ -0,0 +1,110 @@
|
||||
# Library Types
|
||||
|
||||
There are many different types of libraries in a workspace. In order to maintain a certain sense of order, we recommend having a small number of types, such as the below four (4) types of libraries.
|
||||
|
||||
**Feature libraries:**
|
||||
|
||||
Developers should consider feature libraries as libraries that implement smart UI (with access to data sources) for specific business use cases or pages in an application.
|
||||
|
||||
**UI libraries:**
|
||||
|
||||
A UI library contains only presentational components (also called "dumb" components).
|
||||
|
||||
**Data-access libraries:**
|
||||
|
||||
A data-access library contains code for interacting with a back-end system. It also includes all the code related to state management.
|
||||
|
||||
**Utility libraries:**
|
||||
|
||||
A utility library contains low-level utilities used by many libraries and applications.
|
||||
|
||||
---
|
||||
|
||||
## Feature Libraries
|
||||
|
||||
**What is it?**
|
||||
|
||||
A feature library contains a set of files that configure a business use case or a page in an application. Most of the components in such a library are smart components that interact with data sources. This type of library also contains most of the UI logic, form validation code, etc. Feature libraries are almost always app-specific and are often lazy-loaded.
|
||||
|
||||
**Naming Convention**
|
||||
|
||||
`feature` (if nested) or `feature-\*` (e.g., `feature-home`).
|
||||
|
||||
**Dependency Constraints**
|
||||
|
||||
A feature library can depend on any type of library.
|
||||
|
||||
```treeview
|
||||
libs/
|
||||
└── my-app/
|
||||
└── feature-home/
|
||||
└── src/
|
||||
├── index.ts
|
||||
└── lib/
|
||||
```
|
||||
|
||||
`feature-home` is the app-specific feature library (in this case, the "my-app" app).
|
||||
|
||||
---
|
||||
|
||||
## UI Libraries
|
||||
|
||||
**What is it?**
|
||||
|
||||
A UI library is a collection of related presentational components. There are generally no services injected into these components (all of the data they need should come from Inputs).
|
||||
|
||||
**Naming Convention**
|
||||
|
||||
`ui` (if nested) or `ui-\*` (e.g., `ui-buttons`)
|
||||
|
||||
**Dependency Constraints**
|
||||
|
||||
A ui library can depend on ui and util libraries.
|
||||
|
||||
---
|
||||
|
||||
## Data-access Libraries
|
||||
|
||||
**What is it?**
|
||||
|
||||
Data-access libraries contain code that function as client-side delegate layers to server tier APIs.
|
||||
|
||||
All files related to state management also reside in a `data-access` folder (by convention, they can be grouped under a `+state` folder under `src/lib`).
|
||||
|
||||
**Naming Convention**
|
||||
|
||||
`data-access` (if nested) or `data-access-\*` (e.g. `data-access-seatmap`)
|
||||
|
||||
**Dependency Constraints**
|
||||
|
||||
A data-access library can depend on data-access and util libraries.
|
||||
|
||||
---
|
||||
|
||||
## Utility Libraries
|
||||
|
||||
**What is it?**
|
||||
|
||||
A utility library contains low level code used by many libraries. Often there is no framework-specific code and the library is simply a collection of utilities or pure functions.
|
||||
|
||||
**Naming Convention**
|
||||
|
||||
`util` (if nested), or `util-\*` (e.g., `util-testing`)
|
||||
|
||||
**Dependency Constraints**
|
||||
|
||||
A utility library can depend only on utility libraries.
|
||||
|
||||
An example ui lib module: **libs/shared/util-formatting**
|
||||
|
||||
```ts
|
||||
export { formatDate, formatTime } from './src/format-date-fns';
|
||||
export { formatCurrency } from './src/format-currency';
|
||||
```
|
||||
|
||||
## Other Types
|
||||
|
||||
You will probably come up with other library types that make sense for your organization. That's fine. Just keep a few things in mind:
|
||||
|
||||
- Keep the number of library types low
|
||||
- Clearly document what each type of library means
|
||||
Loading…
x
Reference in New Issue
Block a user