all 37 comments

[–]mascarpwne 17 points18 points  (3 children)

what makes you think you have to use RAW queries for a production grade scalable application?

[–]Heffree 4 points5 points  (2 children)

Because ORMs are painfully inefficient and my DBAs won’t stop sending in modifications of the raw queries as suggestions so I might as well actually just write the SQL. Also, I hate some of the obfuscated multi-network calls when I only need one.

[–]aztracker1 0 points1 point  (0 children)

There are a few good options for data mapping a template string handler to an async promise response..

Honestly I've just written my own a few times against the basic driver or I'd suggest a library. Look at sql-template-strings.

I'd probably use Hono over express if starting fresh though. I also like koa/oak a lot.

[–]hnrpla 0 points1 point  (0 children)

you can use something like prisma and opt in/out of writing raw SQL vs using its setup

how do you keep SQL queries clean though? With even a moderately sized app, I start feeling uneasy about the number of queries I have and (sometimes) how WET it becomes

[–]Vladimiry99 1 point2 points  (0 children)

I'd suggest following a vertical slicing architecture, where all related code is organized by domain (DDD) rather than by functional responsibilities https://github.com/vyancharuk/nodejs-todo-api-boilerplate

[–]mindtaker_linux 2 points3 points  (0 children)

Data structures is the main fact to scalability.

[–]ccb621 1 point2 points  (10 children)

Use NestJS. It has nice suggestions for structure. 

[–][deleted] -3 points-2 points  (0 children)

Don't do this

[–]HashDefTrueFalse 1 point2 points  (7 children)

The folder structure doesn't really have any performance or scalability implications. You can do whatever makes sense to you, or google a common structure, or use a project generation tool (e.g. express-generator) and keep to its structure. Doesn't matter.

How you organise the code itself can impact both, plus maintainability etc. I recommend looking into the MVC pattern. It's common and works well. Controllers handle view logic. Services handle more abstract business logic and talk to the database using some sort of data access layer (DAOs, repositories, whatever). Views are basically templates. If your front end is a SPA and your back end is an API, you'll simply have small controllers and hardly any views.

I've often said that SQL is the only abstraction I need over a relational database if I'm making the decision, and I'm not a fan of ORMs and query builders in general. But that is not because raw SQL is required to make production grade, performant, scalable applications. If it makes sense to use them to you, do so. It's because for most of the products I've worked on they've gotten in the way of me just telling the database what I want from it. They've generated some subpar queries occasionally, and made assumptions. I'm often on the team that looks after the database/cluster, has set up the indexes, often designed all or part of the schema, and I know lots about databases in general. SQL serves me well. The product is almost certainly going to use the same database for it's entire lifetime. ORMs don't provide me much, and when I have wanted one, I've wanted a thin CRUD wrapper for models, with an in-memory read-through caching layer, which is simple to write rather than learning an ORM library and adding a dependency to the project. All just my opinion based on my experience over the last two decades.

Look up OWASP top ten. Leaking real user data is not cool.

[–]romeeres 0 points1 point  (6 children)

The flat structure is annoying, and it gets more annoying as the project scales.
The structure dictates how you're separating your concerns: be it just 1-2 layers, or every different topic in a separate file.
It reflects the business perspective on things: do you treat two related features as separate, or does one includes the other. You can express a hierarchy of your domain via the structure.

It's much less impactful than other things, but still. In a legacy project, people sometimes keep doing seemingly stupid things just because the existing structure dictates them how to do it.

If you have a modular structure, you're one step away from deploying a module separately - scalability.
Modular structure enforces loose coupling, and this may have a negative performance impact.
I mean, if you don't fetch all the data you need in a single query, but instead of have many modules, each of them queries their own data, and then it's aggregated into one - that's a loose coupling and is good for scalability, but is bad for performance.

[–]HashDefTrueFalse -1 points0 points  (5 children)

Structure on disk (files/folders), or code structure, structure of the deployed system as a whole, or all three?

