nrwl / nx

Smart Monorepos · Fast CI
https://nx.dev
MIT License
23.25k stars 2.31k forks source link

Best practice on what to put in libs? #259

Closed dereklin closed 5 years ago

dereklin commented 6 years ago

I am hoping to get some insight from the nrwl team on what are the best practices on the usage of the libs. Would you recommend putting only Modules (I mean NgModules) (all code should live inside of a module) inside the libs? Or it's ok to put services without modules (NgModules) and other smaller units without modules (NgModules) such as:

Models Interfaces InjectionTokens Utils etc.

kylecordes commented 6 years ago

I'm not on the Nrwl team; but I think this is actually not a Nx specific question, rather I think it applies quite generally to Angular regardless of the tool you use to manage development of many related applications or libraries.

I believe the generally accepted approach is that you should think of the NgModule as the default unit of modularity, of reuse, etc. of Angular-related code. If you have code sitting in a library that is not a NgModule, then consuming it requires that the consuming code package it into a NgModule, and this of course is fraught with difficulty as the same code could end up double packaged and so on. That's not to say there isn't some use case somewhere where it makes sense to directly expose the code for a Component or similar; just assume that the NgModule is the right way to go until strong evidence arrives otherwise.

dereklin commented 6 years ago

When I wrote the original post I used the term module but was really thinking of NgModule. I am going to update it.

It's trivial to put a function or a group of functions as a javascript module nowadays (http://2ality.com/2014/09/es6-modules-final.html)

//------ lib.js ------
export const sqrt = Math.sqrt;
export function square(x) {
    return x * x;
}
export function diag(x, y) {
    return sqrt(square(x) + square(y));
}

//------ main.js ------
import { square, diag } from 'lib';
console.log(square(11)); // 121
console.log(diag(4, 3)); // 5

In the above example, lib.js could be named math.js or the math module.

But does it have to be turned into a NgModule inside the libs folder?

kylecordes commented 6 years ago

Oh yes, that is what I meant also; and I will also edit mind to be clear that I mean NgModule. If you have some block of code to reuse in an Angular context, that has any Angular contents (components, services for, really anything at all) then the default way to package it should be as a NgModule. If you have instead a block of TypeScript code which has no Angular connection at all, then certainly don't add NgModule to it; just package it up as any Node library would have been packaged even if you never heard of Angular.

dereklin commented 6 years ago

@kylecordes I think if the topic is not nx related, then using NgModule is definitely the way to go for most cases.

One selling point of nx is that the libs are meant to be internal. So apps can provide services inside of the libs, and they can use components inside libs. And having these small units in libs will make them very reuseable. So that is a legitimate use case.

However, for a more long term view, it's probably more robust to create NgModule wrappers for these small units so that if need be, one can package and deploy the small NgModules to npm for version control and so forth. Furthermore, the Angular compiler can compile NgModules in parallel so there is actual performance gain in compilation (Rob Wormald told me about this the other day)

Now, for things like the MathUtils example:

export const sqrt = Math.sqrt;
export function square(x) {
    return x * x;
}
export function diag(x, y) {
    return sqrt(square(x) + square(y));
}

I don't see how to put it inside an NgModule. How do you do this in your real project?

kylecordes commented 6 years ago

@dereklin That bit of utility example doesn't do anything with Angular; there is no reason to put it in an NgModule. It has no state, and doesn't need to be injected with DI. It can simply be used as a plain JS library. It can sit in a Nx managed library directory, it could be published to NPM, etc. without any Angular awareness.

ph55 commented 6 years ago

Viktor actually recommends to put 99% percent of your code in libs. https://youtu.be/qYNiOKDno_I?t=6m35s

playground commented 6 years ago

@dereklin what is the best practice for moving a lib out of Nx and publish to NPM?

kylecordes commented 6 years ago

@playground I am not him, and I don't know about "best practice", but practically speaking it is quite easy to add a package.json file to a lib/whatever directory in a Nx project, and put whatever you need in there to publish the library. Peer dependencies on Angular libraries, ng-packagr to do the bundling very easily.

playground commented 6 years ago

@kylecordes yes, I'm aware of generator-angular2-library and ng-packagr community packaging tools. I was thinking there might be built-in command with Nx to convert the lib to npm module :-)

dereklin commented 6 years ago

