 Oh, so sorry. Once upon a time, there was a front-end developer named Alice. She was working with a far-spaced small organization which was still growing. She was given a project to work on a front-end application. So she thought, let's start with a single-page one. The journey was pretty humble. It started with a single-page application, a couple of components, nothing fancy. It started adding pages. When you have more pages, you need a router. Then came the data management part, something all developers or front-end developers did. That's where the Vuex part came in. New shiny features were added. It could be payments. It could be chatting, messaging, and whatnot. So you see where this is going. And of course, new team members. Alice alone cannot build this big of an application. So obviously, there are going to be new members coming into Helper. What does that mean? A large-scale application. Now, can I have a show of hands? How many of you know what a large-scale application is or are working on a large-scale application? Oh, god. OK, so sorry. So what is a large-scale application? How do you define large-scale application? See, in my opinion, I think it comes down to three points. One, size. Large-scale applications are large. Like, they can be sized in terms of lines of code, number of files, assets. You know the story. Complexity. Yeah, intricate business logic. If, is, this, that, whatnot. And of course, team members. This is something that ideally people might not speak about. People might, or something you would not see in a presentation like this. But trust me, it's a code that says what one developer can do in one month. Two developers can do in two months. So let's take a look at how many large-scale applications are there in the wild. GitLab. Now, this is my personal favorite, by the way. Like, you would not find a better large-scale application out in the wild, open for reference. Like, that is completely built on you. IBM Cloud, the big daddy. And of course, Behance. That's where you all get your design inspiration, right? So let's see how our favorite GitLab handles what it does. Let's see how large GitLab's application is. They have 450 plus view files. And that's just view files. I'm not even counting the number of JavaScript files or assets. Complexity. If you go to the features page, you will see that they have more than 50 plus advanced features, CI, CD pipelines, repositories, boards, Kanban boards. You name it, they have it. Team size. They have a dedicated team of 50 plus developers working only on their front end. So you know the scale. Well, if you had an application this big, how would you structure your application? That's the first question every developer is confronted with when they start a new project or when they're currently working on a project that's going big. So let's take a look at what the view, a typical view app structure looks like. Pretty humble, right? There's a source folder for all your code. There's a directory that contains a components, a directory that contains your views of pages, a main app.view component that brings all of that together, a main.js file for basically as a front runner, a router file, and a store file. You know the story. Think about it. 450 plus view files, intricate business logic. Multiple developers working on it. Will this structure scale? I think not. Let's take a look at GitLab's view application structure. So they have a JavaScript folder because it's a monolithic application. They have a JavaScript folder where all the JavaScript resides. You're looking at one folder called Boards, which belongs to their feature, the Kanban Boat feature. If you look inside that, you have a directory that contains all their components, the store. That's basically your state, getters, set, mutators, and actions. Services, probably classes that are single terms that make API calls. Models for basically that acts as data transfer objects, mixins, and the index.js file to bring all of that together. And I'm skipping the other helper files. But you see where this is going. Let's see what this gets us. Each module and feature lives in its own directory. You saw we had a directory for Boards. Similarly, we might have a directory for repositories for, let's say, if they had a messaging feature that would reside in its own directory. And each module has its own set of components, services, models, helpers, you name it. So what does that bring us to? What's a module? This is the term that's thrown around everywhere. And yet something even Google cannot explain correctly. Like Google has something along the lines of rocket science or something. Go check it out. As a front-end development isn't rocket science. So this is what a module would look like on a high level. Components, routes, pages, services, assets, utilities, and store. You get the picture. All these modules come together like this. This gives you a better viewing or, let's say, a better conceptualization of how your application might look. The question would be, what would a module do? Why is it that you need a module? Why not simply put together files in different folders and call it a day? Well, let's take a look at an example. This is something we might have seen probably 100 times. There's a path. When you visit this path, this particular component gets rendered. Simple, right? Let's visualize it. This is the profile page component, which is rendered on this particular route. There's a data property for user, which is empty or null by default. And when the component is created, you basically fetch the user by its ID, which sets this user object. Simple, right? And once this user object is set, all you do is you render the profile card, right? Let's take another example. Same path except that something nested, which renders the profile settings page. Same visualization. Get the picture. It's almost identical. Couple of problems here. Duplicate data fetching calls. What would happen is that if you visit the profile page directly, on creation it would make an API call. If you visit the settings page, it would again make an API call. Even if we use a store, which of these pages or components would have the responsibility of making the API call? If you put that responsibility in the profile page component, and if the user directly visits the settings page component, the data won't be there. So you see where the problem is going. Also, what about other pages? We have profile about page, photos page, et cetera, et cetera. So bring all of this together. Let's look at how we would modularize it. So on this particular path, we will load this particular component or render this component, the profile module. And we'll have some routes, which will act as children routes for this particular module. If you go inside this particular route, it will render the profile page. If you go to user slash ID slash settings file or route, it will render this particular component. But before this, what would actually happen is it would all go through the profile module. So when you visit this particular route, the profile module will be rendered. We'll have a computed property that observes or gets the data from your store. And when this component is created, we'll basically call a simple bootstrap method, which is nothing but our own custom method or a convention that we would like to follow. And we'll have this router view inside it, which will only be rendered if the module is ready. So how do we get there? This is the simple bootstrap module for our example. We'll pick the ID from the route, and we'll dispatch an API call in our action, which basically sets the user object. So we know that the computed property or the getter will resolve to that particular object, which is like a truthy value, this one. So this would resolve to a truthy value only and only if the user is there. And who is orchestrating all of that? The profile module. So this is ready will only be a truthy value or will resolve to a true value when the bootstrap method is called on creation, which makes an API call, sets the user in the store, and this computed property results in a user object. So these are the four points on how you can get there. Add a wrapper component per module. So each module will have wrapper component. We fetch all the shared data inside that module that is required for your child routes or for your child components in this particular module. It's not just fetching data from the API, but it could be a lot of other stuff. We'll add a router view for rendering our child routes. And that router view will only be rendered when the module is ready. So now the module component has the control to basically render or decide when the child routes are rendered. So all of that is aggregated into your module component or wrapper component. So instead of this, try doing this. This is exactly the same thing, except you have one module to orchestrate all of that, which is outside of your view router. And then later on, you can bring all the routes from all the modules together like this in your view router. You see how simple this is? You have your applications routes, which are primarily static pages. And for each module, we will have their own set of routes which you can load directly here. And this is how the structure would look like. Your application routes are made up of your module routes, like n number of modules you have, all of them brought together. Let's take a look at a quick demo. This is the non-modularized view app for example, which is your typical view app with the router in a store and a couple of pages. If you visit this user's profile, you see the URL, right? It's users slash one. So this is where our profile page component would be rendered. If you click on this link, you will be taken to their about page, which in our example is more or less like the settings page. Just see what happens. You saw a loading indicator there. So what is happening is that whenever you see this loading indicator, it's actually making an API call. So it's one when you visit the profile, it's fetching the user. And again, when you visit their about page, it's doing the same thing. Let's see how our modularized version works out. So if we go to this user's profile, we saw the fetch or the API getting called and the loading indicator coming in. Now if you visit the about page, it's no longer there because we don't have to make an API call. All of that was done by our module component. These are just pages that are rendered only and only if or only when that particular data was fetched and the module was bootstrap, meaning it was ready. Let's dive into the code for this one. So we have our app.view file. Pretty simple, right? Just have a route of view here. Nothing fancy. If you go to the routes file, you see there's one page to load our home page and then we have our modular routes from the profile module. If you go to that particular directory, you can see that we have a modules directory which contains the profile folder for the profile module and inside that, we have our own store and we have a router. Now these routes are exactly taken from the example. You have a path or a component that's rendered on this particular path and we have two children for that, a profile page and a profile about page. Looking at this particular outline of routes, it's very easy for anyone that's working in this particular framework is that we'll only render these routes if that particular module is ready. The control is taken from the router and into that profile module. Now that profile module will decide when these routes are rendered or these components are rendered and before they are rendered, it has the ability to orchestrate or bootstrap or decide when it's ready to do that. Let's look at the code for profile module. We have a router view which will only be rendered if the module is ready. Else, we'll simply show a loading indicator. When this component or module is created, we simply call a bootstrap method which is something that a convention that I really like to follow because it makes it really simple to reason it with the overall journey of or the flow of the application. If you look at this bootstrap method, all it does is it takes the ID from the route and dispatches this action into your view extor which makes an API call, sets your current, sets the user data and that user is available here in this computed property and we get to decide when is the module ready. Right now, it's a pretty bare bones example where we have a user object returning a truthy value who basically state that the module is ready to get rendered further. In your case, it could be, let's say if you need 20 APIs, if you need to call it 20 APIs, let's say God forbid, then you can make all those API calls and only then you get to render or you render the child routes. But all of that logic, all of that control resides in your module or wrapper component. Now, if you go and look at the pages, they're pretty simple. Simply get the user or the shared data from store which we know is already there as this page would not have been rendered at the very first place. No we if, no hacky stuff of making or deciding whether to make an additional API call or not checking for stale data, none of that stuff. It's pretty simple. You know that when the controller, this particular component is rendered, you have all the shared data you need because that's all been bootstrapped or orchestrated by the module component. If you look at the about page, pretty similar. No API calls, no hacky stuff. And just to give you a brief look at how the store is structured, it's pretty simple. We have an action which basically fetches the user from a service which is nothing but a mock or let's say stop data. We simulate the loading or the API loading part using a set timeout and we then commit or set the user once we have the data. When we have the data, this triggers this mutation which in turn sets the user object which is the details or info about the object you're trying to be a profile of. When that happens, this particular computed property returns that object which in a predicate world is a truthy value. Bringing all of this together, imagine if you had 20 other similar modules, a profile module, a user module, a messaging module. It would be very simple. Why? Because then one module or one wrapper component will have the power of orchestration whether the child routes are ready or child components are ready to be rendered or not. So to sum it all up, split each feature into a separate module or separate modules. That basically means one directory for each module. Have one wrapper component per module. This will allow you to make orchestration or make your orchestration easier and you can basically have all your bootstrapping logic in one place. We bootstrap this wrapper component by fetching shared data required by the child routes. It could be fetching shared data or it could be doing other stuff, you know the drill. And we only render the child routes once the module is ready. So you see, you have the power to decide that or that wrapper component has. So, like I forgot to basically introduce myself in the beginning, but I'm Kunal. I basically lead a team of 15 odd people out of London and Bangalore. So I work at this company, which is a FinTech company, an year old. We have raised out of two locations and we currently handle 130 plus billion of ads in the matter. Questions. So we can take up up to three questions this time. Hi Kunal. So we spoke about multiple modules, right? So, if say for example, two of these modules need some generic code to be written, which is used by multiple modules. So where would the generic code go? Right, so add module pattern. What you can do is, where in all the components or stuff that resides, which is basically shared by your complete application. So what happens is that this is pretty similar to how Angular does it, but the thing is that they are structured or Angular designed in a way that makes modularity a first class citizen in the complete application. But in the world of you, it has to go by convention. So a lot of this cannot be enforced. This is the part and parcel of you that is so flexible that you can do anything with it, but it's not opinionated. So you have to set your own convention. Okay. Hi Kunal. Yeah. What are the techniques that we should use when we are managing such large scale application? So not only from code splitting or not bringing the webpack part, but in the view application, what are those techniques like? Generic techniques? Generic techniques, like we are well, like if there are more, like if we are using dynamic import API or something like that for the dynamic routing. So similarly, some other things like if you can share. I think a lot of things in large scale application, the problem that we also face and a lot of other developers face is consistency, which can probably be solved by a couple of things. Let's say for example, a lot of people keep strict linking, right? Their linking rules are strict. The other is documentation. People don't maintain documentation, but that's quite, let's say, essentially in a large scale application. Conventions, a lot of inventions need to be followed. So in a difficult manner, like if you're asking for particular techniques, there's not that you can primarily do in terms of like heavily enforced, but that depends on your use case. Like if you can come back to me with a use case, then I can probably give you a better answer. Hi, Kunal. It was a very good talk. And I'm working in a team where we have a store where we have multiple modules and the modules have started growing to like beyond 12, 13 modules. So we have just started debating among developers like this is not the right structure. There is too much of, no coupling between modules. They should be separated and all that. I could see that you've got different stores and it's not one store with different modules. So how do these stores communicate between each other? But all they have to communicate. So primarily what happens is that all of that comes together in your main store. So there's just one store and where all of this comes together. These are not isolated stores, if you may. These are just store definitions for each module so that they are cognitively separated. So let's say you're working on one module. You wouldn't have to worry about whether this affects the other module or not in general sense. But all of that comes together in one store. So essentially you just have one store but these store definitions are segregated into modules. Thanks. We can take two more. We have time. For particular module requires a lot of data. So how do you go about like instead of fetching all the data for just once for the module or like you go incrementally for child routes? Yeah, so the best part about this module or architecture is that you can have some modules as well. Let's say inside a user module, you had a profile module and a settings module. What would happen is that the data that's required for both of them can be put in the user module. The data that's only required for the settings module, let's say, can only be bootstrapped or fetched in that particular module. The thing is that giving the control to one component allows you to decide like where can this happen? It could be in further child modules. It could be in the high-level child module. So let's say it's an example from, let's say, React world or a few world for that matter that whenever you have shared data between siblings, you lift it up to that parent. Similar thing happens here. It's two sibling modules share some data and if you want to make sure that before they are even ready or rendered, the data should be there. You can put all of that or bootstrap all of that into their commentator. Thank you.