Angular Folder Structure

tl;dr

  • There’s a summary of our new folder structure at the end

Folders

I’ve been working on an Angular application for a couple of months now. One of the things that has changed most often is the folder structure. It’s something that I end up obsessing over. I’ve gone through a couple of different structures, and no doubt will change my mind again in the future, but wanted to share my thoughts here.

A logical place to start is the official style guide from John Papa:

I had initially thought that there would be a completely defined way of how to structure folders, but actually the advice makes a lot more sense. Different projects will simply have different requirements, and the idea of one size fits all, like so many other things, simply wont always work.

Have a near-term view of implementation and a long-term vision. Start small but keep in mind where the app is heading down the road.

That is taken from the style guide. Start small. The best place to start with that is the CLI.

The CLI of course produces a project and a good suggested default layout. I’m mainly concerned about looking at the src/app folder, where all the actual logic is going to go. It looks something like this:

- app.module.ts
- app.component.ts
- component1
component1.component.ts
- component2
component2.component.ts

In actuality each of those files is then broken down into their .html and .scss counterparts.

Pretty soon on, we want to start adding some services to share the logic between the components, and to fetch data. We then want to add some models to model the data. And then some mocks to test those services. At some point we add another few components, one of which is a login system, so we want a guard, a user component wants a directive, a dashboard component wants a pipe. We’re soon taken to the following scenario:

- app.module.ts
- app.component.ts
- component1
component1.component.ts
- component2
component2.component.ts
- dashboard
dashboard.component.ts
- directives
directive1.directive.ts
- guards
guard1.guard.ts
- home
home.component.ts
- login
login.component.ts
- mocks
mock1.mock.ts
mock2.mock.ts
...
mock13.mock.ts
- org
org.component.ts
- pipes
pipe1.pipe.ts
- services
service1.service.ts
service2.service.ts
...
service8.service.ts
- user
user.component.ts

The problem is those component names mixed in with all of the other groups of things. Adding more components just makes everything starts to feel more and more muddled… your head spins each time you look through the folder… things just feel ‘wrong’, and soon it’s screaming out at you, to put all those components… into a component folder.

- app.module.ts
- app.component.ts
- components
- component1
component1.component.ts
- component2
component1.component.ts
- login
login.component.ts
...
- directives
- guards
- mocks
- pipes
- services

Ahh... Simple and elegant. Peace and order has been restored.


Let’s get lazy

The app continues to grow in size. You start to read lots of cool articles, about this new fancy feature called ‘Lazy Loading’. It sounds great!

First task is to decide how to split up different features into ‘Modules’. You come up with a pretty decent sounding list. Now, where to put them. It seems pretty obvious at this point. The app/module folder must be the perfect place.

And everything is still great, organised in its own little folders, with a good flat structure.

- app.module.ts
- app.component.ts
- components
- component1
component1.component.ts
- component2
component1.component.ts
- login
login.component.ts
...
- directives
- guards
- mocks
- modules
- login
login.module.ts
login.routes.ts
- admin
admin.module.ts
admin.routes.ts
- dashboard
dashboard.module.ts
dashboard.routes.ts
- pipes
- services

You keep adding more components, everything gets broken into it’s own module, because the ability to lazy load is awesome. But once again, something starts to feel… ‘off’. The components folder is getting really big. It becomes difficult to find the exact components that you’re after, as they’re all getting muddled again. And as component structure turns out to be so useful, you split your previous components into smaller and smaller components. Single Responsibility Principle for components? Maybe it’s going a bit far, but it sure feels satisfying.

So perhaps a new naming convention is needed in the components folder? Something like:

componentName.moduleName.ts

- components
- details
details.admin.ts
- checker
checker.dashboard.ts
- login
login.login.ts
...

Hmm… doesn’t help navigation. Looks like we’re going to need a new folders:

- components
- dashboard
- layout
layout.component.ts
- panels
panels.component.ts
- login
- input
input.component.ts
- pwCheck
pwCheck.component.ts
...

Ok, now we don’t have a massive folder holding loosely related items. Instead they’re all a bit more connected, but with an extra layer of structure instead. Order restored…?


Pages

I spent a short amount of time looking into Nativescript and Ionic. They both use the concept of ‘pages’. I found that kind of interesting, as I don’t tend to think about apps as having pages so much, however websites typically do.

However, in Angular, a page is a component, or at least a collection of components. In our website, we very much have a concept of navigating to particular pages. In other Angular apps, this would almost certainly not be the case. So, I was keen to bring in the idea of pages in order to lighten that components folder.

In order to decide what was a page and wasn’t, I simply looked towards the url bar.

www.awesome-o.com/login
www.awesome-o.com/dashboard
www.awesome-o.com/organisation/robot

Here we have login, dashboard and organisation, which are already modules. But I felt that they could also have their own ‘entry’ page to go with it. It’s the page that the router will navigate to at these urls. In addition there is also a robot page, which can again have its’ own page.

In reality, each of these pages is simply a component. All I’ve done is change the folder extension from:

componentName.component.ts into componentName.page.ts

Again, I can imagine many scenarios where this wouldn’t be appropriate, but for our website, which has the more typical concept of ‘pages’, it works quite well.

Where to now put these page files. Well they’re closely coupled with the router, and for most modules, we have a corresponding page, it makes sense to put them in the module folder.

But wait, if we’re putting the ‘page’ component with the module, why don’t we just put all related components for a module in the module folder?? And while we’re at it, any services related to the module in with the module? Each module is essentially its own self-contained app.

What about sharing components?! Someone once screamed at me. The whole point of components is to make them easy to share! Well, we can have a shared folder for any components like that. Putting that altogether…

- app.module.ts
- app.component.ts
- modules
- login
- components
- input

input.component.ts
- pwCheck
pwCheck.component.ts
- pages
- login
login.page.ts
       login.service.ts
login.module.ts
login.routes.ts

- organisation
- components
- input

input.component.ts
- pwCheck
pwCheck.component.ts
- pages
- organisation
organisation.page.ts
- robot
robot.page.ts
       organisation.service.ts
organisation.module.ts
organisation.routes.ts
- shared
- components
- mocks
- models
- ...

So at this point… flat it is not. But there are a few hundred files in total in our project, rapidly nearing the thousands number, and getting anything much flatter results in a massive folder of unrelated items.

For now this works pretty well. The most annoying thing is importing from the shared folder with lots of ‘../../../../../’ . I know we can have alias paths with typescript, but I haven’t quite gotten that to work reliably yet. Hopefully in the future, and then we can simply import from ‘shared/components/name’ .


CLI and Summary

To summarise our new folder structure:

- app.module.ts
- app.component.ts
- modules
- module1
- components

- pages
module1.service.ts
module1.module.ts
module1.routes.ts

- module2
- components

- pages
module2.service.ts
module2.module.ts
module2.routes.ts
- shared
- components
- mocks
- models
- ...

One thing that I was a bit unsure with, is how well this will all work with the CLI, in particular for compilation. After changing a couple of settings in the config, it works without any problem. The CLI is pretty un-opinionated with most things it seems. I’ve just switched to creating components by hand as that’s pretty simple anyway.

I don’t foresee any particular problems with this approach for our site for now. It’s not an approach I would jump to straight away for other projects, as it wouldn’t necessarily be appropriate.

But for now, order has been restored.