Agree with @kylecordes I followed this project: https://github.com/dherges/nx-packaged

dereklin commented 6 years ago

Just watched James Henry's talk on ngVikings youtube channel: https://www.youtube.com/watch?v=bMkKz8AedHc

He gave this code structure example: nx-structure-example

And at 20:30 he talked about the practice of putting non-reusable app specific module in the libs folder.

This rocks my understanding of nx a little bit. What are the benefits of putting an app specific module that's not reusable in libs? What's preventing from other apps importing from the app specific modules?

Another thing, the common-ui module could get really big. This reminds me of when Angular Material first came out, everything was in a single module, and then it was split up into smaller modules to take advantage of treeshaking. Am I missing anything obvious?

goelinsights commented 6 years ago

@dereklin I agree...seems an odd recommendation given the focus on bundle size, componentization, and lazy loading. Given reuse considerations (and that xplat is probably coming), I've been separating out my UI (splitting web from amp from ...) from non-UI elements (splitting these into core and admin) at the major feature level. Makes for a lot of libraries under each directory!

Had a followup question about any best practices for reusing selectors, reducers, etc (my use case is an admin module that has more functionality than the core module, so reuses selectors but needs to extend the State elements within the feature). Should we put the StoreModule and EffectsModule, for example, in the app or the lib? StoreModule.forFeature('feature', reducers)

kennyfowler commented 6 years ago

@dereklin i guess putting even non-reusable code into libs makes totally sense when you see your apps as "build targets", libs define your app-functionality, but you need a different (app-) boilerplate, configuration, whatever, if you want to build the same app-functionality for electron or cordova.

danieldanielecki commented 5 years ago

I think it's getting better with the documentation, well done. The libs is now slightly described in the new docs, but something like "best practices" would be appreciated as well I guess.

bcabanes commented 5 years ago

As @danieldanielecki mentioned it, we are approaching this issue in our new documentation website, as well as in our book explaining all the concepts around Nx.

Closing the issue.

demisx commented 4 years ago

I think the examples and best practices can be further improved. As a newbie evaluating Nx it's very confusing to comprehend what goes where. All examples are pretty trivial. I wish there were more advanced examples for organizing full stack enterprise code, e.g. multiple backends, multiple libs, etc. The "Hello World" type of examples are just not enough.

EthanSK commented 3 years ago

This is still very much needed.

konstantinschuette commented 3 years ago

I can only agree with your opinion. In 2021, the documentation on workspace structure is still very superficial. It is such a key thing to consider, but sadly not well enough documented.. I found this article very useful for my needings: https://blog.strongbrew.io/opinionated-guidelines-for-large-nx-angular-projects/

juristr commented 3 years ago

@KonstantinSchuette Manfred Steyer has also written a (I think free) book on Angular Enterprise where he describes how to use Domain Driven Design principles with Nx: https://www.angulararchitects.io/book/

Also if you search on Google, there are lots of talks where he in particular goes into those details a bit.

demisx commented 2 years ago

We are still missing guidelines for organizing backend projects (NestJS). Seems like most of the docs are focused on the Angular frontends.

alxpsr commented 1 year ago

Still don't get why i have to store 99% of code in libs even though it's only app-specific logic. What's the point? And according to new docs i have to store Domain-specific shared libs at root-level Shared scope. Don't get it as well. Why i need app-specific lib folder then? As far as i understood the app-specific scope (at high level of libs folder) is the place where you store logic that belongs for all particular apps. So shared app-specific scope then for me it's kinda contradictional stuff

FractalMind commented 1 year ago

Still don't get why i have to store 99% of code in libs even though it's only app-specific logic. What's the point? And according to new docs i have to store Domain-specific shared libs at root-level Shared scope. Don't get it as well. Why i need app-specific lib folder then? As far as i understood the app-specific scope (at high level of libs folder) is the place where you store logic that belongs for all particular apps. So shared app-specific scope then for me it's kinda contradictional stuff

Took me a while to understand too, but a friend explained to me that app-routing-module.ts is the only thing dictating what page loads in what app. So ALL pages are in /libs but only few are accessible. If that makes sense

Screenshot_20230317_143832_Samsung Notes

github-actions[bot] commented 1 year ago

This issue has been closed for more than 30 days. If this issue is still occuring, please open a new issue with more recent context.