DEV Community

Discussion on: Implementing SOLID and the onion architecture in Node.js with TypeScript and InversifyJS

Collapse
 
alexmreis profile image
Alex Reis

Congratulations, you have now turned JavaScript into a shittier version of Java.

I have had this discussion before with friends that started on dynamic languages and are now enamored with types and the SOLID principles. Dependency injection and nonsense like 300 interfaces are the things that drove half of the Java community to move to Ruby and Python 10 uears ago.

I personally love JavaScript for it's practicality and adaptness. Tieing it up with types is bad enough, and if you add DI and interfaces all around the place, it loses everything that makes JS more interesting as a language than Java or C#.

Coming up next, Fizz Buzz Enterprise Edition in Typescript, because serious node.js code is written by serious businessmen to support serious businesses.

Honestly though, I recommend you read Eloquent Ruby to learn new idioms that make sense on dynamic languages.

Collapse
 
luisnoresv profile image
Luis Nores

So you will probably hate DENO too

Collapse
 
ydennisy profile image
Dennis

Hi Alex,
I sort of agree with your view. However could you suggest some alternatives patterns to better structure node apps?

Collapse
 
remojansen profile image
Remo H. Jansen • Edited

I respect your point of view but my personal experience is radically different. When I have something like the following:

import { inject } from "inversify";
import { response, controller, httpGet } from "inversify-express-utils";
import * as express from "express";
import { AircraftRepository } from "@domain/interfaces";
import { Aircraft } from "@domain/entitites/aircraft";
import { TYPE } from "@domain/types";

@controller("/api/v1/aircraft")
export class AircraftController {

    @inject(TYPE.AircraftRepository) private readonly _aircraftRepository: AircraftRepository;

    @httpGet("/")
    public async get(@response() res: express.Response) {
        try {
            return await this._aircraftRepository.readAll();
        } catch (e) {
            res.status(500).send({ error: "Internal server error" });
        }

    }

    // ...

}
Enter fullscreen mode Exit fullscreen mode

There are a few things that I wouldn't get without types and without dependency injection:

  • I can do TDD / design by contract with confidence. If I'm implementing a type, the first thing that I can do is click on "Implement interface" on my IDE and it will create a default implementation. If my implementation is a violation of the interface I will get a compilation error.

  • While I implement this controller, another engineer in my team can be creating an implementation of AircraftRepository. We can work in parallel and we know that there will be no integration issues because the interface is a contract between us that cannot be violated by either side.

  • I can use declarative routing @controller("/api/v1/aircraft") without the need to implement a factory for my controller by hand.

  • I have a lot of metadata that can be used for things like generating UML diagrams or Open API definitions.

  • If I need to refactor something I can do it with confidence, a change in the contract will break all the related parts. I know exactly what is left for my refactoring to be completed.

  • I can also use InversifyJS to implement interception (This is not documented in the example but it is supported by inversifyJS). This allows implementing an aspect (e.g., logging) while keeping my entire code base free of logging concerns.

These advantages make a difference for me and I enjoy them. This is why I advocate this approach and I'm sure I'm not alone because there are a lot of emerging Node.js libs and frameworks in this space and some of them are becoming very popular (e.g, nestjs.com/).

Collapse
 
bmarkovic profile image
Bojan Markovic • Edited

I can agree that if you are used to that way of working and actually enjoy it (personally I'm a J2EE refugee and enjoy the fact that I've left that world of pain), sure, have a blast.

But the thing is that almost every meaningful feature in this approach already has a simpler, more streamlined and more idiomatic alternative in Javascript.

For example, DI is useful for testing. But there are testing-oriented DI implementations for JavaScript that do it much nicer than the square-peg-round-hole approaches like InversifyJS. I'm talking SinonJS, Testdouble.js and Steal.js.

There are numerous AOP solutions for JavaScript like Aspect.js or Meld that are both simpler and more idiomatic than DI inception based ones and things like Express' Debug clearly demonstrate that where AOP is needed it can be added without needless OOP overengineering and it's actually the anyithing-goes, dynamic nature of the language/runtime that massively helps in this regard (higher order functions alone make a world of difference).

In actuality an API is a contract. OO interfaces are really just one way to go about them and not necessarily a better one. But as IDE support is a given using interfaces, I can imagine circumstances where that might be preferable to documenting an API or adhering to agreements, but IRL I've never run across such a situation, yet I've dealt with incredibly slow progress and numerous pain-points in OO-heavy codebases using these patterns (despite working with stellar engineers).

I'm not even interested in going into a debate on typing. The thing is that there are no conclusive proofs that types reduce bugs at all (but abandoning OOP for functional programming apparently does), and one can sprinkle Typescript or native type annotations where one finds them needed without full-on commitment (where lack of strict types has actually helped dynamic languages and JSON to flourish in this world of distributed software and fast-changing APIs).

The "without the need to implement a factory" and "generating UML diagrams" bits in your comment do say a lot too.

Just my 2c.

Thread Thread
 
godsavethewww profile image
Derek Branizor • Edited

History lesson...Microsoft Windows is built as a bundle of exposed API's -- unlike linux with text config files -- and they caused immense pain in the 90's by breaking their bogus contracts. So I beg to differ, API's are not real contracts. In fact I'm sure the heinous behavior of Microsoft's APIs were one of the reasons Java made interfaces a big part of its paradigm.

Collapse
 
falagan profile image
Eloy Pérez • Edited

Take it easy. Here, in the article, there´s some work and time invested trying to explain another point of view about node and how to build software in this platform. So, respect for other opinions. I respect what you`re saying about JS and how people work with it taking advance of the flexibility that provides...but this requires a very advanced skills and discipline to maintain the state of the soft clean, stable and readable for others. You must be really good on JS (sure you are), but for others like me who are not that proficient on JS we appreciate solutions like this or Nest that allow us to build (yes, the same house, with the same structure, features etc many times...) projects safely, with rail guidance etc because sometimes you can not wait to be a rock and roll programming star to build and finish an app. With JS you can build custom fancy houses, great... but sometimes you have to build a block hundred identical houses with many workers and in this scenario, Remo solution, fits very well in terms of productivity. I think that it was not Remo´s intention to offend "node's pures engineers" ;)