DEV Community

Cover image for Why your folder structure sucks

Why your folder structure sucks

Mike Pearson on June 06, 2023

YouTube Inexperienced developers organize their code by technology rather than by feature. This is a mistake. Separating by technology leads to ...
Collapse
 
denartha10 profile image
denartha10

Hi Mike, I’m relatively new to web development but thought myself to program in python after university and managed to get a job. I’ve always leaned towards fp because I hate oop and so when I finally decided to switch to web dev (because I like Colors lol) I found myself gravitated towards videos like yourself and josh moroney because I love declarative code.

That was a ramble. The question I wanted ti ask is how do you decide what’s a type/feature. Like in web applications what counts as a feature. In drag and drop functionally for example is the dropzone and the draggable item a separate feature? These are just questions I have that I figured I’d ask because they confuse me 😅

Collapse
 
mfp22 profile image
Mike Pearson

Cool! Well hopefully we get more people to like declarative code.

Every type could be a separate feature, but a lot of types will never be used away from other types, so you would just be creating work for yourself by separating them out. There's no perfect answer, and you'll never get absolutely everything right, so it's not the end of the world if you realize later that you need to separate something out.

Collapse
 
denartha10 profile image
denartha10

Okay hmm so a project card would be an example of a type. A login form with validation would be a type..

Okay I sort of get it but I’m hoping it’s a familiarity thing and I’ll pick it up as I go along. Thank you!

Ps: wouldn’t a va odd extension that could color code statements to see what state it effects be cool (I got the idea from one of your imperative vs declarative examples where you showed how declarative code was unique separate blocks whereas imperative was like a bag of skittles!)

Thread Thread
 
mfp22 profile image
Mike Pearson

Yeah right now I'm doing it manually with a syntax highlighter plugin. If someone created one I would pay money for it. Changing colors has been a pain, and the whole thing is tedious.

Thread Thread
 
denartha10 profile image
denartha10

Yeh would be a cool learning tool is well as it would point out to people learning declarative code when they’ve stepped in to imperative!!

Collapse
 
elisechant profile image
Elise • Edited

Thanks for sharing, this is great. +1 have also had success grouping by feature/[domain].

This is pretty relevant to other languages as well. It might interest others that .NET MVC structure also support use of a Features directory, as well as Areas. Worth checking those links out, also this blog post about it medium.com/c2-group/simplifying-pr....

Collapse
 
mfp22 profile image
Mike Pearson

Wow, it's didn't know that. Do people use that?

Collapse
 
mfp22 profile image
Mike Pearson

Here's an example where I refactored from technology to feature organization: youtube.com/watch?v=scbedYdly6U

Collapse
 
pvivo profile image
Peter Vivo

Thx, Mike, this is so essential!

Collapse
 
rainerhahnekamp profile image
Rainer Hahnekamp

Hi, have been redirected to that article by you via Twitter :)

The architecture we are proposing has two layers. The first one is non-technical: the domain. We propose multiple folders per feature within a domain, but they very often depend on the same set of services. That's why we have a technical separation between features and services (data).

Since features also have dump components which should not allowed to access service, we move them to a separate UI (technical) folder.

We use folder because our dependency rules can only operate on folders.


You were proposing to base dependency rules on file (file extension) level.

So we would have

  • Smart Component: customers.feature.component.ts
  • Dump Component: customers.ui.component.ts
  • Service: customers.service.ts

Dump can't access services but Smart can.

Now, this would be a perfect fit if all three files would be within the same folder. Unfortunately, since services are shared, we would have a mix between folder- and file-based rules. I am afraid that this would make things even harder.

What is your take on that?


Image description

Image description

Collapse
 
mfp22 profile image
Mike Pearson

Thanks for putting thought into this.

Honestly, asking for rules based on file extensions is just trying to fight a reason for rules to exist at all, because the only useful one I've ever seen is to prevent published libraries from importing from private ones. I don't like the model, data-access, feature, ui convention for the reasons I mentioned in this article.

