My implementation of Clean Architecture in JavaScript. Would love your comments.

1,607 views
Skip to first unread message

Igal

unread,
Oct 10, 2015, 10:34:42 AM10/10/15
to Clean Code Discussion
Hey Uncle Bob & everybody,

I implemented my point of view on uncle bob's clean architecture in JS, mainly for NodeJS usage, but it can help on the client side as well with Browserify/Web Pack.

You can find it at:
https://github.com/tounano/mue

The documentation refers to it as utilities for MicroServices, but eventually it's a Clean Architecture implementation. I took Uncle Bob's advice from these posts:
http://blog.cleancoder.com/uncle-bob/2014/09/19/MicroServicesAndJars.html
http://blog.cleancoder.com/uncle-bob/2014/10/01/CleanMicroserviceArchitecture.html

Eventually, my implementation defines a microservice as an interactor. Each interactor is unaware of where it's being deployed or how the communication is
being done.

Interactors can be deployed within one Node or in several nodes. It doesn't matter. Because as Uncle Bob says: "deployment is a detail" :)

The main idea is that each interactor is a stream (similar to streams in unix). It receives messages (boundaries), changes the state if needed and sends
messages downstream.

Dependency Injection could be done using Interactor/stream factories, or by a microservice. Each microservice can request configuration on it's initiation,
and it should get a response that would provide it what's needed.

I would love to hear your thoughts/comments and of course criticism.

Thanks.




Dave Schinkel

unread,
Oct 11, 2015, 2:02:25 AM10/11/15
to Clean Code Discussion
Nice, curious how you're doing DI with Node.js, I'm working with Node.js as well and this is a huge pain point with JavaScript for me and how these languages don't have the concept of interfaces..

I realize ES6 has classes and you can inherit stuff with nicer sugar through prototypes but it's still kinda challenging or at least more work to Inject via a common interface in JavaScript to lets say injecting a gateway and in-memory gateway implementation into an interaction via some kind of interface.  I have yet to solve HOW I'd be able to share some kind of interface to do so.

Igal

unread,
Oct 11, 2015, 3:58:27 AM10/11/15
to Clean Code Discussion
Hey Dave,

Thanks for taking a look.

So basically, when I moved from OOP languages to NodeJS, I was creating classes like crazy. With DI and all the SOLID aspects.

I was DI fanatic, that I was injecting even the `request` module.

It was 3 years ago. As time went by, I moved towards functional programming, which I like more than OOP.

What I'm doing now, is I try to create small modules (I limit myself to 200 loc). But the average is around 100.

I do DI, only for objects that depend on IO. So for example I'd inject a MongoDB dependency to a Data Access Object/Function. And I'd inject this DAO to an Interactor.

As for business/domain logic, I don't do injections. I use NPM as my DI.

Most of my function signatures look like:
  • function (state, cmd) which is the signature of reduce.
  • function (dependency1, dependency2, state) with auto currying, which is the signature of map.
You can see that in this module: https://www.npmjs.com/package/selectors That I've developed.

When I just started with Node, I was using https://www.npmjs.com/package/wire as my DI framework.

But I don't use it anymore, since the style of my coding have changed.

As I see it, an Interface is just a contract. That's all. So in the abstract level you do have Interface in JS. And ofcourse function signatures.

Another common signature that I'm using is:

function InteractorFactory(imports) {
  // Validate contract of Imports
  return function interactor(boundary, cb) { /*...*/ }
}

As of Imports, I inject sort of a master state object that would have most of my dependencies of the app. The Interactor can use whatever object it needs.

IMHO, it's clean to inject the application state into an interactor. It's not colliding with any of the SOLID principles.

Because the interactor expects to get and access to imports.getDogsList()

If imports looks like:
{
  getDogsList: ...,
  getCatsList: ....,
}

It still fufills the contract.

Another pattern I'm using, is the builder pattern, which builds the imports object and the application state on request.

It builds it incrementally in an immutable way....

Let's say I'm just initing the node. I'll build a state for the app.

Then, someone hits my web api. I need to pass it to my boundary... So I'll have a function that receives the appState and the request object from express, and builds a new state out of it.

You don't really need DI Container/framework when you work that way, because the application state builds itself incrementally.

And the best thing, is that all the functions are clean and testable.

Those are my 2 cents,
hope it helped.

Dave Schinkel

unread,
Oct 11, 2015, 2:33:00 PM10/11/15
to Clean Code Discussion
thanks for the input.

Yea I don't like DI frameworks either.  I don't use them.  When I say "inject" I property inject stuff.  But in JS how are you validating the interfaces?  And when you say "inject" do you essentially mean the same thing, send in a JS object into a method sig, etc. And really the "interfaces" is something you have to check, since objects aren't typed in JS, you are checking each and every property to make sure it matches the kind of JS object you expect incoming to a method?

Dave Schinkel

unread,
Oct 11, 2015, 2:35:23 PM10/11/15
to Clean Code Discussion
and I've never used something like an auto mapper when I was doing C#, I just absolutely never saw the need for one.  If you keep your classes very small, why would anyone need a friggin mapper.   At the most you should be injecting ONE thing IMO into a class...through an interface by either a property or constructor...if we were talking C# for example.

Igal

unread,
Oct 12, 2015, 1:44:01 AM10/12/15
to Clean Code Discussion
Hey again.

I use a module called "ajs" to validate JSON schemas. It's really fast and efficient. Also, I wrapped it with my own function that validates the interface only in development environment to keep it efficient.

Regarding mapper, well, that's how my Nodejs coding style evolved. Before I was coding in Node, I wasn't doing it.

Dave Schinkel

unread,
Oct 12, 2015, 2:10:16 AM10/12/15
to Clean Code Discussion
I'm going to use json-schema for entity validation....  But just for general injection, it could be anything.  You could be injecting a use case into a controller.  Or a gateway into a use case, and the list goes on and on...

Kaleb Pederson

unread,
Oct 13, 2015, 3:59:47 PM10/13/15
to clean-code...@googlegroups.com
When I started doing JavaScript a while back I had to figure out how to handle dependencies between objects. I started looking through a bunch of different NPM packages to see what was freqently happening. I found that most packages provided either an object or a function available as a module (AMD or CommonJS). I wanted to be able to control all the internals for testing yet still be able to ensure the consumer wouldn't have to worry about doing so. I ended up separating my modules into two different parts, one that I called a Types module and a second "Friendly" module that understood the Type module's dependencies and would inject them.

It looked roughly like the following (using AMD modules):

define('MyModule.Types', [/*always empty*/], function () {
    function MyType(service1, service2 /*, other dependencies*/ ) { ... }
    return {
        MyType: MyType
    };
});

And then the injected version:

define(
    'MyModule', ['MyModule.Types','Service1', 'Service2'],
    function (myModuleTypes, service1, service2) {
        return new myModuleTypes.MyType(service1, service2);
    });

For testing I could then request the Types module and inject all its dependencies. Most consumers, however, would simply request the more friendly module and then never had to worry about any of the dependencies.

I'm not experienced enough with JavaScript to know whether this is a great approach, but it has worked well for our needs. I'm curious to know what other approaches are out there and/or your thoughts on this approach, such as any drawbacks it may have.

Thanks.

--Kaleb

--
The only way to go fast is to go well.
---
You received this message because you are subscribed to the Google Groups "Clean Code Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clean-code-discu...@googlegroups.com.
To post to this group, send email to clean-code...@googlegroups.com.
Visit this group at http://groups.google.com/group/clean-code-discussion.

Reply all
Reply to author
Forward
0 new messages