Muhammad Ahsan Ayaz: Mastering the Monorepo: How NX Schematics Transformed Our 80+ Angular Projects!
About this talk
In this talk, the speaker Muhammad ASAS, a Google Developer Expert in Angular, shares his experience of managing over 80 Angular projects using NX to implement a monorepository structure. He discusses the challenges of maintaining consistency across multiple projects, particularly when updating them under Angular's predictable release cycle. The speaker explains how he successfully used NX workspaces and plugins to streamline project creation and management, allowing for quicker updates and a more efficient workflow. Additionally, he demonstrates how to automate project generation and apply global styles across applications, ensuring a consistent user experience. The session highlights the importance of using tools like NX to enhance productivity in large-scale Angular projects.
Full transcript
[Music] hello everyone and welcome to this talk uh this is going to be very interesting for a lot of people who are interested in devops but then also angular specifically and this is a this is a real case study regardless of theoretical you know uh Concepts so we're going to talk about mastering the mon repository and how NX basically helped me transform 80 plus angular projects what
were the challenges that I faced and how did we solve them but before getting started I want you to scan this particular link which is essentially a feedback app which you can give me feedback through on how you liked the whole talk and you can also add comments as well so I'm going to give this a couple of seconds while you use your camera to basically open
the link and then we'll get started awesome we can move forward now so I want you to imagine 80 plus angular projects that you need to work with and I want to ask you have you been in a situation where you had to update multiple projects Al together I'm not talking about 80 projects but maybe five six 10 projects have you been in this situation and obviously
one of the biggest challenges is when you really have to make sure that the whole UI is consistent throughout the applications that you're working with so you could be working in a monor repository or you might be using let's say a package that is published on npm or your own registry but you need to ensure that all of those projects use the right package and are consistent
when it comes to ux well for the next half an hour almost we're going to talk about exactly this now before we get started I want you to think about why should you care about this talk and I already have the answer for that and that is because after a lot of experience and a lot of hard work I was actually able to convince Google or at
least one of the products of Google that I am an expert and then it happened it started showing me recommendations like this on YouTube where it says not everyone should code so Google and YouTube I'm not really sure what you're talking about but anyways just kidding so a quick introduction of who I am I am Muhammad ASAS I am a gde in angular I'm originally from Pakistan
I live in Sweden at the moment and I'm working as a software architect at Scania group at the moment and I'm also the founder of vision wise I'm also the author of two worldwide published books and one of which is this recent one this is called the angular cookbook which is updated to angular 17 and now angular 18 is coming up and it's going to be updated
for angular 18 as well and because of what I'm talking in this session it is so easy for me to Now update all the 80 projects together that would have taken me I don't know even a month that I can just do it in a couple of hours at Max now what in the actual schematics are we talking about so today we're going to talk about how
it saved me tens of hours if not hundreds working on the angular cookbook second edition what were the challenges and how did I solve them so let's talk about the problem in 2019 I published this book called The angular cookbook and it has gotten I think more than 46 or 48 reviews on Amazon it still has got 4.1 or 4.2 rating which means people really really liked
it and when working on the second edition I had to deal with 80 recipes so the cookbooks sort of Architecture is in terms of the book when you read it you're given a problem you have some steps to follow just like a recipe and then you end up with the solution so it's a very practical very fast-paced book but you can obviously go on your own pace
but it's not that you have to spend days on understanding something and then you Cote something it's really quick but 80 recipes or 80 plus recipes means 80 plus angular projects and that is a lot to be honest now one of the challenges that I immedi mediately faced when I published the book and as you know about angular angular has a predictive release cycle so every six
months there is a major release now updating all the recipes all projects whenever there's an update to angular it's almost like this it's very slow I would have to dedicate the whole month maybe to update all the projects to ensure every project Works nothing is broken because obviously it's important for for the readers as well and having a full-time job and then also managing this with a
family I think it's really really tough and technology is obviously there to make our lives easier not tough so what was the solution I'm really glad that I stumbled upon anex which is one of the best tools out there I think today for working with web applications whether you're working with angular react and especially if you're working with mon repositories and cicd now the solution is that
we have two NX workspaces only so instead of 80 projects and 80 projects for the start State and 80 projects for the final state which actually makes it 160 now we have only two workspaces which means that I only have the starter workspace and final workspace and for me it's now much easier to manage those how does this look like I have a single monor repository but
this monor repository is my technical monor repository it's not NX so I'm keeping a one GitHub monor repo in which I'm actually having two NX workspaces one for start and one for final and it's important for you to look at the architecture of the folder structure of this mon repository so each workspace that is start and final has different folders for each chapter which makes it really
easy for the reader to go and navigate through the workspace and you know Finding code when they need it and each chapter then has every single recipe and I want to tell you out of the bat that each recipe has a unique name which means that the CC inputs and outputs cannot exist in another chapter because it's a very unique recipe throughout the book so that makes
things easy but also tough when we talk about angular and when we talk about creating folders whenin a workspace so this came with a challenge if I tried to create an application using NX and I said hey npx NX generate an angular application inside this folder so I'm giving the folder name chapter one and ccng on changes when I do this it actually generates an app but
the name becomes chapter 1 ccon changes so it essentially takes all the folders if I had multiple folders nested here here in this command then they would also become become the part of the app name and if you talk about the readers of this book I don't really want them to remember the chapter names or even to to write the chapter names because if we can make
lives easier if they can only write the name of the application I think that's a win and that's what my target was so they would have to write the chapter name and as well as the other app name as well and if I go forward this is the problem now if we talk about the situation where we have all the unique names having them remember everything I
don't think that's a good idea so the question is can we do better what are the possibilities what are the possible solutions in this case I thought of two different solutions one was can we exclude the chapter name when we are creating the project so when we are creating a new angular project within the monor repository can I reduce the chapter name that was the one thing
and that happens when we are creating this another solution is is it possible for me to create the project first and then rename the project somehow and that was the whole idea now I could I could not really find any particular way of sort of stopping NX to you know remove the chapter name when I'm creating those so I went with the other solution and what I
used for that is called NX plugin so I needed to have some automation inside my whole workspace for the codebase and for that in order to host everything I opted for creating an NX plugin myself and this plugin would contain different things that we will use so I ended up creating the anx plug-in to rename my application after it has been created and this is sort of
the architecture so I would use the NX plugin with two different things one is the generator and second is the executor now if I go back to the previous situation where we would be application you will notice that I ran initially this command that was npx NX generator narval angular application this is nx's default generator that we use to create angular application but I opted for creating
my own generator which in turn would call this but with my configuration at then would do some more magic would add some spice to it so when I created the plugin I essentially used the generator to generate the application and add the executor to the projects Json so I can use it later on so the idea was generate the application first and in the process add the
executor that I want to run for renaming the application in the generation process so the first step is the generator creates the application and adds the Json in the project Json or adds the executor in the project Json and the code looks like this this is my generator's code when we have this function executed throughout the generation process it essentially goes to the configuration or the projects.
Json and then adds this rename block into the targets so this adds the rename executor right here now you'll notice two things here this is the executor script that my generator will run later on but then it needs two different things two two parameters s to say one is the chapter and the second one is the app name and I'm providing this in the project gson so
when I'm renaming I can split down or remove the chapter and I can still keep the app name so just to make it easier for the rename script on and when we create this for example if I created the NG on changes project in the end the project Json would contain exactly this thing so the chapter would contain value chapter one and the app would contain the
name and you can already see how this is working so initially we had the name generated as chapter one- ccon changes now I can basically strip them off so the second part of this was the plug-in executing or using the executor to rename and for renaming it would take out the parameters from the project Json and this is the code for renaming so it essentially goes and
gets the project Json when it reads it it essentially replaces everything and replaces the chapter name- app name with just the app name and that basically results in project so the idea is that the reader in the end could run this particular command they could run npm run create and then when I run sorry this is the command that I would run to create so I would
run this create command with chapter name I would run this app name and then the title and this is a best that I ended up creating so my bad script when I run npm run create it would run this bad script and it would run the generator first and then the executor and this is sort of the gist of the B script I've Not Included the whole
B script but you will get the idea when I run the B script using npm run create it comes into this bash script and then it takes out a couple of things it takes out the app name it also takes out the chapter and essentially it runs this command to create everything in the end I would have just run this and this would generate the app project
for me and this is the target project structure that I showed before if you see here we have the final workspace and we have the start workspace when I run this command I want to create inside my start folder or start workspace a chapter folder and then the project name now even if I have the chapter folder here the goal was when renaming to not have this
in the project Json or end up in the app name so I can run NX serve just the app name and not the chapter name and all of this was done to be able to do just this so it's a lot of work but to me it makes the lives easier for the readers so they don't have to put chapter name every time here so in the
end they would just have to run NX serve CC on chages NX serve NG drag and drop NX serve you know cdk stepper API so just the app name or they could run npm run serve which is uh something that I've put in the book so they essentially run npm run serve and then just the name and that's pretty much it if they wanted to run both
the start and final projects I've also created a b script to just provide this both parameter at the end with the same command and then it would spin up both the start and final so you can see that this is making the lives easier for the readers to just run the commands very easily I faced another problem while working with this whole scenario when we want to
create multiple projects and when you have 80 projects we talked about what happens if I want to keep the uiux consistent throughout all the applications now you can imagine that I have a structure for my applications and this is the structure I want to have a header up top which should have the angular logo which should have the title of the application and then an info button
that takes them to the thank you page for buying the book and then in here we have the actual content of the application now if you look at this the same header should go in all recipes and the this content is the one that is dynamic now what is the solution if I want to create this I opted on going with something including a library and the
generator and I'm going to talk about why the library is important in this case I created a UI library in which I put the header so when I'm generating the application I can use the same header component and then or the same header so to say and then just move it around so if I go forward this is the header component that we are talking then if
we move forward this is the whole flow we have the NX plugin that uses my generator so when I'm creating an application it uses the NX generator and then it uses inside the library to create the header component and I'm going to show you how that looks so when we run this particular command npm run create blah blah blah it is dissected into a couple of things
when we run npm run create it runs A bash script then it takes the chapter number as the first parameter or the first argument and then this is the app name and then you provide a title in the quotations all of these things are important and the title goes inside the header for the structure that I just showed you and this is my configuration I have some
templates that I use in my generator so whenever a new application is generated it goes into the HTML and adds a couple of things for example Tailwind CSS some fonts whatever I need in all of my application is inside this template and same goes for for the app itself so the angular app component template looks like this I have basically my header in here and this is
having an image of the angular logo it uses the title that I'm using in the bass script and then does the info button and I'm going to talk about this in just a bit but what about Styles if I just have some HTML sort of being copied to the newly created application how do I ensure that the whole layout is consistent in terms of CSS as well
so what I ended up creating was in my plugin I created a global. scss inside my library so in my angular or angular cookbook UI Library ngcb UI Library I ended up creating a scss file and then what I did was in my package I'm exporting this style file as well which means that now this is going to be available to all the application if they just
do node modules this library and then the global scss they will find it really easily so there were two steps involved one creating the scss file for the global Styles second exporting them from the library so it's packaged and the third step is in my generator when I'm trying to rename or adding a script for renaming the app I'm also adding something in the project Json which
is the Styles and this includes in all of my projects the Styles which are Global so every project gets the same Styles and in the future if I needed to to change anything at all what do I need to do I will go to my library I will update those Styles I'll just rebuild the app or rebuild the library and since this is automatically importing everything from
the node modules and I've linked them together all the Styles would be updated in all my applications so I have one Global Styles in my library which will be updated everywhere and I don't need to publish this on npm or anything it's all linked locally which is amazing now one of the challenges that I also face Ed with tailn CSS that if I had any Styles in
my library it would not work for me because my apps have tailin CSS configured but not my library so what I ended up doing was this just trying to figure out a way of why my styles are not processing and then what I basically did was in my angular Library I used this package to set up tailin CSS as well this ended up having the tailin config
created and also the tailin config created for the library and then I was able to ship my styes and this was the final output as you can see so all of my applications right now have the header they have the logo and they have the title that's super cool and then they also have this whole dynamic content sort of slot which is used for all the applications
and they all are centered so they all have the same style so when the reader goes through different recipes they're not like okay this is completely different from the previous one or the styles are missing or something is completely off they all have consistent ux all of this was going great to be honest until something happened and that was around the angular 17 launch and this happened
angular changed their logo and now you can imagine what would happened I was left with 80 projects having a hardcoded logo and that was a disaster so if you remember my generator of app template I had the image tag using this hardcoded Source attribute and when generating the applications all my applications essentially got this toolbar with this hardcoded logo which means that I had to go to
each particular project replace this whole code manually and use the new logo and as you can imagine this this is what I was feeling like that now I have to do a lot of work even if I have NX if you ask about what the goal was in this case is to go from this where we are having a toolbar that has a hardcoded image and the
title and the info button to be something like this where I could have the header component one single component and I could just use the templates right there because if I needed to change anything else at all let's say add anything on top of the header I would have to redo everything on at projects I don't want to do that so this was the goal so having
the HTML like this and having the template something like this so each new application has an app component created which has the header comported important and that means that the header component will be used in the template as well so this should work just like this but as you can imagine as I'm thinking about all of this I'm already too late because I cannot now use my
generator this code that you see is for the generators this is when I create a new recipe for my readers and once I've created a recipe I don't create it again so if I'm updating my generator code it is still too late because I already have all the projects now what I need to really do is to replace all the headers somehow so as I mentioned I
already had created the apps so I could not use the generators in this particular case so what did I do then what is the solution NX has something called migrations which you can apply to the whole project and it uses a similar concept like executors so when we run executors we run a nodejs script essentially which goes into the project and takes the project tree which contains
all the folders and all the files and then you can do manipulation on base of that so the goal was in our case to use the NX migration to go both into the start and the final workspaces and replace all the instances of the headers that was our goal so what I ended up doing was I wrote an NX migration script that would replace all the toolbars
in the projects and I quickly want to just show you the migration and how it looked like so if I go to my code base this is how it looks like so here if you look at this you can see that I have a script which is pretty straightforward and if I just show you the gist of it it goes ahead and it takes out first the
toolbar and then it replaces the toolbar with Ng CB UI header that's one part of the puzzle so I need to replace the header in both the template but then I also need to add that to the types file as well so I do that later on in here and I add essentially the header component as an import in that particular file and I also add that
to the Imports of the angry application so that's what this whole commit did Al righty so that means that I was really able to do something interesting and that is replacing the whole code but in order for the migration toen happen you need to do two different things one you need to create this migration script and I already showed you the file for it the next thing
that you want to do is to create a migration. Json so my package or my plugin which is Angie cookbook recipe inside that I created a migrations Json and inside that I added this generator and named it replace app toolbar this is going to be executed by NX if I set up everything correctly and that means I'm telling here if the version of the plug-in that I'm
using in my workspace is 0.0.2 or above that's when I have to run this migration and I'm giving the path of the migration file as well which means that if I just go in my workspace and run NX run migration or NX migrate run migrations it will automatically run all the migrations for me and replace all the toolbars so that happened and finally I was able to
run the migrations and I could replace all the headers so if I show you this right now on what the migrations look like or what the commit look like I think you'll be really amazed so in here you'll see that we are at start apps and here we are at chapter one and in chapter one the only two files are updated that are the HTML and the
module. TS so in the HTML you can see we replaced all of our template with just the ngcb UI header that the that is the component that we used and in the app. modu TS we imported the header component and we also imported the header component in the Imports of the module so some of the applications used Standalone components some of them did not so in some
of the cases you will see that we only updated the app component TS and not the module. TS but regardless we were able to go into the whole project and I could run just one script and it would run everything for me I don't have to do this manually and this is the final result that I want to show you if I look at the final thing
that now the reader see this is what they see now we have the new logo yay we also have the title and we have the app running inside that and you can see how cool this is I can essentially drag and drop this in here I could move this here and go inside and see all the values here as well and then in the future since I
already ran this migration and I'm using a component I could also add this cool thing here as well so more components or more updates can be done now easily with all this magic that we have talked about so having said that let's summarize what we have talked about we moved over 80 projects to just two workspaces using Annex that's super cool we also discussed how NX generated
works when you want to scaffold new projects so you have a template that you can use and that template is going to be used everywhere we also discussed how you can run anex executors to run something after your projects have been created or even the projects have been created but they help us make changes to the project or the files themselves we also discussed how we can
export Global styles using a library and then using them or using the NX generators to apply them to all the projects that we create and finally we also talked about how to compile the tailman CSS Styles one more thing that I've not mentioning here is that we also discuss about how NX migrations can help us do you know a lot of significant changes in the whole project
and can help us recover from disasters so to say so that's the whole summary and with that I would like to thank you for Dev days Europe for inviting me for this talk I hope this was helpful for you and uh you learned something you can also go to the GitHub project because it's public if you search for angular cookbook second edition you're going to find the
GitHub link for this and you can inspect the codebase and you can check it out if you have any question just write an issue there or reach out to me on my socials and you can find me on Twitter as well at cod withen with that said thank you so much and if you have any questions feel free to go now for them thank awesome that sounds
good then so thank you so much everyone who joined and uh feel free to reach out on any socials and thank you have a great rest of the conference
More from this event
See all 58 talks →
Halil Ibrahim Kalkan: Building a Kubernetes Integrated Local Development Environment
45:20
Paco Orozco: Growing at the Edge: Doubling Traffic While Changing the API Gateway
45:03
Viktor Vedmich: Ideal Blueprint Versus Reality for CI/CD Pipelines
46:03
Koray Oksay: Continuous Deployment: The GitOps, The Pipelines, and The Ugly
43:03