they very often depend on the same set of services

This is for each to decide, and the placement of those services should have nothing to do with how they are used by other features. Each service has a job to do, usually managing a certain type of data, so it should be next to other stuff that's dedicated to that data type.

Unfortunately, since services are shared, we would have a mix between folder- and file-based rules.

Why does something being shared mean that there needs to be any folder-based rules? All code should be potentially shared. Components are components precisely so they can be reused. But it's not the concern of each piece of code to manage stuff that is importing it. It is what it is, it stands alone, exposes certain APIs (inputs & outputs) and it doesn't matter what else is importing it.

Collapse
 
rainerhahnekamp profile image
Rainer Hahnekamp

I see the beauty and elegance of your approach (I also watched the video), but I can't see it working in a larger code base. Especially if my feature is a little bit bigger, I don't want to have 20 files in my folder. That's where sub-folders, after the technological aspect, become useful.

The thing that I take away is that we shouldn't always automatically subdivide a feature into technology but wait until it is needed.

All code should be potentially shared.

I want to limit the impact and define the scope of a change. I need those dependency rules to do that. Are there alternatives?

Components are components precisely so they can be reused

Some, but quite a lot, can be used only once because they are feature-specific. Especially if I use an existing UI library, the majority of my components are not going to be re-usable.

Components also allow me to split a huge feature into smaller pieces.


I know your work and what you are capable of. So, you definitely know what you are talking/writing about.

But could it be that we two are exposed to completely different types of applications? Not needing rules and creating components primarily for re-usability is something I find really hard to agree on.

Thread Thread
 
mfp22 profile image
Mike Pearson

I can't see it working in a larger code base

I can't see anything else working well in a larger codebase.

I've recently been in a project with 100+ components with these superfluous technology-based divisions and even the simplest things were driving me crazy. I had to show a data type in a component. Let's call it Person. I want this component to be reusable, because 6 other pages need to show the Person component inside of them. But each of them needs to create a FormGroup to represent the data that the component manages. How it should work is I create a library like libs/person and export both createPersonFormGroup and PersonComponent from there, maybe even the same file, because they are meant to do the exact same job. They inherently go together. But instead, I needed to create a utils library for the form group stuff, because there's a rule where not many things can import from ui libraries, and the overall FormGroup object needs to be created in a component store or service that's also fetching data so getting classified as data-access. Now I need a ui library for the component. So these 2 things are just completely arbitrarily split up.

Features can always be split up further vertically. Scopes can be divided. Yeah, subfolders are fine. But I wouldn't create a tests/ subfolder, or a state/ subfolder.

I want to limit the impact and define the scope of a change. I need those dependency rules to do that. Are there alternatives?

Organizing by feature minimizes the scope of a change (affected libraries) unless you split every single file into its own library. I mentioned this in this article. I also made a quick 4-minute video demonstrating this: youtu.be/RIbmwm3yIlI?si=JKg1cur7X8...

Some, but quite a lot, can be used only once because they are feature-specific

Well, why can't features be reused? Or do a split screen thing and put 2 on the same page? The most common that has hurt me in the past is when there is both a page where something can be done, and then a dialog for multitasking with that thing while in the middle of another version of it. Not the best design in my opinion, but that's the design we had to implement. I've seen this sort of thing 3 times I can think of immediately. But with components, it is fine—you just need to provide the inputs and the providers, and it should work. Some fragile global state management patterns struggle with this, but I think you could even provide a separate NgRx Store as a short-term solution.

But could it be that we two are exposed to completely different types of applications? Not needing rules and creating components primarily for re-usability is something I find really hard to agree on.

If there was a concrete example to analyze, that could help. But I've seen a lot of different types of applications by now.

Thread Thread
 
rainerhahnekamp profile image
Rainer Hahnekamp

Okok, thanks for the discussion. I think I understand you now much better and I don't think we are that much apart.

The main difference is that I probably tend to jump very quickly into "boilerplaty" stuff and divide my code into different folders even if I wouldn't have the need for it.