In my last post, I talked about how I see folding code regions as an anti-pattern as a design error in code. That post was inspired by a conversation I’ve been having via email, and that conversation has taken a rather interesting turn.
It started as a question about what editor to use, and a desire to find a modern editor with code folding. But since then, the conversation has turned into a discussion on the realization that the “old” way we built large-scale software in .NET, Java and other “enterprise” languages, may not be so “old” after all.
With permission, I’m posting a slightly edited version of the conversation. There’s a lot of truth and shared experience, here, for developers that are jumping away from “enterprise” languages and heading toward the “green pastures” of Node.
On Bloated Express Router Methods
Them:
I’m noticing I’m bloating my routes with a lot of logic that should be in the model … validation logic for custom fields, create logic with subdocument logic (preventing duplicates), and logic to convert the model into lighter-weight DTOs.
Any chance you can think of a few posts and/or videos that hone in on these topics?
Me:
These topics are epic length articles and book, each :P
I don’t have a lot written about them, and the only recordings that generally touch this are the more recent WatchMeCode episodes on Express.
In general, you’re talking about the business of your application with logic to do what you need. That layer will most likely live in a library of modules, outside of your web app.
Your routes, then, would gather information from the request and call out to the library modules as quickly as possible.
The goal is to reduce the number of calls in a single route handler, to keep your real business logic out of the web app and living in other modules where they can be called from the web app, background services, or wherever else they need to be used.
The only exception to this, off-hand, is validation.
On Validation
Me (continued):
In my experience, validation is something that lives in the application layer – the web app, the API, or the whatever-application-host-you-are-using layer.
I find that it’s rare for a model to have a single set of validation rules that apply to every single use of that model. The web app may expect one set of rules to run at a certain time, while the API may expect all of the rules to run together, or have a different set of rules, entirely.
When it comes to sharing validation rules (which will happen – you don’t want to write the same “name is required” validation more than once), I’ve found composition of shared rules to work best. If you have a “user” model, you may have a separate “user validations” library from which you can pick and choose which validations are used, in what scenario.
Everything Old Is New Again
Me (continued):
None of this is new ground, of course. It’s all just the same questions over and over and over again, now applied to Node instead of C# or Java or Ruby or whatever.
It would be good to reach outside of Node, then, to find the answers to these questions in other languages.
For example, on my old LosTechies blog, I wrote a post with questions about validation and collected a list of links in response. It’s all about C# and domain driven design (“DDD”), but the concepts are still valid. you might read through those to get an idea.
Ultimately, these can be long and difficult subjects to tackle. My best advice is to try and read between the lines in my videos.
Notice how my routes continue to remain small and notice where I am putting code that you would have previously put in the routers. even if the type of code is not the same as what you’re doing, the underlying principle is.
Them:
[sigh] … yeah, I basically when through that same thought process when I hit “send.”
In an surprising twist, the move from .NET to MEAN Stack… and trying to do things the “MEAN Stack way”… has actually made me make a lot of 101-level mistakes.
The world of OOP/OOD and .NET is chock full of concepts that end up becoming instinctive over time… separation of layers, ACID, whatever… that I’ve almost forgone while trying to “simplify” the apps I’m working on.
Take validation, for example. I can’t stand it when developers craft custom types in SQL Server and would almost never write a field-level validation rule in a trigger. However, that’s essentially what is happening when you allow Mongoose to validate the model.
The service layer seems to suffer from the same type of ambiguity. Had I been working in .NET, I’d have formal business libraries, service libraries, and the service host (WCF, ServiceStack, etc.) would only be exposing those operations relevant for the service layer.
However, in MEAN, it almost feels customary to blur the business and service logic into the routes and flatten the back-end stack.
It’s odd, but I’ve tried so hard to create that flattened / simplified stack that I’ve almost gone too far.
Those Who Cannot Remember The Past…
Me (continued):
We certainly have gone too far – almost all of us, as a JavaScript community.
We tried to throw out the “old” way of doing things because we were on a “new” platform, and it turns out we’re making the same mistake as Ruby on Rails when it made fun of Java for all those years:
“Patterns? Enterprise scale?! HAHAHAHAHA! That’s so DUMB!“
And all of this eventually turned into:
“Oh… wait… maybe these ‘patterns’ things were a good idea. And hey… Java and .NET? Umm… How did you grow your apps with the enterprise needs, again?”
I did the same thing when I moved from .NET to Rails, years ago. I thought, “It’s a Rails app! Everything goes in here and I don’t have to worry about it anymore!”
How wrong I was.
… Are Condemned To Repeat It
Me (continued):
My journey into Node was the same story at first.
“It’s just Javascript. How hard can this be?!”
It turns out it can be very hard when you bloat your routers and embed your business code into your web application.
I learned a lot of these lessons the hard way when I was building SignalLeaf, and as I have been building apps for my one client.
In the end, I’ve realized that the “old” way is still the “right” way for the most part.
Large systems are not to be giant monoliths. They are to be composed of smaller applications that each have a specific focus.
Applications are not to be monolithic. They are to be composed of smaller modules and libraries, with the core business logic separated from the the application shell as much as is reasonable.
Modules and libraries are not to be monolithic. They are to serve one purpose and serve it well…
…it’s turtles all the way down.
On Timeless Principles and Patterns
Me (continued):
All the principles, patterns and practices of large scale .NET systems that I used to build, still apply to Node.
And I completely understand what you mean about Mongo / Mongoose to validate the model. This is what drove the giant beast of a model that I showed in my post on code folding.
My love of Mongoose has quickly diminished, as I’ve seen the damage I can do with it. I don’t dislike it… but I’m certainly questioning a lot of what I’ve done with it and a lot of what it allows (or wants) me to do.
I am seeing a need to look at the old principles and patterns of my .NET days. I am seeing a need to take the modular and component-based architectures that I’ve built with Backbone and Marionette, and do the same on the back-end again.
The implementation may look different when you get into the weeds, but a large scale system still looks like a large scale system, whether it’s built in .NET, Rails, Node, Rust, Go, Java, Erlang, Haskell, or all of the above.
You Are Not Alone
Them:
It sure is refreshing to know that I’m not alone.