Video details

The best way to Architect Your Angular Libraries | Tomas Trajan | angularday 2021

Angular
06.06.2022
English

The best way to Architect Your Angular Libraries - Tomas Trajan
In this talk we're going to explore how to architect Angular libraries with the help of sub-entries so that our consumers can benefit from the smallest possible bundle size and our teammates from great developer experience when adding additional components and services to the library itself!
-------------
Angularday 2021 happened online on 12th November 2021.
Info and details about this edition’s schedule: https://2021.angularday.it/talks_speakers/
Next edition: https://www.angularday.it Twitter: https://twitter.com/angularday
-------------
Keep in touch with GrUSP! • Upcoming events: http://grusp.org/en/ • Subscribe to our newsletter: http://grusp.org/en/nl • LinkedIn: https://www.linkedin.com/company/grusp • Instagram: https://www.instagram.com/grusp_/ • Twitter: https://www.twitter.com/grusp

Transcript

So our next talk is called the Best Way to Architect Angular Libraries by Thomas Trail. This it does not sound correct. No. Okay, we need to start this over and make things right. What do you think? Hard to be like one person, two places at the same time. Let's try it again. Let's try again. Hi, welcome back. And I'm super happy now to introduce our next speaker. Talk is called The Best Way to Architect Your Angular Libraries. And please welcome on stage, Thomas Ryan. Oh, hey, Julia. Long time no, much better. Right? Right. Sounds about right. So I would just suggest, because the. Talk is pretty long, let's just hop straight in and let's get this started. Can share your screen and we can just go you're doing some live coding, aren't you? Yes. Cool. So let's get started. So, as we said, the best way. To no, I don't see your screen. All right. Pardon. That's a very good point. This is exactly why we want yeah. Now we are ready. Okay, now we are good to go. Perfect. Sally. My name is Thomas Triang, and today we are going to learn about the best way to architect angular libraries. So let's go first. Yeah. My name is Thomas Tran. I'm a Google Developer expert for angular and Web technologies. Besides that, I am working as a consultant in Switzerland, helping Swiss Enterprise organizations achieve their goals with angular. I tried to become like an international speaker in various conferences and meetups, but then, yeah, the corona happened. Besides that, I'm also doing angular workshops for various topics like the Intro to Angular Basics, more advanced angular stuff, and even stuff like Advanced State Management with the most popular state management library called Ngrx. Besides that, I have been writing angular and front end related blog post for more about like, five years. It has more than 3.5 million views at current point, so feel free to check it out at thomasttrayon Medium.com. And also, if you are writing articles on the medium by yourself, don't forget to check out Medium and Enhanced Stats, which is a chrome extension, which will give you this very nice overview of total stats milestone, motivate you to write more very cool stuff. It's free on the Google Web Store. Now, if you are just starting with the angular, you might want to check angular Ngrx Material Starter, which is an open source project you can clone from GitHub, which will teach you how to use angular together with angular material, how to implement custom teams, how to use NGX to manage your state. It has more than 2500 stars on GitHub, more than 35 contributors. So feel free to check it out, see how people can do stuff in angular, including animation and stuff like that. So this can be very nice starting point for you if you are just starting to work with angular. Besides that, last but not least, if you are on Twitter, then you may want to check account called At Releasebutler, which is a Twitter bot, which basically always checks if there have been a new release of one of the popular front end frameworks and libraries like angular TypeScript, ngrx reactor, whatever have you. And it will basically check if there was a new release. And if there was a new release, it will take a screenshot of the change lock and make a tweet out of it. So that way you can stay up to date with most recent changes of your favorite popular front end framework or library. So now, because they are doing this remotely, you can tell me about yourself. But don't forget to drop me a line in your comments if you have any kind of question. If you have any kind of like if you noticed something in the content, feel free to drop me a line and let's talk. So, what is the context for this talk? So, we said that this talk is called the best way to architect Angular Library. So why do I would say something like it's the best way? Because it's like a pretty strong statement to make, right? So it's based on experience from a relatively large enterprise organization with more than 120 angler spas and more than 30. So you can imagine like a situation when you have the library depending on the library depending on the library. So there is quite some extensive experience from this kind of environment where it was used extensively. So it was easy to notice what kind of impact various approaches could have on this kind of environment, right? So, as you can imagine, it's like a nontrivial dependency graph, a lot of dependencies all over the place and yeah, it's easy to detect the impact. Now, what can be like angular library? Like why would we even write angular library, right? Because usually when we are writing something new, it's like an angular application. It can be like application like a normal web app. It can be a mobile application. So sometimes we are consuming like open source libraries, for example, component library, like angular material or something more specific. For example, if you want to highlight code, we may reach for something like NGX highlight. Or if you want to have like a more markdown support, we may reach for something like NGX markdown. But why would we write our own libraries? So this basically boils down to our environment. So we are just writing a single application like a relatively small company, or we are like doing something for a customer, then we may not need that. But if you are in much larger environment, for example a large enterprise organization with multiple teams and tens of developers implementing tens of angular applications, these libraries may come in very handy. So what does that mean? For example, we might need to implement a custom library which implements some custom company specific components and widgets which are then shared across multiple consumers, right? Or it can be something like a larger chunk of business logic. So, for example, we can have something like a document viewer or like check out Flow or like a login screen or like a customer profile which we then may want to share across multiple applications which may need to show this kind of component with the related services. Or it can be even extracted lazy route which is then used across multiple applications, right? So that's another example. Or it can be something like a core utilities which implement some cross cutting concerns like logging, telemetry, authentication and what have you, right? So there are many reasons why we would want to implement an angular library. And there are many types of logic we might want to extract into angular library. But it all boils down to the fact that we most likely have more than one applications and more than one application most likely also more than one team. And we want to share some logic between those applications, right? Cool. So now that being said, let's just hop straight into the best way how to architect angular libraries. And the thing is, I really don't like slides. I'm very lazy to prepare slides somehow. I just don't like to do that. So we're going to do it with the live coding. As you know, you have to have patience with me because of course, stuff with life coding is that it never goes as you plan. But let's hope for the best, right? So first of all, we see we are in some folder and let's just go outside. I prepared it. So let's say that's basically like on the side note, what is very important to realize is that one of the best things about the angular is that we are able to scaffold a lot of code in a very short time using angular schematics. So, first of all, what I have done is like I said engine new and I called it like angular library architecture and I said create application falls which basically generated me an empty angular CLI workspace. And I already did that before. So we don't want to wait to watch as the dependencies are being installed. So just go into the directory because I already did that. And now once we have this empty workspace, as you've seen, I said create application fall. So it's totally empty. There is no application, there is no library. So we can create now our library. We can say NGG library some lip. And again, I already predid that because we don't want to watch the dependence is being installed. But this would generate our library in the projects folder. And then last what I did was NGG application and let's call it Showcase, which is kind of like this pattern. Whenever you are implementing a library, it is pretty useful to have like a showcase spa in the same workspace. So that way you can kind of see what you are developing in your library, there's a pretty common pattern and here we want to have like a routing support and style CSS because it's just a showcase, not a real application. So it doesn't really matter how it looks like, we will not write a lot of styles. So now predid all these things and now basically when we look, we already have this workspace prepared and now if I open it in an editor we will see that we have this projects folder and in that projects folder we have the showcase which is an empty application and the lip. So this is our starting point. Now the next thing what we are going to do is to prepare a couple of things to demonstrate our examples later on. So first of all, what we are going to do is to generate multiple modules and components in our library. So we can do that with NGM for the module and let's call it library A. So it's easy to follow. I will do A, B and C. And as you can see, this is very fast and now in those modules we create like a component. In each of modules we have A, B and C component. So let's do that Ng GC as a component in library A and the name of the component will be A and now we do the same for the B and same for the C. Perfect. So now let's have a look in our workspace and as you can see, if you collapse the showcase and open the library here we see we have now three folders and each of those has a modular component and we also see there have been some files generated like beforehand when we just generated the library. So this is not a problem, we can delete all of those, we're not going to need those. So let's just delete them. And now in the public API we cannot export those things which we just deleted. So we have to export this newly generated stuff instead. So we have the library A library module and same for the BNC and again same for the BNC. Those things don't exist but those modules also have components so we also have to export those so C component and let's do this for the A and also let's do this for the B. Perfect. Now the last thing left is that those library modules, they have the component. So the component was automatically added into the declarations because we use Schematics. But now we also have to export it because it's a library. So if we import this library A module in some angular application and we want to use that component, we also have to add it into the exports array of that module. Cool. So now we have this, let's do this also for the other modules. So let's just here add export B component and in the library C we had exports c component. Let's save this and let's try to build our library with the Ng built. And as we're going to see, our library is being built and now we see some of output. This is angular 13th. So as you can see like this fresh new bundle with bundle files with the festimal 2020. This is fresh from the angular 13. So let's see how this looks like in our disk folder just to see that this have worked. So if you look at the sum lip and we can explore like either festival 2020 or 15, it doesn't really matter. It depends on which one is then consumed. Here we see there is basically the A component library, a module, B and so on and so on. And this will then have later impact once we want to show some more in depth stuff. Cool, so we have this. Now we have to also make some small setup in our showcase so that we can consume our library and demonstrate all these concepts which we want to demonstrate. Now let's generate some modules in the showcase. So we do Ng GM and it will be in the features for like lazy features and we will call it lazy a route will be a like on A and it belongs to the core app module. But now, because we have more than one project in our angular CLI workspace, what we have to do is to basically say in which projects we want to generate this module. And by default it was in the library. So for the library we did not have to specify anything. But now because the library's default, we have to say that this has to happen in the showcase project. So we just say Project showcase and this will generate us the module with the component out of the box. And now we do the same for the B. And then as you may have well guessed it, we do exactly the same also for the C. Let's do this. And now let's try to also build our showcase so that we see that everything works. So we can do Ng build and now Project Showcase and then we can say basically named chunks because by default it would be just like change into number in the production build. But we want to see which chunks, which chunks so we can track the bundle size across the lazy chunks belonging to the lazy module of ours. And we also want to say output hashing none. So we have like a nice short file names without the hash. So the hash is actually very important when we are building stuff for production because we want to get the cash busting. So that means whenever we have a release of a new version of our application that those files, we have different file names so that the consumer or like the browser of the user downloads the new bundles. And as we can see, we have two parts of our application. We have like the eager part of the application where the main JS is like the main bundle and then we have three lazy loaded bundles for A, B and C module. So that's exactly what we want. Now let's start with something very simple. So let's say we are having this showcase and we want to use a B component from the library B module in the library in the lazy B feature of the application. So first what we have to do is to import that module. Perfect. And I like to also sort my import. And then once we imported this module in this lazy loaded module of the angular application, that means we can now use that exported component in our template. So we can say here lip B, right? So this is exactly what we want. And now if we rebuild the application we are going to see that the bundle size of the lazy B was increased just a tiny bit because our component has like a tiny template and as we can see it's not one point 21 kw, right? So that's cool. That's correct. Now let's start introducing our first scenario and for that I prepare this 100 text to inflate our bundle size. So now let's see that let's say we add these 100 text into the C component in the library. So not in the B because the B is the one which we are using in the application already, but in the C. So now let's rebuild the library also because our library has changed, right? So in general we would most likely automate this like behind the scenes to have like a watch build on our library. But now for like the sake of argument, so it's all explicit, we will do this manually. So now we rebuild the library and now we are going to rebuild the application again. And what we are most likely going to see is that the bundle size was not increased, which is in fact correct because since we have angular IV which was possible to toggle like enable from version eight and then right now basically in 13 there is only IV which is amazing, as you can see that the tree shaking of the unused code works very well. So we included this 100 text in the component C which is not used by our application and this code in fact did not land in the application bundle which is perfect, right? So this scenario works. So now let's switch it up a little bit. Let's say now that in the showcase we will not include the B anymore. So that we comment this stuff out, right? And we are going to include the C instead. So we want to demonstrate how we will get this 100 library C module. We want to demonstrate how we are going to get these 100. This will help us then to illustrate another point. So we have this library C that means we can use the lip C. And now if we rebuild the application we should see that instead of like the B having one point 21, the B will revert back to and as we can see the C was inflated with these 100 text. So this is exactly correct. So now this really looks perfectly fine. So then you might ask like okay, so like this architecture of the angular library out of the box which is generated modules, we include the modules per lazy route, right? And the tree shaking works perfectly. So what is there to architect actually, right? So it just works, there is no reason to do anything more. Well, unfortunately this is not really the case. So let's start with the very first example which is that people still nowadays use libraries which are common JS. So very infamous examples might be something like a moment JS or a lo dash. So let's start with installing something like a moment. So this is a library which brings around 350 extra payload in anything it touches kind of, right? But it's still very popular used by many people. So that's like this iconic example of what can go wrong. So now we install it and what we can do now so let's remember our application is using this component C, nothing else and it inflated our bundle by 100. That's correct. So now let's say that in the library in our module B and component B we start using this moment, right? So we are going to do is to import star from moment stars with the quotes of course. And now we say we just want something small, we want like just date, right? So let's say date which is moment now. And now what we can do is to call this in a template date so that it's all bound and now we can rebuild the library again and after that we are going to rebuild our application and you may guess it based on what I said previously that the outcome will not be in our favor. Let you see. So just to recapitalize, remember in the last build we were basically including the component and module C in the lazy C and nothing else. And now we just added the moment JS in the module B and component B which is not used by our application whatsoever. But as we can see the bundle size was inflated by additional around 350 KB even though our application is not using that component, not using that module at all. So the reason for this is that basically like behind the scenes angular CLI uses web pack to bundle all the JavaScript to traverse all the dependency tree with all the used libraries and the whole basically dependency graph through the imports and it basically kind of tries to inline all that code into the bundle files, right? But the problem there is that under some circumstances webpack is unable to determine what parts of the code are used and what parts of the code are not used. Which leads exactly to this kind of case where we are basically bringing in the code which was not used at all, which inflates our bundle size, which slows down our application and so on and so on. It slows it down for the users, it slows it down for the developers because then the rebuild times are slower, the testing times are slower, so it has a lot of negative downstream effects. So what we just demonstrated is that out of the box angular library architecture breaks with common JS dependencies. But that's not all. There is more. So let's just say that we will have import another component. So now we are importing in the C. But now let's just say we are also going to import in the B of the showcase, right? So in the lazy B. So we removed these comments. So that means we are going to use the B and the B and the C and the C and let's see what happens if we rebuild again. So this will uncover another problem which is about the topic of codes splitting. No, it didn't. So we have to have a look. So this is still the same now, all right? Oh no, it did actually. Okay, so everything is all right. So as you can see, previous we only had three bundle files, but now we have four lazy bundle files, right? So that means there is something called like a virtual pre bundle which Webpack tried to extract for us because it knew that these files are shared by two different lazy routes. So what does that mean in the practice? Let's just start up our application and we are going to see what's the problem with that approach. So you may say like, okay, even though it's all together, because this is like moment together with these 100 KB, even though it's all together, we don't really care because it's anyway loaded only when it's lazy, right? So it doesn't really influence our startup time, but actually it's not all that simple. So basically now, if you look, if we load our application initially we see that there is like the styles vendor and main and now if you go to A, which we didn't touch at all, we will see that the A lazy bundle was loaded, right? But now basically, if you think about it, then we added something to the B and to the C. We say that to the C we added only 100 KB. But now when we go to see, we are going to get all these 400 KB which is not really something nice, right? Because the C is actually not using that moment, but it's anyway triggering loading of that moment. So as we can see, this is already breaking the code splitting. But wait, it gets a little bit worse than that. Even so, now, our current situation is we have the B in the B C and the C we are bringing this moment. But what happens if we also start using A in the eager part of our application? And you may ask like, why would that happen? Well, so let's say we are building like a component library and we may very well need a couple of those components in those lazy routes to implement those business flows, whatever that might be. But maybe we also need some of those components in our initial layout, right? So this is like a pretty common pattern with something like when using something like angular material. So we may need, for example, material toolbar to implement the main layout, but then we might need like input or select or autocomplete to implement some form in a lazy feature, right? So this is like a pretty common use case that the part of the library is used like in the eager part of the application and the other part of the library is used in various lazy parts of the application, right? So let's now start using the library A module which we haven't touched till now in the eager part of our application. Which means we are going to import it in the App module. And to do that, we just say library a module. A little bit of OCD for this. So perfect, we have this. And now, because we imported again, we can start using the library component. Let's also just delete this so that we are not confused. So this is like this initial what? This is the initial layout which is so previous, which is generated by the angular clip. But we can just say that we have something like angular library texture so that we are not confused. Perfect. So now we have this and we have to rebuild our application. And what we are going to see is something not so nice. So previously our last status was that we're using B in the B and C in the C. And we have seen that the angular CLI extracted a virtual pre bundle which then was loaded when any of those B or CLA routes was triggered in the browser, right? But now we see that as we included A in the eager part of the application, the A which was maybe like 1 KB in total because it was totally empty. And now we can see that the whole payload is now included in the initial application bundle. So the main JS is now 675 KB instead of something like 230 or whatever it was initially. Right? So this is pretty terrible because this is like a very huge impact on our performance, right? And this is again, keep in mind that this is like a very minimalistic example, but in practice this can lead to like megabytes and megabytes of bundle size for our initial bundle, which we definitely do not want, even with the fast networks, we can say like oh yeah, I'm living in a country which has a very fast network. Well, the problem with that is it's not actually the network speed which is the biggest problem because anyway, you can say the bundle is anyway cached after the first visit, right? So it's not the bundle size which is the problem. The main problem is that it actually takes a lot of time for browser to parse and execute all these JavaScript. So if you have like five megabytes of bundle size, it can slow down the startup time easily by three to 5 seconds, which is terrible for the user experience, right? So, as you can see, just to summarize, basically if we just approach building of an angular library with this naive approach where we just generate some modules and export them all without any kind of additional architectural concerns, we get a situation where two things are broken out of the box. And the first of those things is basically the tree shaking of the common JS dependencies which will not be tree shaken even if they are not used because the weapon cannot determine if they have been used or not. And the second, and one would argue even worse thing is that this kind of naive library architecture breaks code splitting which leads to the extraction of virtual pre bundles which are suboptimal in terms of that first lazy route which was triggered, will download the whole thing even though it doesn't need the whole thing. But even worse, if the part of our application is used in the eager part of the application, and even if it's a small part, then anyway, the whole payload will end up in the initial bundle. And this is definitely something which we do not want. So now, how can we solve this? What is actually the best way, the right way to architect angular libraries is when we use something called sub entries. So let's get to it, let's implement sub entries. So we are going to go to our library and we will see that to implement seven trees is pretty trivial. So we have to do is to basically add three files into all of our modules in our library. So one of them is called like index TS and the other one is called PublicApi TS and the third one is called package JSON. Jason is very important to not make a typo. I learned the hard way, once we have this, basically the content of two of those files will be always the same. So index to TS will always export start from the public API and this one is empty. So this is why it's red. And then in the public API we have to export everything we have. So export star from we have two things, we have the module and we have the component, right? So in the A there is a component and now if you look in the index TS. And if we restart the types of compiler, this should be okay. And now the content of the package Jason, should be well, I actually don't remember. So this is why I saved it here. But it's the only thing which we need to remember. So we have to add this and this will always be same. So we can just copy it around. This is still red, but we know it's correct. So it doesn't really matter. It's not red anymore. Perfect. And now we can take these three files and copy it in all of our modules in the library. So we do it for B, we do it for C, right? And then of course we have to rename this because this is not A, but this is well it's not A, it's B and B. And in the C it's not A again, but it's the C and C. So let's do that. Perfect. So once we have this in place, what we have to do is to do a small change in the TS config Jason, and we have to introduce additional Pot alias which is like Sumlip Star. And we say this is in projectsum. Perfect. And when we have this, the last thing is we have to change the top level exports. We are not exporting the things directly anymore, but we are exporting our newly created sub entries. And this we can do like this. And this works because we prepared those New Path aliases. Now we can say B and C and now we can delete all of this. And now we can rebuild our library and we will see that the build output will now change. It will not be just one entry, but it will be in fact four entries. We have the three sub entries and we have like the top level entry of the library itself. So let's see how does that now looks into this. So you can see some library. And now we open this, we see we have a couple of files, there are also maps, but we are more interested in the JavaScript. So the top level is just these exports. And now then basically those sub entries have their content. So the A didn't have anything. So it's relatively small. It's like only 42 lines of code. Right? Now we know that in the C we had this 100 text as we can see here, right? This is correct. And then in the B we will have somewhere well, it's not only bundled, but there is a import for a moment of course, which will be then processed during the build time of the consumer. Cool. So what's going to happen when we now rebuild our application? Right, so you may well have guessed it is that we are trying to fix our problems with the common JS and with the code splitting. So first we are going to see that the cold splitting is going to work as expected. So look at that. Our main bundle has 201 KB instead of 800 kwh or whatever that was, because it only uses this library A module which only brings this A component which is very tiny, has less than 1 KB, right? So this is exactly correct. And we also see there is no pre bundle anymore, no virtual pre bundle extracted. But actually what happens is that all of those lazy loaded modules only get bundled together with what they are using. So if the C uses 100 text, we see it gets these 100 KB here and we know that the B is using common JS. So B correctly has 372 KB. So, as we can see, the code splitting story was solved by the sub entries, right? Now what about the tree shaking of the common JS dependencies? So, to reproduce that, what we can do now is to basically keep it in the library, right? So the initial situation was that we had moment JS in the library B module and the B component, but we have been not using the library B module in our consumer. So that means we remove it from the consumer, from the application. And before we had seven entries, the moment was anyway still bundled in our application. So now what we hope to see is that the moment will be correctly tree shaken because our consumer application is no longer importing the library B module. And that also means that it's no longer using the library B component. And as we can see, this is correct. We are only using the C and the A. So A was included in the main, the C is included in the lazy C bundle, but there is no extra 350 payload in any other lazy bundle because we are not using the module which actually brings the moment JS. And this is correct. So, as you can see, the use of seven trees solves both of these problems, which is one first, like the code splitting, the correct code splitting across the lazy bundles and the eager and lazy part of the application. And the second of all, it also solves the problem of the tree shaking of the common JS dependencies. Perfect. Now, there are a couple of more things, but first thing to realize is that why is this important? Right? Well, first of all, whenever we are building something for the end users as a company, as a start up, as an enterprise, we need fast applications because the application startup speed and just the use speed and all these kinds of stuff has a real impact on the user experience. And in the end, like on the bottom line of our organizations, because the more people keep using our apps, whatever that means, maybe there is more conversion, maybe there is higher usage rate, whatever that is. We want people to use our applications and to use our applications. They cannot get frustrated that our application is taking forever. To start, right? So there's like this most obvious top level benefit of having smaller bundle size and faster applications. But there is more to that which is more hidden. And the thing is that when this is done correctly, right, when our application is small, when the code is bundled correctly, this does have a real impact on the developer experience. So whenever we are implementing like an application, we would have everything just in a single application module and our total bundle would be like five megabytes. And then whenever we make any kind of change in our application, the build process would have to rebuild that huge bundle, right? And it will take forever. So every kind of change like our feedback loop will be very slow, right? And compare this to this case now, which we see on the screen where if I be working on the lazy b feature of my application, which is really tiny, of course we would have to increase all those numbers, right? Like to be realistic. But anyway, some lazy features will be smaller than the others. Then the build process only needs to rebuild that much smaller bundle for the lazy feature instead of everything, right? So this has a real impact not only on our users but also on the developer experience and the feedback speed for developers. So it really makes a huge difference if we make this right. Perfect. Now, what about more hands on developer experience? So this looks cool, but what happens like when we for example need to use one part of the library and the other part of the library? This is a pretty common case when basically we have a couple of components. So let's say we are building like a component library and then we have a button and then we have some special selector, whatever widget which also needs to use that button, right? So we need to import one of those modules in another one. So how does that work with the sub entries? So let's say I want to use the b in a, so I can say library b and here I get two offers and I picked the first one. So basically the gist of it is that we should use these kind of imports which we are anyway getting out of the box. So now if I build this, this is going to work and then we will compare it with relative imports, right? So the point here is that we do not want to do the relative imports between the seven trees. So this is correct and this is based on our TypeScript alliance. But what if I did this relatively? So instead of doing this, which I got as a code competition for the editor, I would do this and try to build this. This will lead to an error which is correct because we should never import across the sabotage boundary with the relative imports, right? So this error is not that helpful yet. Like this message is not the best yet. It's not so obvious what went wrong. But still, if you are doing small enough steps, it should be pretty clear what went wrong. Now, again, just to highlight this, that actually editor gave us correct import when we're just doing the auto import. So this is actually the case. Perfect. So this is cool. Now, what about something else? What about the situation when we have more modules and we kind of are losing oversight of who is using who? And what would happen if we try to introduce a circular dependency in our library architecture. So we have the situation where the A is using B, and now let's just say that the is going to use C and then we can say that the C start to use a library A. Perfect. And those imports again are correct. This is from the editor, right? So this is all perfect. And now you can see this is already highlighted in our editor that there is a cyclic dependency between the modules c to A to B to C. Right? So this is amazing. But even if you didn't see the editor, if we try to build this, as you can guess, we are going to get exactly the same error, which is much more clear what happened. Actually, I started to play with these things around angular ten and those errors were not that great and it was very hard to figure out. But now it's like clear as a sky like cycle found. So let's just fix it. Let's remove this and the cycle is broken. And now we are going to be able to build our library again. Perfect. So now what about making stuff private? So let's just say we have some internal digital service or whatever, which we want to use in our library, but we do not really want to provide it to the consumer application, right? So let's just say that the A is going to be private. So to make stuff private, basically what we need to do is to not export it from the top level. And if we remove this here and rebuild our application, what they're going to see is that no application. So the library, then we gonna see we are using A here and this should not work anymore. Let's just see. It might be that because we already imported stuff in the stuff that's going to still work, but let's just see. Yeah, okay, perfect. So now we can see that library is unknown because the module was not brought, right, basically. But anyway, we have to clean up a bit more. So let's just remove those imports across so that we get exactly the picture which we want to get. So let's just make all those modules stand alone again. Because of course it doesn't make sense to make stuff private, which is anyway then exported transitly. So that's okay. So we are going to remove all of this and then we see that the A is not here anymore and we are going to rebuild the library and now we are going to rebuild the application and it should not work and now also if we see it in the way it doesn't and as we can see there is no library module exported from Saturday if this is correct. So the only way to get this module now would be to use deep import and of course this is something which we do not really want, we never want other consumers to use the deep imports and there is a way to prevent that using the TSLint or ESLint. So there is something like an import blacklist where we can or deny list where we can prevent that from happening. Perfect. So that's it for today. Let's just summarize everything. So live coding was pretty successful, there were a couple of small things but I think we get exactly the outcomes which we wanted. So what about the other best practices? So there's like a couple of things to keep in mind whenever implementing angular libraries and that is like the library should never bring those base modules which can introduce application via singleton. So one of the very common examples is like the browser module which should be only imported once per application because the registers all those single term providers. Another can be like Http client module for root or for example, even our own stuff. Anytime a module tries to register application via singleton's and providers, we should never import it directly in our library, but in the showcase instead because it's the responsibility of the application to prepare that environment for the library. Because if you put in library, we do not control how the people are going to use our library so they may use it in the eager part, they may also use it in the lazy part and if they start using a lazy part, they will get multiple instances of those providers which should have been singletons. So that's like a very important thing. So the exception to that rule is that we are free to use any kind of module which follows that pattern which says like for child, right? Because that's exactly the splitting that the Ford route registers these global singletones and then the fourth child doesn't for child usually just bring some tokens to be used in a template so that's something to keep in mind the next thing, like if the module only brings components directly and pipes so the things which have only something to do with the template, those can be imported wherever so it's always about those providers because there we will get more than one instance in case we were only expecting a single global instance if we import such a module in both eager and lazy part of the application. So the key takeaways please do use sub entries library architecture for the reasons which we described. So basically without that it breaks the seven trees and it breaks the tree shaking of the common JS dependencies. Of course, if you have something tiny then it doesn't really matter. But whenever you are implementing a large library then this might benefit you and your colleagues in these ways. So the good thing about the seven three is that it kind of forces you to think about how should you cut and split your functionality in your library, right? So it kind of forces you to have a clean one way dependency graph and clean architecture which is much easier to lose when you just keep like piling the code on the library because it's still somehow works. And it's like that basically starts to depend on each other and it's very hard to untangle and to understand. If you do that, you will enjoy the smaller bundle sizes and better developer experience and clean dependency graph so that your library will stay, maintainable and you will be able to evolve it and change it for much longer time without much headache, which is usually the case, otherwise unfortunately cool. So if you want to get in touch, please do not hesitate and check towelstrend.com and I will also share the links to these slides under the video. So feel free to click on those links if you are interested in the blog post, talks, stuff like that. If you are interested in ad hoc consulting or like in some workshops, check the [email protected] And we will also have an upcoming website called Angularexperts Ch. So please check it out. And if you want to get in touch and prefer more like a terminal person like me, then you just can write MPX Thomas which will basically download a small package which prints all the contact information. And depending on how good your console is, you may even click it to get directly to that website. So feel free to try it out. So before we finish, I have a question for you. So, if you are working in a very large enterprise organization with many projects and libraries, you might benefit from Omnibor, which is like the best tool for the lead software engineers, software architects and tech leads which helps them to get overview and drive change in their organization. So it's basically a tool which I built based on my own needs when I was contracting these large organizations. And I always was like okay, we should migrate this, or there was like a release of the new internal library and there was an API change. Now we have to migrate the consumers, but who is actually using this library, which parts of the library have been used? And stuff like that. And I always kind of needed to get answers to these kind of questions to be able to effectively do my job right as a contractor in these larger organizations because you don't want to break all your consumers you don't have to have like a production incident and stuff like that. So you kind of need to get an overview whenever you want to do any kind of bigger change which has happened because the environment keeps changing and for that you need to get some support or some tooling to be able to ask these questions and get those answers and visualize this data which you retrieve to be able to basically function in this kind of environment. And this is why I created this Omnipote. So this is somehow it looks like. So you can define any kind of custom dashboards based on the data you retrieved. So basically the flow is you have a question and you encode that question in a check, which is like Omniboard's way of expressing that question. So you can have like regex based check, ex path based check, JSON part based check, whatever that is, and you can basically run those checks against your repositories and upload the results and then those results can be visualized using various generic dashboard which is to create custom dashboards. As you can see here is like custom dashboard for angular environment overview, where we see like what is the version breakdown between all the application, which application is on which version, how many applications already have IV and all this is like custom, right? So this is not like a tool just for the angular, it also works like with back ends or if you use react or whatever that is because you can basically express any kind of those checks or any kind of those questions and then you can just use those generic digits to get these breakdowns of what your current situation is. And then another example, for example, this is like a dashboard for the clinic of the rhythm. So here we are trying to figure out which applications use some of the wrong imports and which are still using the rhythm compound dependency because we want to migrate all our applications to the latest version of the RHS and using the correct imports because again this will have impact on our final size which we want to be as small as possible, right? So yeah, you can create custom checks, customer dashboards, there are various dashboard widgets, it has front end and back end support. So basically whenever your project has a package, JSON or Pom, XML, it can be tracked and there is a easy continuous integration, pipeline integration. There is also like single sign on support and yeah, please check it out at OmniBoard dev and keep in mind that even though this is a paid tool, if you have a free account, you can track unlimited amount of projects with I think five checks and one dashboard with three dashboard widgets. So you actually can get a real valuable overview of your organizational environment even with the free account. And then of course if you like it, you can step up later. So please check it out, try it out and get some valuable insights. Anyway, thank you for your attention and see you next time. Ciao. Sometimes it's just not today, but I still hope you could see the most of it. And if you have any kind of questions, then please just let me know. In the chat. I think people have been able to follow your talking. I see they are very kind, like Michelangelo. Thank you very much. Don't worry, we can understand the internet issues as well. So Alyssa was asking if you can share the code maybe some right? Yeah, I can. There is no reply, but I can upload this. There will be people will be receiving. Like the videos or something. So I can put the link or something, right? About that. All you attendees will receive tomorrow, probably an email with a link to the recordings, so you can rewatch all the conference. And if you will be able to provide the code, they can also use it to try again. There's a couple of questions in the Q and A section. Let's have a look. What is the benefit to have single library with three module seven instead of three distinct libraries? Very good question, actually. So it all boils down to the trade off with the overhead you are going to have, right? Of course you can have three libraries, but then it's very often the case that one part of the library needs other parts of the library, right? And if you have it separated in the three distinct libraries, then your process overhead will be much larger because then you're working on one feature and you have to make a change in first library, then you have to release it, then you have to update the version of your second library, then you have to update the second part of the functionality and release it. And all the way if you have more than two or three. Right? So it depends. If you have totally independent pieces of logic, then of course it may very well make sense to actually have it in separate libraries. But if we have something like a. Component framework, probably do not want to have a library per component, right? So it always depends. But it's pretty often the case that you want to have more than one module in your library. And in that case the sub entries are the perfect solution. So I hope this answered your question. Besides that, we have Marco, we have created an SDK using anglers libraries, just classes and services. Do you have any suggestions on how to generate documentation? Use compado, but maybe there is a better way. So in practice, I also actually use compodoc with some custom tooling around it to basically document and show the API of the stuff which we are providing. So we first use the Compudoc to generate the JSON with all the description. But then we brought a little bit of custom tooling which pulls out some of that information to display it in a very nice way. So I hope this also answers your question. Is that something you wanted to add? No, I think we are good. So the question is answered anyway. The only thing I can say sorry. Again for the internet. I hope it's not your fault, I heard at least something and I will try to share the working code example and maybe also. Like a link to. Another video where I recorded at home. Because I have such a video where. You can watch it again. Maybe we can make something happen with Julia. Yeah, sure. There's another question from the other one. Yeah so please sorry for the summer is deep import in library components module. Should be avoided because they have bad impact on output bill showing console. Right? So the deep import is usually when the application is consuming the library. That's the most common case where you would have a deep import when the application is trying to consume something which was meant to be private for the library. And the best thing about that is that it was private for a reason, right? So we want to be able to change that private part without breaking our consumers and this is why it's very important that the consumers are not dependent on the private part of the functionality. So I hope this answers your question.