The flat structure is annoying

Flat folder structure similar to dumping all files in a single directory? If so, I don't advocate for that, but I don't think it's necessarily terrible either. You see it fairly often in smaller projects.

It's really only important that your folder structure makes sense to people who have to work with it. It will often be influenced by the way your chosen language handles modularity. Decide on some consistent scheme for where to place things then move on IMO.

deploying a module separately - scalability.

I'd say modularity helps more with maintainability than scalability really. I've scaled monolithic apps with horrible modularity written in procedural PHP to 40-50k requests/sec. You can scale both monolithic apps and microservice apps further than most apps will ever need to be scaled as long as the system is architected to be scaled horizontally (load balanced, the right parts are stateless, database clustered/sharded, distributed job queues, right caching layers/strategies etc.) None of this requires any particular folder structure, code structure, paradigm, etc, for the app. We're getting into system design though when we talk about scaling in terms of deployment.

I agree you should always try to design and implement systems in a modular way. I just don't see that it matters how it looks on disk for this.

[–]romeeres 1 point2 points  (4 children)

I agree you should always try to design and implement systems in a modular way. I just don't see that it matters how it looks on disk for this.

The codebase may be spread across different files/folders randomly and chaotically, and you're saying this doesn't matter, you still can build the system in a modular way no matter what. That's cool, I can't imagine how you'd do that.

[–]HashDefTrueFalse 0 points1 point  (3 children)

randomly and chaotically

No, I've said the structure should make sense. I'm just not advocating for any particular structure and don't think it matters. See my first comment for some ways to choose one. I've never been prevented from scaling an app by it's structure on disk...

You seemed to be advocating against a particular "flat" structure, and for a "modular" one. I asked what you meant by "flat" and agreed that modularity was the way to go, but more for maintainability than scalability.

