Juri Strumpflohner === Juri: [00:00:00] Hi there and welcome to Pod Rocket, a web development podcast brought to you by Log Rocket. Log Rocket helps software teams improve user experience with session replay, error tracking, and product analytics. Tried for free@logrocket.com today. My name is Paul and joined with me is Yuri Str Flounder. He's the senior director of developer experience over at nx, and besides for working in over 10 plus years in technologies like java.net node desktop, mobile, and beyond, Yuri is joining us. Today we're gonna be talking about architecting your project and the way you're setting up your project, and a broader scope, is a monorepo only for mono repos or can we modularize code? In a different way. Welcome to the podcast here. Paul: Hey, thanks for having me. Juri: So recently you gave a talk, , next Generation Code architecture for building maintainable node applications. I think the talk really stirred some inspiration in me because you're talking about we can modularize code without being this monster [00:01:00] monorepo for every scenario. And it's just a different approach. So I'd love to step into that. What inspired you to come up with this talk? Is it something that was happening over at NX with new features you were releasing? Is it something that came from personal inspiration? Paul: , both, , , , because you mentioned I've been working also in.net and Java backends and stuff, and so structuring code in the best possible way is something that I strive for, so I remember back, like when I did a lot of net development, , we tried to figure out like, how do you modernize your backend to not have one gigantic app and just separate by folders, and so we started like in, in net there's the concept of DLLs, which kind of libraries, and so we started splitting out stuff into smaller, fine grid libraries. Like the usual, hey, there's an API layer. Then there's just more the business layer, the data access layer, like that's the first things that you start usually with. And then we start going a bit deeper, , not just maybe by like horizontal types, if you want, the typical pizza box architecture, but like even splitting by domains and that type of stuff. So it was always something that [00:02:00] I had played around with. And, , to the next part. We have been thinking about that there as well. Because for those that don't know, an access is a development tool, that aims to increase developed productivity, , in a variety of different ways, and so Monorepo is where we started with and or where we're still big obviously, because that's our origin story if you want, but a lot of the concepts that we have developed there, In particular about structure, modernizing code can easily also apply to non, right? so that's kind of where that originated. And so why we sat down and thought about Hey, can we just add, give that flexibility of modernizing your code also to those people that are like looking at Monorepo and they're like, oh no, that's not my situation. I don't definitely don't want monorepo, I just wanna single application. Juri: And if anybody listening wants to learn more about mono repo's, and if you're interested in what Yuri has to say in our podcast today, , he has been on in the past, and definitely tune in to check out some details if you want to hear more of what he has to say. Now Yuri, you're saying like a mono repo. Some people might say, that's interesting. I want a single application, but it's not for me. That's not my situation. [00:03:00] Do you think slapping the monorepo bandaid on every project is like people do it when they don't have to do it? And that's why you're giving this talk and looking at these other solutions. Paul: Yeah, exactly. So like when I talk to users of an X for instance, but also users that might potentially want to use an X, like a lot of the things that I hear is Hey, I looked at it, it looks interesting, but I don't really have a monorepo. At least not now. And that at least not now, comes up very often because what usually happens, or what often happens is you start with a project and this is just one project, and then you develop on it and you. Keep going. And then if you're already familiar with the concept of the mono repost, then you were like, Hey, I need a second kind of related, so you jump on a mono re and do that. And then there's the second side of users, which are really passionate about an X. And so they just use it even though they have just a single lab, so they already use it in a way where they have one app, but they leverage the modularity approach that the next can give you, , with libraries and all that type of stuff. But like technically it wasn't really a monorepo, but it [00:04:00] still was set up in a Monterey profession because, That's what it was thought about, that was the plan of an ex in first place, and so that's when we saw those two things, we were like, Hey, it wouldn't be that hard for us to support like a single project scenario set up, which we then called standalone projects or standalone applications because just one, but still leveraging some of the functionality X provides. And so that's why we jumped onto this, and to kinda potentially to remove a bit of fear of people that see mono repos in the first place, , so that they can still benefit from some of the underlying tooling and then transition to a mono repo if they really need to at some point. Yeah I would definitely wouldn't jump immediately onto it just for the sake of it. , if you already know you're gonna have multiple labs, sure, go for it. But otherwise, what we want to provide and what we are currently looking into is provide a transition path. Where we say Hey, you can start with a single app. That's what you need right now so you don't overcomplicate your situation. But we will have some good migration paths along the way. , when you then come to the point where you see, Hey, I wanna add a mobile app or split up my [00:05:00] application in multiple ones, and so you can transition into a monorepo. Cause obviously we know how that works as well, so that's the idea behind it. Juri: What's an example of something that you might want in NX or in a monorepo setup that you took out of, or boiled, reduced out of the standalone project. Set of tooling. Paul: Yeah, usually, , it starts like in a sense that if you have a single application right, and you start splitting it up, then what often comes, , you come to the point where you say you wanna deploy it differently, in the sense that you have that application right now, you just deploy all of it into single deployment environment. But maybe some part of the application are more user facing, some are more like, , backend, like administrative phasing where like you're, I don't know if you have a, an online shop or something like your resellers jump on and add their product. So those might have a lower requirement on the scaling, for instance. And so you already start seeing those patterns as you create a bigger and bigger application where you could say Hey, what if we deploy that like really high, on, on, on servers that are highly capable of like [00:06:00] lot of traffic, and then have a second piece of it which doesn't need that high of scaling. And so we can separate it out and maybe they have even a different cadence of deployment, and so that's when you have that transition point from, Hey, I have my single app. Now I wanna have multiple ones. I'm gonna split that single piece up into to smaller pieces. And deploy them independently. So that's one common thing that I've often seen, or even people going more crazy if you want, where they start with a single application and then they want to go the micro front end microservices route. So they have not just like one afterwards, but they have one per. Business domain area of that product, because they want that independent deployability and all that type of flexibility. And so it's a good way because you don't necessarily have to go full in from the beginning because setting up like a microphone and or microservice infrastructure from the get-go might overcomplicate things, because you might not need it. Juri: There's a lot of guardrails that are needed. Paul: Exactly. And so we don't want to have that super complex setup initially. We want to [00:07:00] rather have you focus on actually developing your product and then go and then split it up at the later point. Now that's where my talk, , went a bit into that direction, focusing on notes specifically like on backend applications. But we have seen also similar situations on front end where. Like companies said Hey, we wanna go micro front ads. We were like, why? What is the business case for it? What's the use case? And so you come to the point when you ask a lot of those questions where they say Hey, actually we just wanna have like modular approach, where we ha wanna have everything nicely separated, such as we can have different teams working on those different areas. And then maybe we need the separate deployment, but only maybe right when we see like we hit roadblocks down the road because one team. Moves faster than other and doesn't want to wait. So in that kind of situations, and so if you manage, and obviously it's tricky, but if you manage to structure your application in a way that you can have it modularized along the way already. With some kind of in back in the back of your mind, like we might split that up at some point in the future, so you can already [00:08:00] start to put some guard rails around your domain areas and structure code in a way such that becomes easier. It's never easy, there's no magic button that you press. Now out of a sudden you have microphone ends. But we try to help you bit on that way by providing some tooling around it. Juri: So you mentioned in your talk it's really beneficial to think about separating your app and your business domains. You have the sales, you have the data layer. Where do you draw that line about? You guys, maybe a microphone end would make sense, but right now we're gonna draw that business domain a little higher up on the abstraction scale because I feel like microphone is, correct me if I'm wrong here, but microphone ends it's almost like drilling down one layer deeper and you're reaching into the deployment and operational side of the thing. Okay. Paul: Exactly. When you are at like microphones, microservices, you are ready at the very end basic you wear, you just deploy stuff, but in order to be able to do that, you have to have the structure be beforehand at, on your code level to be even able to do it [00:09:00] to be even able to just deploy a single smaller piece rather than the big monolith part. So yeah, what I talked a bit about in the talk is mostly. What can you do to not have that, what I call like monolithic code base? , , and I target specifically about Node, but it's not really limited to that. , but if you look at Node for instance, it is really flexible because you just set up a new app, with Express, fify, whatever. , I actually used to find an example because it already comes with some of that modularity in mind, so it has some features around it, so that's nice. But in the end, whatever framework you're using, usually what people do is they set up a single project, they have their source folder, and then they just have the code in there right now. Ideally, you're already separated by folders inside there. So not just Hey, there's API folder, there's business layer four, data X layer folder. Because then you have the code really for every feature spread across all these different kind of subfolders, which is really messy. But rather the idea is try to group it into its own folder, so if you [00:10:00] have an online marketplace, or let's say Amazon, or some sort of web shop, like. Have your products, features or products area in the products folder, maybe you have different ones. You have product lists for just the visualization of the list, because it might be that complex, with searching and filtering and all that stuff, but have it grouped together as close as possible. So pieces that should stay together are already grouped together and that's what I was advocating for. Now, the problem is if you have it in just a folder structure, Then the problem is that you can really easily grab across those boundaries, because they're very lightweight. You just import something or what often happens with myself as well as you're coding visuals through the code, or whatever Id you're using, and you create, you get those outer imports of types or functions you're missing and it just grab from some other folder because it happens that there is one there, But, Like what happens if you think about it along the way as you think gets bigger is it becomes really messy because you have all these cross references and it's really hard to separate those pieces apart. And so that's where I digged into Hey how can you [00:11:00] fix this? What are structures like we thought about, , specifically talk about an x, , of how you can actually, , help with those type of things. Juri: So do you feel like. We're moving from, , localizing our code in for its semantic infrastructure relationship into more, like we should move towards relating our code into folders for its business logic. And if we keep the business logic closer together, we're gonna have a cleaner spider web at the end of the day. Paul: Yeah, exactly. It goes in that direction where I feel like personally, for instance, you can still have. The like API business data access, , separation because it is useful, right? if you search your API stuff, you go into that folder, but have it within the actual domain area, so if you have, let's say a products orders, , I don't know, authentication, my account profile management kind of thing. Features, so you have three, four of those features then have like sub folders within those folders. So have a source product folder, and within that you have an API folder, a business folder. , so , you [00:12:00] can group it still within the product. So whenever I need to look up something of the product, I know I go into that folder and I work in there. And the thing I mentioned that is because then it gets kinda outta consistent. Like you have that. Integrated piece, basically where people work on, because if you are working in a large environment, changes are pretty high. That if you're in a products team, That's where you're gonna work, it's not that you're gonna grab into the orders, maybe orders you might touch because it's kind of closely related, but usually just stay within your area. And so it's much better if that is also on the folder structure visualized or separated because then you run into less travels, get merges like you it's just more consistent in that sense, and so that's where I pushed, , people towards also in a talk. The problem is, the thought is they're true lightweight, usually. And so that's where the whole concept about local libraries came in which then I dived a bit deeper. Juri: Local libraries. That's different than A package in a mono repo slightly different. I'd love to get into that, , Yuri. But right before we do, I just wanna remind anybody listening that the podcast [00:13:00] that we're listening to right now is brought to you by Log Rocket. Log. Rocket offers session, replay, issue tracking, and product analytics to help you quickly surface and solve impactful issues affecting your user experience. With log rocket, you can find the issues faster, improve conversion in adoption, and spend more time building a great product and less time debugging. So try for free@logrocket.com today. So Yuri, coming back into what we were talking about when you guys are developing this standalone application with the libraries inside, does this sort of urge the team to almost want to rewrite a spec for how. Your everyday node developer sets up a project because you must have been tackling like, okay, how does a typical node developer set up like a FFI application? Now how do they do it? A standalone project? Are there some like conventions that you found you dug up that you maybe want to broadcast to people? You're like, this should really be in everybody's toolbox. Paul: Yeah, exactly. The, what I did is I looked [00:14:00] at a couple of different, like setups that people provide, in terms those frameworks like Express coa, ffi, and some even don't have a C at all. So all you get is basically, hey, there's an MPM package here. , and just install that and then, Just go ahead. And then you create your controllers, your ip, api, layers, middleware whatever you're working on. And that is also kinda legit in a sense that those people work on actual framework, so having a C L I tool along the way that you also need to maintain is a lot of effort, but still it's a very, on the other side, a very good thing where, Newcomers to the framework can use it quicker to spin up a new application and explore it and play around with it. And so for instance, looking at Fify, they have a c l I, , and actually talked to Mateo, , which is, , the creator, Mateo Colina from Fify. , and he mentioned, yeah, we have it, it's like a nightmare to maintain because as I mentioned, like he, he just drags it along the way. But in the other side, he also says a lot of users jump onto it because, Hey, let me just quickly generate an application. And however, already a running Fify app and then play around with it, learn [00:15:00] about it. But then I probably might want the setup, like a proper setup that's not using fast fsi, because that's not like really meant for production immediately. And he's working on some stuff there as well for deployment and more. So feel free to dig deeper on that end if you're curious about, but it's where, which kind of sparked our interest because. At the next, we provide literally cli, we, that's what we develop, it's a common line interface, a developer tool that allows you to scaffold new projects. And so that's why we started also looking into that area and say Hey, We have already those code generators, so let's look at, for instance, what the Fify provides and what that generates to you is just source folder and a sub folder. That's it. Like routes for just to demo how that works. But it doesn't really impose any structure, , per se, so that's where you then have to dig into the docs and explore, hey, How would you do this and how would you structure, like you have to really go deeper, and so our idea was like, what if you provide like a custom on the next site that uses an access engine, if you want that allows you to have a more if slightly [00:16:00] different, slightly opinionated, more opinionated setup, where you say Hey, you generate an application like this, so it gives you almost the same setup as to Fify, c l i. But then thanks to like further generators, you can create what's so called local lips. So there's just a generator that creates a library. If you, within that workspace, which we obviously use already in our monorepo setup, but now you can also use it in some migratory folder within your standalone app setup, and just for the purpose of modernizing your code, because now if you have it in library, things become already much more explicit in a sense you have. Some public entry point, some API where we expose stuff, other stuff that remains include just visible within that library. So you already impose some of those structural things, which help you think more about, Hey, This is my product domain area. What do I wanna expose? What should stay within my product area because no one else should use it. So you can see like how you already start thinking about what is my API to the other domains within the [00:17:00] product. And it's still a single application, we're not talking about monitors and anything, but you already start to get into that habit of, hey, this is a way of structuring it need to think about all those details basically. And so . The thing that comes out of it, which is our hope, is you create a better structured application just because of that already. Juri: That structure that is imposed at the beginning when you're using the C L I and you're getting this assistance and creating a good hygiene. What tools exist within NXS and standalone projects to help maintain that hygiene? Or even, let's say you want to maintain it, , there's a lot of fuzzy questions you might be asking yourself is this one domain or is it another domain? On both of those fronts, how can you tackle that as you go along? Paul: Yeah. So the domain part there's two aspects I think in this. One is the, from the pure tooling perspective, like what does the tool in this case and X give you to kinda implement some of those restrictions or like opinionated setup or whatever. , and then there's , what are my domains? Like where are the boundaries? Like how do you structure that and. Let's [00:18:00] talk first about that, because I think that's a more difficult thing, Juri: yeah, I totally agree. Paul: yeah. Yeah, because it's really dependent, like tooling is easy, that's just, Hey, there's it's the Juri: You learn it. Paul: Exactly. You learn it, use it. That's it. You customize it maybe to your needs. That's it. The other thing is more about, , like reasoning about your product, about your, whatever you're developing. And so it's really depending on what you set up, that's what defines your domains. And , by the way, , when I talk here about domains I'm talking about domain driven design, so that's also just one. Like aspect or one way to architect your system, there's others as well, potentially. So this is just one methodology if you want, where you start thinking about, hey, what are the actors, what are connections? And you even see that when you talk to your like customers, if you have a very customer facing product, when you talk to your customers the way they talk, like those domains are usually reflected right in their business. So that's where all those things come from, where you then also cut the boundaries because. You always want that the domain is attached to business, basically. Because then [00:19:00] as the business evolves, your domain representation, your software evolves in the same pace. So that's the idea behind it at a very high level. Now, that's good. I'm on tooling side. I'm not a domain driven design expert whatsoever. , I've looked a bit into it, but like that's kind of the idea, from the tooling perspective, it's, as you said before, like you, you need to have some sort of mechanisms, basically like implementations at hand that help you then implement such a domain-driven architecture. And in an X, actually we don't really. Push to the domain-driven architecture at all. Like it's more general purpose obviously, because like we don't wanna enforce some pattern on top of what you're doing. But what we provide in terms of helping you implement such mechanisms is mostly those generators. There's code scaffolding mechanisms that help you like set up quickly such a library. Because obviously if you want to have those libraries, but you need to manually spin them up. Create them or maybe just copy paste old ones, remove code. That becomes tedious, and so it shouldn't be something that kind of stays in the way just then people then avoid it and then still stuff everything into one or two [00:20:00] libraries because then again it doesn't make any sense. And so those generates help. We like do that initial scaffolding, that's one thing. And the other is what we do is also provide like mechanisms around them enforcing those things because. . One thing that we have seen and that comes from the Monorepo setup is if you have an application and a ton of such libraries, then yeah, sure you have a good structure. But again, just initially nothing prevents people, like someone joins the team, doesn't know about all the details. Obviously didn't read the docs into manuals, because who reads them anyway? And so you just jump in and start importing stuff across all those libraries and all those packages. And so then you need not just something that helps you do the initial setup, but also something that helps you guardrail that along the way. , and so that's when we referenc in an x started introducing what we called module boundaries, so that those are basically lin tools. So , custom lin tool, basically where you define. Like what the relationships look like between those [00:21:00] libraries. So what library can import from which other library, and you define those rules, which is actually pretty simple to, to draw out once you have your domain areas. And that's then enforced on ci. So you see I will break if you import for some project, which is you not allowed of. And so that's when then your bells like ring is wait a minute. And you look at it and maybe it's legit, maybe you need to adjust your rules because your product evolved, but maybe it's not. Maybe it was some accidental import by BS code, so you go and fix it. So that's the kind of idea to have those generators, but then also things that along the way help you also maintain that state, or kinda alert you whenever something goes wrong. Juri: I could see this being insanely useful if you have a standard way you write. A database service data access, and you have it in every domain package. And I wanna make sure I'm right. I'm pulling from the right one. This is a problem I have personally faced and it has really annoyed me. And to have a lint tool, so it must be some static analysis, Paul: Yeah. Yeah, exactly. The reason how it works is basically an X behind the scenes has the concept of a [00:22:00] project graph, so it follows the imports of like type script that you're using. And so it builds up that graph and understands, hey, these are the relationships. And so it knows all the project and there's some manual steps that need to do, which is like whenever you create a new project, you give it, , a tag or some identifier where you say like, Hey, this is Of scope domain. So this is my scope product. So this is from my products domain and the type here is a data access library. And so you give it to the project definition. That's basically a tech that you define, which can be an arbitrary string. And then in the rules, you basically say, Hey, everything that is from scope, data access, or type data access can only reference other type data access, for instance. And they also need to stay within the same domain, or they can go across domain, but you define it basically. And then, yeah, as you said, why a static analysis, having a project graph, having that set of rules, it just walks through basically and looks like, Hey, this is a product that is part of that scope, that domain area is of this type of library. Looking at the rules, is this loud or not? And so if it's not [00:23:00] loud, just raise the Lin Tower. , which by the way, like before I mentioned CI checks, it's obviously checked in ci. But Lin has really good tooling support as well in the IDs. So if you have , the Lin Extension install in MES code or whatever you're using, you will see also you type that import. It will immediately have like squiggly lines below and say Hey, This is not possible because you defined a rule that says you cannot import from that R library. So it's from the very perspective, I think it's really nice because then you immediately see what's going on and you just fix it because maybe it was an accidental import. Juri: This really reminds me of, , like a swagger doc or open a p I spec where you create your contract and then it's amazing to have this if you have like a team of more than like a few people, because you can go out and all have some agreement about who does what. Paul: Yeah, exactly. And I think it's also these rules grow, like you start small, you have maybe one or two rules because you don't really have all that separation, all those different domains. And then as you go, you just add new ones, like , you grow that with your product. So it's never, , like some [00:24:00] huge overhead, which by the way, I also suggest to do because it's more problematic if you don't do initially. , when it is small, but then when you have that gigantic application with 50 modules and whatnot, like you're like, Hey, we really need to have those rules. And really someone needs to sit down and write all those rules on you will get a lot of lin errors for sure. So that's when it's painful, so the whole idea is start building it up as you start growing your project and then over time, basically just adjust it, , to your needs. Juri: So if people wanted to try this out today, they want to try standalone project, , Yuri what command do they run with the product annex delivers the c l i. How can they get started , and is defining the rules? You said www you do it in some definitions file. Paul: Yeah, exactly. Yeah. So to start a standalone project, , you can just go to nexo dev slash docs. , you will get there to the starting page, which has already like d different type of setups, basically, where it says Hey, you on a Monterey boat? No, I want a standalone project. Okay, which one? And so you can then go through that and we'll have all the commands that you will need. You kinda get that started.[00:25:00] , but there're basically like presets or templates that you can pass to the CLI to set up a new project. So we will know, hey, This is a note project, and then go with that and kinda ask you a couple of questions. What do you want? Which type of setups do you want? And then go ahead with that. So that's the approach that you could take. And then from there you can add those rules, which are already like predefined in the es lin RC file. So it's just a es lin definition file where you define all those rules. So if you use such a standalone setup, it'll already come preconfigured, which an empty rule set. And then on top of those, you just keep adding basically the rules that you wanna define. Juri: Do you urge the next, , the person who's listening to this, the next project, they try and know just to set it up with this simple guardrail? Cause it's like a no-cost ad, to run this. Yeah. Awesome. Okay. Paul: Yeah. I would say just even if you just wanna explore it, because for instance, if you wanna start, let's say create a new, , fify node app, just play around, try to use such a standalone setup. And just create a bunch of libraries [00:26:00] important. See how it feels, because you can literally do it in five minutes, and then see how the rules work and just to get a feeling of how the whole setup behaves. Because then you can also kinda understand how would this apply to my current situation where I maybe built an old application, and then wanna deploy it somewhere. And then it comes also with other tools on top, we keep improving that as we go. Like we have. Want a docker. Okay. Like just run and generate sets up, , docker file for you. That allows you bundle it up and then just chip it to some environment. , reason we all started adding serverless support and edge function deploy. So is something that we start evolving. So we literally just added this two months ago or something where we started looking more into streamlining the whole node setup and looking into use cases. Juri: It's like a the cli of professional boiler plate. It feels like, to me and beyond because it's These are things I spend hours setting up like a Docker composed file and like making sure like it's formatted and has the right version. I don't, just stupid stuff like that, that it's just coffee and pasting. I really wanna try it out. Paul: [00:27:00] The whole idea behind is also because an X itself. And it's also, if you look at NX dev slash docs, there's a, I think Y NX menu entry or something which kinda explains the overall architecture which might be really useful if you start using nx. Like never heard about it or maybe just heard about it, but never used it. Kind of look at how it is being made even an X itself's kind of modular or build. So it has all those plugins. Where at the bottom there's just an X that runs tasks that make sure those tasks run efficiently. It has the base cli and then the generators come really with those plugins. So the same thing we talked about here, , while that works on Node, , which we talked about today, but it also works on React application on the front end, it works because those mechanisms are really general purpose. If you think if you have. Front end libraries or like backend libraries, it's always libraries, where we might need to guard rate boundaries and all that type of stuff, or we might need to generate code. So that's kinda the, the idea which we had behind it. Also, why we expanded it to all those different environments because it's, it applies easy to, to different kind of technologies if you want. Juri: Uteri, , thank [00:28:00] you for your time coming on and talking to us about. The standalone libraries and what NX is coming out with. We'll mention one more time the name of your talk. It's Next Generation Code Architecture for building maintainable node applications. I reckon you're gonna be working on this in the coming quarters and months still as time goes on. Paul: Absolutely. yeah. Juri: Awesome. So if people want to find out more, will you be posting on Twitter? Paul: Yeah, so you can find us on Twitter, like either NX or dev slash docs. That's where we push like our documentation updates, , for just updates purely on new features and stuff. The best would be Twitter, which is at nx dev Tools. Everything written together like one word. , and the same hand laws also exists on YouTube. , we actually push a lot of videos there, so usually whenever we have a release or some new exciting feature coming out, you will. 1% find a YouTube video there or a short one that kind of walks you through it, explains how it works, , stuff like that. So that would probably be the best way. If Twitter is not your place, just go to YouTube. Juri: Awesome. Well, Ity, thanks again. It was a pleasure to have you. Paul: Thanks. Thanks for having me.[00:29:00]