
Refraction – JS library to make modules independent, testable and re-usable - mbasso
https://github.com/mbasso/refraction
======
h8liu
No dependency is often not the right dependency. For example, http depends on
tcp, because an http stack cannot work or be reasonably tested without a
working tcp stack. Yes, the tcp stack can be provided as an injection. Yes,
the tcp stack can be defined as an interface and faked or mocked with a
minimal implementation that barely supports the required execution, but still,
without providing the depended tcp module, the http module does not work
"independently". Sure that the http stack semantically only depends on the tcp
"interface" to "compile" (if that was a thing in javascript), but as an http
module that ideally should just work out of the box, it should always be
shipped with a default tcp "implementation" that just works fine, rather than
having the user of the module to "register" a tcp module.

Using centralized message passing/routing/registering is just being lazy on
handling dependency, it is like abandoning structure programming and starts
using "goto" for every branch, which makes the logic flow eventually
untraceable. For a system of N modules, it ideally should have around O(N)
dependency edges, but a centralized hub is essentially modeling a possibly
O(N^2). It only makes sense to introduce a hub when everyone potentially wants
to talk to everyone.

And worst of all, every module using the framework now depends on the "hub"
now. While the actual logic-related dependencies are all converted to
"dynamically" instantiated ones, this "static" dependency on the hub becomes
extremely hard to remove in the long term. It's like a cancer. It is not a
good "pattern".

------
JohnDotAwesome
I don't want to rail on the author here - lord knows I've written some
JavaScript that I regret now - However, putting things on an event bus does
not make modules independent, testable, or re-usable. You get those things
from careful planning.

You are still going to introduce conceptual dependencies between modules. I'd
personally rather have explicit control over those dependencies and their
interfaces rather than passing it through a bus.

But anyway, y'all, if class A depends on class B, formalize the interface
between the two (throw an error when expectations aren't met) and then inject
the instance!

~~~
wesleytodd
Hey John!! Couldn't agree more.

We had an event bus in an early implementation of our application and it was a
huge pain later when we didn't know every place that was using and relying on
a given event.

We switched to a much more explicit structure with defined dependencies and
more boilerplate to reduce the "magic". It is much easier to debug and
maintain over the long haul. The boilerplate stuff sucks to write, but when
you are going to maintain the application for more than a few months, it takes
MUCH less time to go back and understand than the alternatives.

~~~
pheeney
Would it be possible to write some pseudocode on how you converted some of
your code? I am trying to avoid some "magic" as well but not certain how to
handle some of these dependencies.

------
gnestor
Redux (among many others) uses an immutable log of messages (actions) to
manage state and uses pub/sub to communicate between UI components and the
state "store." This is significant because it allows the developer to debug
the application by replaying messages/actions. Refraction appears to to take a
similar approach with "history" and "replay" and "publish"/"subscribe". Both
Redux and Refraction have a concept of middleware (such as a logger) that will
log every messages that dispatched/published. One notable feature of Redux is
the single reducer that centralizes all state update logic. It appears that
with Refraction, subscriptions are scattered about the project and there is no
way to inspect the dependencies between modules at a given point in time. So
my question is: Why use Refraction in lieu of Redux or a Elm architecture-like
pattern?

------
Noseshine
I don't quite understand it, so just to be sure:

Instead of fixed calls to other modules all modules use this library to call
other modules' functions?

I think everyone has that idea at some point. I started writing my own but
ended up going back to rigid coupling because unless I _really_ decouple the
system, for example to place modules in different processes or even machines,
or letting different versions of certain modules "plug in" simply by loading
them (run-time configurable) instead of having to change module names all over
the code (needs to be done before deployment), it didn't really give me any
benefit (at that early stage). I still use it for logging: I don't have to
call a specific method, instead it's all just "messages", and when I have a
"logger" listening for certain messages it logs them on the console, in
production I'd leave the calls in but send them to a server, simply by loading
another logger module, without code changes.

My statement is only for my own specific project, I found that whether to use
such a method or not is highly specific.

So my only point is that trying to use such an approach "because it sounds
cool" turned out not to be sufficient reason. IMHO the provided "Motivation"
document is not sufficient
([https://mbasso.github.io/refraction/docs/introduction/Motiva...](https://mbasso.github.io/refraction/docs/introduction/Motivation.html)).
I think more could (should?) be said and explained.

------
ubertaco
So...it's an event bus?

~~~
JohnDotAwesome
Yep. Who needs explicit coupling when you can silently couple interfaces?

~~~
scotty79
Could you still have explicit coupling? For example by keeping definition of
given message in the same module where the sender is?

------
romaniv
One interesting way of avoiding direct dependencies is to let modules interact
with one another via reading/modifying DOM. It probably sounds strange, but
the approach works well when you want to create reusable code that also
follows the progressive enhancement principle.

------
throwanem
This appears to be an inspired and clever attempt to solve a problem which,
unfortunately, is already much better addressed by means of dependency
injection.

~~~
exception_e
My thoughts exactly with things like this. If a new pattern is introduced, it
should be superior to the old one and (hopefully) translate across languages.

------
NathanKP
Very nice. This technique of code organization will bring some of the benefits
of microservices even though the code is actually all running in one
monolithic process.

And as a bonus its not hard to imagine adding RPC capabilities to this library
if one of the modules has performance characteristics that require a different
type of scaling, so you want to move that module out to its own process
running on a different machine.

------
stanislavb
I've added Refraction under the "Package Managers" category; however, I'm not
sure this is the right categorization
[https://js.libhunt.com/categories/1-package-
managers](https://js.libhunt.com/categories/1-package-managers)

Suggestions?

~~~
spriggan3
No it's a pubsub/event listener/subscriber library, backed by Rx js, it has
nothing to do with package management. The use of "modules" here is
misleading. It has nothing to do with ecmascript modules or even js modules.

------
shove
this is a great example of a good idea being turned into rubbish by dogmatic
worship of The Idea (tm) rather than understanding the reasoning _behind_ that
idea.