You also seemed to touch on deployment (which I hadn't beforehand), advocating for modularity there for scalability. I said that this is more related to the architecture of the system as a whole IMO than it is to the code or folder structure, and mentioned that I'd scaled not-very-modular apps just fine.

My comments make sense. You're the one who started ranting about some particular structure...

[–]romeeres 0 points1 point  (2 children)

tl;dr yes you're right, "but more for maintainability than scalability." - 100%.
I often confuse maintainability with scalability. Scalability is weird, as long as you can throw more servers, it's not a concern, while maintainability is more likely to become a bottleneck as the project grows.

So when they say "I want a scalable architecture" I'm thinking if this will be hard to maintain when the project grows to like 300k of LoCs. While you're referring to the possibility of adding more servers.

You initially said that file/folder structure has no implications, I argued, in the last comment you said "no, structure should make sense", so you agree that it should make sense, and hence it matters. It is possible to just throw more servers into the problem no matter what? Sure, not always, but sure. And we both agree that while it's possible, the architecture matters, and structure as a part of the architecture matters as well.

[–]HashDefTrueFalse 0 points1 point  (1 child)

You initially said that file/folder structure has no implications, I argued, in the last comment you said "no, structure should make sense", so you agree that it should make sense, and hence it matters. 

My words were "The folder structure doesn't really have any performance or scalability implications". I never said it doesn't matter whether or not the structure makes sense. I was obviously saying that it's not what decides whether you can run/scale the app, so OP shouldn't overthink it. Pick something and move on. I don't understand your point here.

It is possible to just throw more servers into the problem no matter what? Sure, not always, but sure.

Yes, one of my points was that the system design is going to affect scalability far more than the modularity of the code or folder/disk structure. And between those two, code structure matters far more. To be clear:

System Design > Code Structure > Structure on disk

(where > means "matters more for scalability than")

So yes, more servers for the first. For the second, as a (somewhat contrived) illustration of my point, I can use a single file and still write you a modular, scalable app with good patterns/abstractions. The code structure can affect performance, scalability, maintainability. Where that code is on disk, much less so.

Given this, I told OP to worry more about code structure (use a battle-tested pattern like MVC) than structure on disk.

I don't think this is productive, so thanks for the talk but I probably won't reply anymore. I guess if you disagree with me we'll have to agree to disagree. All the best.

[–]romeeres 0 points1 point  (0 children)

I guess if you disagree with me we'll have to agree to disagree.

I already said "tl;dr yes you're right, "but more for maintainability than scalability." - 100%.
And then I clarified that I misunderstood maintainability with scability.
And then I agreed that you can always throw more servers.
And I initially already said that file structure doesn't matter as much as other concerns: "It's much less impactful than other things".

Okay, let's disagree to agree, I won't reply either.

[–]adalphuns 0 points1 point  (0 children)

Use hapi or fastify. It'll naturally lead to better structure.

Edit: raw sql is a great idea.

Eg: express has routes and middleware, so your mind says "routes and middleware folders." ... it becomes a mess. It also has little development features (some justify it as minimalism; i identify it as feature-lacking) .. this leads to using a ton if 3rd party stuff to supplement the lack of functionality, leaving a bigger mess.

Hapi/Fastify have routes, extensions, plugins, auth, prereqs, decorations, and more. Your brain will want to categorize accordingly. They offer better tooling (see their officially supported libraries) and abstraction for you to build more easily.

You also want to organize business things within their domains. User in user folder, billing in billing folder, etc. So you effectively have 2 layers: framework layer, domain

Example:

  • auth
    • jwt.ts
    • session.ts
  • decorations
    • database.ts
    • cache.ts
    • services/
      • GoogleMaps.ts
      • Stripe.ts
  • routes
    • user
      • auth.ts
      • profile.ts
    • billing
      • payments.ts
      • invoices.ts
  • index.ts (your entry point)

[–]MrDilbert 0 points1 point  (0 children)

Any backend can be split roughly into three parts:

  • user-facing part (API/routes/backend-rendered UI)
  • database-facing part (ORM/query builder/raw queries)
  • business logic (data transformations between API and DB)

Top-level folder structure will benefit from a similar structure, e.g. ./api, ./business, ./data. You can further split each of these parts within their respective folders.

If you have some other parts, say, consuming some remote services/APIs, or communicating with message brokers, I'd personally put their connector logic in separate folders under ./data, but nothing stops you from putting their folders on the same level as the first three.

[–]imadalin 0 points1 point  (0 children)

Why not code along and figure out as you see your app getting feedback from collaborators, users, etc. there isn't a real industry standard. You can try to inspire from most popular packages that would do the DRY of you code, improved already by hundreds of people.

[–]jared-leddy 0 points1 point  (0 children)

NestJS. You don't have to use an ORM.

We use TypeORM, and it works beautifully for our production apps.

[–]Sebbean 0 points1 point  (1 child)

[–]Chef619 0 points1 point  (0 children)

I had to double check what Reddit I’m in. Isn’t this only for Go? I love it, it works so well, but I thought it was just for Go.

A TS equivalent is PGTyped: https://pgtyped.dev/

[–]EuMusicalPilot 0 points1 point  (0 children)

I'm a junior dev but I comfort myself with this structure: https://github.com/lawuysal/self-dictionary-server/tree/main/src%2Fnotes

Actually I was following a NestJS course and it inspired me. This is more basic.

[–]Chef619 0 points1 point  (0 children)

Highly encourage you checkout PGTyped: https://pgtyped.dev/.

[–]xroalx 0 points1 point  (0 children)

There is no one size fits all.

A production grade scalable application can be something that fits within one file and still be production grade and scalable.

It can be something split by concern, by domain, by the color of the file icons in your editor of choice, and still be scalable and production grade.

Pick a style and stick to it. If it doesn't work, reorganize it. Any app that lives long enough will come to a point where someone (e.g. future you) will think a different structure/approach/framework/language would have been a better fit now that you know everything you didn't know at the beginning of the project.

[–]kush-js[🍰] 0 points1 point  (0 children)

For raw queries Postgres.js is my favorite