Because Node and the most popular web frameworks (like Express) don't provide much if any guidance on how to structure your REST API, many Node developers are often left floundering. Developers coming from Spring, Ruby on Rails, or Django benefit from those frameworks being so opinionated on structure/patterns, so don't usually have to struggle. But I've found that even those developers - because they've had their hand held by the frameworks - when they come to Node realize they didn't understand the underlying concepts for how to structure without Spring for example telling them where their code should go. Add to that that many back-end Node devs are front-end devs thrown into it and haven't worked on REST APIs before, there ends up being lots of confusion.
So you see people asking questions on online forums, desperately trying to figure out what "the right way" is - or how to get their already out of control codebase in some workable, maintainable order. Sometimes they'll get constructive answers, but often it will be an answer to the wrong question. For example, a bunch of opinionated advice on which framework to use, or which AWS services to use when the question was about what goes in a controller.
Part of the problem in my opinion is that often the people asking these questions and the ones answering don't have a clear set of definitions. What exactly is meant by "structure"? By "design"? By "architecture"? There is overlap between all of the aforementioned concepts, but they are fundamentally distinct. And they're almost always used interchangeably...
And maybe the most important reason to have a clear understanding of the differences between these concepts is because when you and your team are developing your application, structure dictates code maintainability and application scalability. Thinking about organization at each level of abstraction is necessary to developing an application you won't hate working on in 3 months.
This post will go into a discussion examining the differences between structure, design, and architecture - the three terms used most interchangeably but referring to very different things.
Sometimes the word "structure" is the most vague when talking about Node services. There are two parts to this in my opinion - where do I put this code? vs. where do I put this file?
If you don't know how your code should be organized, that's the most fundamental structure there is that should be figured out first. And I think this is usually what people struggle with the most.
Code organization/structure here is basically where do I put this type of logic? How do I maintain separation of concerns and keep the code itself maintainable and testable? Where do I handle HTTP requests? Where do I handle validation? Do I validate the request or the model?
And this can fall into several different organizational structures but is usually a web layer (handling HTTP requests), a business logic layer, and a data layer (handling storage/retrieval of data). Code will fall into routes, middleware, controllers, services, models, etc. Some projects will have the code-level structured a bit differently, where you have Entities, Value Objects, DTO's, etc. But that's still fundamentally at the code level of structure/organization.
Once you have your code organized and separated by concerns in a maintainable way, organizing the individual files is much easier. You're not wondering where to put the actual lines of code, you're figuring out how to arrange the files that hold that code.
The two main types of file-level structure are usually organizing by component/feature/use case (i.e. - things like Orders, Payments, Items, etc.) or by role (controllers, models, etc.). There is a lot of debate on which of these is better, but I think as long as your code-level structure makes sense, the file-level structure will be less existential because it can be changed more easily. Generally the way the code is organized informs the file organization anyways, so often it will just fall into place naturally.
Going an abstraction layer higher, the next type of organization (I'd still kind of consider this to be a structure type) is "design". This word gets thrown out a lot and can again refer to many things so let's narrow it down - "design patterns".
This is no longer in the realm of dealing with separation of concerns (organizing different types of logic), but in the realm of solving a technical problem in a maintainable way that is easy to understand and performant. With design patterns we're getting into the implementation details, but implementation in a way that is still related to structure.
For example, if you wanted to abstract away the creation of a lot of "User" objects, you might choose the Factory pattern to do that instead of having to
new up a lot of instances in your code manually. This is still a type of organization but is a different type than the previous ones discussed. Here we're dealing with code implemenation, but its abstract enough it's not dealing with actual business rules but code maintainability. This is where design patterns differ.
Moving again a level of abstraction higher to "architecture". There are two types - application and infrastructure.
Application architecture is how the pieces of your application fit together. For example, if you're building an ecommerce backend you may have a queue that handles processing of purchases, a piece that handles auth like signup and login, and a piece that handles caching of items displayed on the web. Making these choices and figuring out how they fit together is what I consider "application architecture". We're not really at the code implementation level (although that will have to be taken into consideration) but a level higher up dealing with the application more broadly.
Application architecture is needed because otherwise the application would have no guiding "north star". It's fundamentally a guide.
Compared to design patterns, generally speaking I'd consider those to be more "local" and less far-reaching compared to "architecture". For example, you might have caching for single class or function in memory (using a design pattern) vs. caching application-wide involving Redis as something standalone (architecture). Generally a pattern is applied more to a "unit" rather than something comprised of multiple units - "architecture".
The other type of architecture is infrastructure. This is pretty easy to understand - it's like application architecture but how the infrastructure pieces fit together. I.e. - a PostgreSQL database, Redis, VPC, load balancers, etc.
I wrote this post to give some more clarity around these terms since they are often used interchangeably. Without some distinctions, it's harder to understand the concepts underlying them.
Also, it's important to think about the levels of abstraction/organization discussed above because scalability problems can be introduced at each level. And often the even bigger issue is maintainability. Application/code maintainability is essentially team productivity scalability.
When you’re thinking about how to refactor a problematic app or how to start off fresh on the right foot, it’s important to consider each “level” so you avoid problems down the road, and in the case of refactoring, so you can correct existing problems.
If you realize you're stuck at the first step and want a detailed explanation of code-level (and getting into file-level) structure, I wrote a post on that here. You'll get an explanation of the different types of REST API logic and where those pieces of logic go, how to structure the API, and thoughts on file organization. Signup below and you'll get that post as well as an example repo containing those practices emailed directly to you! You'll also receive all future blogposts.
Subscribe for the explanation and repo!
No spam ever. Unsubscribe any time.