A good project structure can have a huge impact on how successful a project is in terms of understanding the codebase, flexibility, and maintenance...
For further actions, you may consider blocking this person and/or reporting abuse
Grouping code by faculty like controllers, components, hooks etc is an anti pattern. Means all your related code is shattered and spread throughout somewhat nondescript folders.
Rather group code by feature, for example all code related to the "accounts" feature goes in an accounts folder with all related routes, components, hooks, fx, actions, reducers etc together.
This reduces cognitive load and makes locating code later on way easier.
Common code used by multiple features can go into a parts folder holding sub features within for example parts/access-control
If you really want to be efficient only ever store one function per file, then your code is searchable from an os level and you don't need to read x amount of lines to find the function you're looking for.
With a feature driven convention you can scale your code base into a mono repo and make all your features packages if needed
This ☝️
Also, definitely do not seperate your unit tests. The unit tests for a component should be in the same directory as the component itself. I really really hate having to navigate through all the code when a test fails. It is much better dev experience if you can just go to the failed test and the component will be right there in the same directory.
I am a believer that your folder structure should support the architecture you are pursuing. I think calling this "anti pattern" is too much.
Let me explain with an example I think puts in perspective that niether the article's or your approach works.
If you layer code (which you should), and later on you decide that a layer needs replacement, wouldn't it be super nice to just be able to remove a folder and that's it? If I were to go your way, I would have pieces of the layer I want to remove everywhere. Not good.
This is why I originally commented:
Your folder structure needs to facilitate maintenance. That's it. No "patterns" here.
Define "layer" in a modern stack context. Layers generally refer to data access and business logic from legacy N-tier architectures which lean towards being intrinsically monolithic and DI heavy.
In your scenario replacing a "layer" implies commonly used parts like access-control, transmission, subscriptions, elements or some kind of modifier or service that would be widely consumed.
If so, how is that different from a parts sub-feature or package in a Lerna/Nx mono repo?
If you have feature "accounts", imagine having dozens of those. I don't see myself ever trying to browse a folder tree like that. No way.
Layers are indeed how you chose to implement your functionality in terms of smaller "pieces". HTTP stack / Validation / Business Rules / Data Storage. That would be typical, I would say. If I chose a particular validation technology/package/whatever and then wanted to replace it, I don't want to chase around in every folder things I need to delete, no sir! I want to delete a folder, then create new implementations of the adapter that connects the deleted layer with the other layers using my new selection. A different approach requires the developer to scan all folders because you never know where the layer could pop up.
Define "DI heavy", because your entire application should be running on DI. Composition Root pattern -> Consumers. Ideally, nothing is left out of DI.
Dependency Injection as you'd expect it in Angular or C#, class instances implementating an interface passed at run time. Object oriented nightmares to reason about.
I don't consider React/Redux a DI heavy technique as the state relationships between parent and children props and context is declared when writing, not inferred at runtime.
The case for bulking business logic validation and transports together for all areas and features of the system is imho monolithic and the trait of object oriented architects that rely of obtuse structures like adapters, factories and all that heavy machinery, which kinda defeats the elegance and beauty of using the declarative React ecosystem imho
Ok, then define "monolithic" in the context of React or front end, because in my head, the opposite is microservices or micro frontends. Even if you create micro frontends, your layers would still be complete within the confines of that single micro frontend, and I would still stand by my point of view.
But I think I see what the "issue" (cuz not really an issue, just your own feeling and you are entitled to it) is here. You dislike many design patterns when applied to React (at least, maybe all of frontend dev?). I see now.
To each its own. This right here is exactly why I said one structure cannot possibly fit all! You have an aversion towards several design patterns. That's ok. I don't. Therefore your folder structures and not for me. Same goes the other way around: My way doesn't work for you.
That's alright. Cheers!
The opposite of monolithic is modular. A collection of features adhering to an enforced standard that are declaratively composed together and configured to operate as a whole.
I don't consider microservices or micro front ends a great way to build modular solutions as extreme discipline is needed where standard adherence is not built in.
Composition is a design pattern I believe best suited to declarative stacks like React and Feathers for example but not great for OO stacks like Angular and DotNet Core.
So, to your point, horses for courses, choosing the right architecture and convention for your stack choice is as important as tech choice for your solution - which is ultimately influenced by team experience and preferences
Ok, so we agree. Trying to bake a one solution for all is not good.
Couple of things:
Agree to this 👍
Really? Based on what?
Based.
That would be one hell of a mess lol !
Write enough commercial products at scale and it'll make sense. As products and customer services grow, feature driven conventions tend to emerge organically as more members join to form service focused teams.
Your architecture is not scalable for big projects. It will only cause big mess when you will have tons of features, and your store, services, helpers, hooks etc. will have a hundred of files. All will be highly coupled.
Also, helpers, utils are antipattern, you should avoid this. Since this is a big bag for everything.
In that case, consider Feature-Sliced Design or monorepo with separate packages per feature/layer managed for example by nx.dev
This is the MVC pattern, just with more folders, and less abstract names.
The problem with the MVC pattern at a system scale is that on large enough projects, you are going to have thousands of files in each folder. All of the files in that folder are completely unrelated to one another. You start relying on naming practices like
It becomes intractable, after a few dozen files, to figure out which files you need to change, while working on a new feature, or while hunting for a bug.
It also makes migrating / refactoring miserable, because you don't know what you can move from where. Something insidious also starts happening; developers who want to get something over the line start reaching into other features’ files, for their exports, because there is no boundary there, no matter how artificial... and instead of factoring the code out, into a shared dependency, they'll keep it where it is, and use it as-is, and woe be unto you, if they modify it, in place, for their needs.
There's nothing you can really do to prevent that, either.
In component/plug-in/module/onion/honeycomb/etc architecture, the ideal is to separate code by feature; colocating all of the services, styles, templates, tests, et cetera, which that feature needs to function. To the point where, if you really wanted to, you could publish each feature as an npm module. Don't; the overhead of managing dozens of releases is intense, unless you need to... but you could if it is warranted. Internally, if you want your feature to have an MVC folder structure, you can knock yourself out. But all problem solving or code additions, tests and the like are localized to either that specialized folder, or its explicitly listed dependencies.
/features/a/
should never, ever reach into/features/*/
for anything, and common dependencies should be factored into their own space ("/shared/", "/common/", "/my-app/", or "npm i"... whatever). The imports not reaching into the internals other features/components can even be checked statically, depending on the tool, versus relying on human code reviewers to be scrutinizing each filename and import statement.You could independently document each feature, with a readme, or a storybook, or both.
Then it's just up to the app to instantiate the pieces with runtime dependencies and app-specific parameters, and tie everything together.
It's not perfect, and publishing each component, or running a massive monorepo have their own drawbacks, but you don't need to go that deep to reap the benefits.
MVC is an abbreviation with quite a clear meaning, not every top-level structure should be called MVC, and at the same time, feature-based structure can be MVC.
For example, in my home directory I have "images", "videos", "downloads", "documents", it is a top-level folder structure, but it's not MVC. And, for example, if I have multiple feature folders each of which contains "model", "view", "controller" files it is MVC.
I was working with MVC framework in other language, and I keep wondering why does it lost its meaning in JS world? Do you think MVC is a synonym for top-level folder structure?
No, I don't think that Model View Controller is just folder structure. Nor do I think Model View View-Model (a Microsoft variant, essentially based around ActiveRecord), is just top-level folder structure.
Rather, I think that a lot of people blindly adhere to patterns without asking why, or without questioning the pros and cons of the pattern, themselves.
And thus, when I see someone talking about the potential of having hundreds of files all jammed into a folder that have nothing in common, except for how they are used in a conceptual, hand-wavy cognitive framework, it instantly trips alarm bells which are also tripped for me, while being brought in to consult on other codebases, which happen to be MVC/MVVM... and funny enough, most of the criticisms still apply.
Note you didn't say anything to address any of my issues, which apply regardless of code style (but are worse in mutative OO, due to the nature of mutation), and also note that the code itself does not need to be architected expressly in an MVC/MVVM fashion, for my concerns to apply, directly.
Also, if I cared more, I would take umbrage with "MVC has a clear definition" the number of people arguing in the early 2000s about whether it was fat models, or fat controllers, or whether Views had code or needed "code-behind" view bindings that were separate from the view (eg: ViewModel), or if services should be separate from Models or Controllers, or they should be the same code... and into ORMs, and if they counted as Models, or Repositories, abstracting ORMs...
Yeah, I’ve been through the people following Fowler and Feathers and Evans and Martin and Beck and the like. And I have seen a lot of the followers blindly applying patterns, arguing over whether a domain model was or was not anemic, if the model didn't balloon to thousands of lines of code, to encompass literally every possible type of thing you could do to validate, use, save, convert, transmit, or exponge what would otherwise be a struct. And yeah, I like a lot of those books. Refactoring, and Clean Code and Domain Driven Design and the rest are really good books. But lot of the advice in each conflicts with advice from others... and yet, remains good advice, in context. In context. That's the key. Context. Your suggestion is for everyone to follow this folder structure. That's context-free. Just like the blind adherents to MVC.
I'm totally sharing your frustration about all of these.
Less experienced people don't have the answers to "why" so they have to trust to others. Overexperienced people may be injured by bloody enterprise and arguing about rich models vs anemic. I agree that asking "why" is the key, and ability to summarize pros and cons is what makes you a really good developer. But it's complex, you know. I have own favorite approach (three tier I guess), but it's hard to say if it become a problem on a specific project.
MVC is just a model view controller, it doesn't say where to put logic, it's bearable on small projects and unsuitable for bigger. My point was that people misuse it in a different meaning.
I agree this post teaches beginners to do thoughtless things they could just do themselves, but the only letter from MVC it has is "views". "React is a JavaScript library for building user interfaces" - so it always has a view layer by definition, it doesn't make it MVC.
The MVC problems that people have with MVC aren't that the parts of code are called "M" and "V" and "C"...
They could be called "X" and "Y" and "Z" and it would still be the MVC problem.
Like an "XY" problem is when you try to go three steps too far in the wrong direction and are looking for some ridiculous solution to a huge mess, when you could just reverse to X, and go in the correct direction and all of your problems would disappear. That's the XY problem. It doesn't matter if the variables are actually called X and Y.
The organizational problems of MVC aren't even code-related, let alone MVC code specific. They are the MVC organizational problems, because the problems are embodied by the MVC pattern.
MVC has other issues, but all of my criticisms have been about MVC organizational issues (that being grouping everything horizontally, and then flattening many of those horizontal layers down, and stuffing anything that could possibly be shoehorned into one of those layers into the same folder). The problem is the same, regardless of whether the folder is named "M" or "m" or "model" or "Model" or "models" or "Models" or "Bob".
No, not every random structure can be called MVC.
Thanks for letting me know about XY problem! Now I can use this.
I think you're trying to prove that horizontal structure is bad because of MVC.
X = horizontal structure is bad
Y = MVC
You're trying to prove X because of Y! That's a XY problem.
Not all horizontal structure involves sticking all code in that layer in exactly 1 folder.
Lots of horizontal structures might have many folders for that layer.
Your structure has exactly 1 folder per layer. MVC has exactly 1 folder per layer.
The problems I am complaining about arise from the cross section of:
That is why it's the "MVC" problem, and not the "all horizontal architecture" problem; because MVC exemplifies the problem of having exactly 1 folder per horizontal layer.
It's a coincidence, not a MVC pattern or MVC specific problem. Nobody is forbidden to restructure MVC as they like, there is no such rule to have 1 folder per layer in MVC, it's just the way people do it usually because they suppose it's simpler.
It's not a coincidence, it is an antipattern that is replicated by a lot of MVC frameworks / authors / evangelicals / etc.
That's not accident, it's cargo cult.
I assume you, like anybody else, have a home directory that is structured in a similar way. Do you have "Images", "Videos", "Downloads"? When your browser downloads something, it saves it to "Downloads", and doesn't create some folder by feature or domain or something.
This is a natural way to structure files. Requires no thinking. Structuring files in a different way requires a little thinking.
So you have to agree it's not an MVC pattern but a natural way of structuring files (which doesn't work well in software, but anyway), or please explain why your home directory follows MVC pattern, are you a cargo cultist?
I don't actually use the general folder structure.
If I am making a video, raws are stored by date, by camera, not just "video".
Then separate pools/folders are made for clips, based on intent (feature), not just put back in "video".
You have won at this pointless arguing! 🎉
My sincere congratulations!
Really good article, I would change the name from services to utils. It just makes more sense to me. Services makes me think it's something related to the api layer. Also, I have seen utils folder in many large complex enterprise code bases
While ordering code files does go a long way, I don't really think there's a single recipe that can be applied everywhere.
Everyone who makes posts like these share their experience and their experience alone. In my honest opinion, there should be a standard way of arranging the files around which every developer can make minor modifications suitable to their needs and practices.
If we think from a standardization's point of view, we already get a standardized way of arranging the files in any project. We usually build on top of that given skeleton to add up things we are going to use - and discard what we don't need.
Arranging the files in a project can be a recipe of disaster, in my honest experience, if the developers just chose standard "names" for the folders instead of going their way and forcing everyone else in the line their way, it doesn't matter how you organize the folders. Jose has given an acute example of this scenario. Instead of calling the folder services, calling it utils feels much more robust and standard way of naming it. I mean the big dawgs are doing it so why shouldn't we?
For me, it's just a way of looking into the soul of a developer when i read articles like these. There is absolutely nothing wrong with such articles, only when someone starts imposing their way onto the general community.
A big drawback of imposition is that mostly these articles are read by novice developers and they pick up habits that deter them from climbing the ladder in this already challenging market.
But it was a good write. Me likey ^^
Sure
Absolutely.
Very Informative..!!
I would recommend another architecture: screaming-architecture-evolution-o...
Nice write up.
Thanks so much for article. I really appreciate
Such structure becomes maintenance nightmare once the project grows bigger than "to-do list" and more than two people working on it.