
Message-Oriented Programming (2017) - sea6ear
https://www.joeforshaw.com/blog/message-oriented-programming
======
w_t_payne
I have developed a somewhat similar system for Python / C / C++.

I declare my data structures in YAML. From this I generate code for memory
allocation; data validation; serialization and de-serialisation routines.

I also declare the topology of the data flow graph in a separate YAML file,
decorating each edge with the data type.

Finally, I have a separate YAML file that specifies which computers/processes
each node will run on.

Nodes communicate by passing messages. For nodes in the same process, this is
simply shared memory (zero overhead). For nodes in different processes that
share a memory bus, this is a shared memory queue. For nodes on different
machines, the data goes over the network.

Nodes can be python, C or C++.

Very nice. Very declarative. The bare minimum of duplication and boilerplate.
Changing from MIL to SIL to HIL is (mostly) just a configuration change.
Logging, measurement and replay is built into the framework, as is support for
parameter tuning.

Within a single process, the model of computation is synchronous data flow.
Across processes, it is asynchronous data flow, but because we have queues
with blocking reads and nonblocking writes, we still keep the ability to
replay data through the system in a deterministic manner. (Modulo nodes
exceeding their time budget).

~~~
Game_Ender
Some quick questions:

\- How do you version your messages?

\- What problem domain are you working on?

\- What kind of process orchestration system do you use?

~~~
tmpmov
Same questions. These would be good to know.

~~~
w_t_payne
Problem domain: Embedded machine vision / Embedded sensor data processing /
Autonomous systems.

Process orchestration: Currently very simple. (Python multiprocessing). May
change to something more complex in future.

Message versioning: It depends. My configuration management / deployment model
means that most of the time backwards compatibility is not required. (The
entire system is deployed as a single unit). When messages are persisted, the
data recording system files the messages in a configuration management system
that knows the system configuration that generated those messages -- so that
when they are replayed back -- e.g. to test a new configuration -- the
appropriate reading/de-serialization logic can be selected.

~~~
tmpmov
I appreciate that you replied, even if my reply was late!

~~~
w_t_payne
No problem. Sorry for my delay in responding.

------
sktrdie
Honestly, I'm surprised by how these very opinionated and seemingly evidence-
lacking posts that propose "new cool solutions" are able to get to the front-
page of HN. They spice me with some information "look it'll be easy to test"
and immediately slap me in the face without a shred of evidence and without
even referencing similar or prior research - at least a comparison to event
sourcing seems necessary.

Not the author's fault - he probably just felt the need to share his thoughts
on this cool new idea he had.

I just wish HN users would take better care of the implications of clicking
the +1 button. You're wanting others to read compelling information you've
found.

~~~
mikekchar
On the other hand, I don't mind reading a short article on a different way of
approaching programming. I agree that I would prefer something a lot more in
depth, but if it eventually comes along I won't be surprised about it now.

For me the key here is that it's short, easy to understand, describes an
interesting idea (even if I'm sceptical), and avoids trashing other ideas.

I certainly prefer this kind of article to the majority of stories unrelated
to tech or business that are very popular on HN.

~~~
RobertRoberts
I appreciate reading it first, then coming here, reading a valid criticism,
and then a rebuttal. It's an incredibly fast way to help me separate useful
information. :)

------
simula67
Alan Kay, one of the fathers of Object Oriented programming mentioned this :

> I'm sorry that I long ago coined the term "objects" for this topic because
> it gets many people to focus on the lesser idea. The big idea is "messaging"

[http://lists.squeakfoundation.org/pipermail/squeak-
dev/1998-...](http://lists.squeakfoundation.org/pipermail/squeak-
dev/1998-October/017019.html)

~~~
leoc
On a few occasions he's actually said that the Linda model is better than
straight-up message-passing.

------
leoc
As a first-class language feature this goes back at least to 1985 and Linda
[https://en.wikipedia.org/wiki/Linda_(coordination_language)](https://en.wikipedia.org/wiki/Linda_\(coordination_language\))
and tuplespaces. (You'd know about Linda already if you'd read _Concepts,
Techniques, and Models of Computer Programming_. Don't delay, read CTM today:
[https://www.info.ucl.ac.be/~pvr/paradigms.html](https://www.info.ucl.ac.be/~pvr/paradigms.html)
) And here's a Martin Fowler talk about the practise of implementing it
informally as a pattern or framework:
[https://www.youtube.com/watch?v=STKCRSUsyP0](https://www.youtube.com/watch?v=STKCRSUsyP0)

~~~
macintux
I still have my JavaSpaces book lying around somewhere; tried to get Jini up
and running to experiment with it to no avail.

I definitely liked the idea of tuple spaces but have never seen it implemented
in a production system.

------
gilgoomesh
Highly dynamic behavior like this is great in very clear scenarios but think
carefully before using it for the majority of your program.

Dynamic behaviors are gained at the loss of type safety (function calls are
type checked, messages are usually not) and system coherence (just because you
can add and remove components on the fly, doesn't mean that it's _safe_ to do
so). To compensate for these losses, you need to increase documentation and
increase testing. Most people don't and the result is significantly less
reliable code.

> Coupling between callers and responders is removed.

But the number of dependencies is the same (reference versus message type).
And now it's unregulated dependency without any compiler-level contract
between the two. Are the parameters still what you expected? Are the
parameters in the correct format? The message type will continue to match,
even if these details change.

> Messages can be dispatched to multiple subscribers

Which is great for communication that is _intended_ to be observable but if
you're using public message passing for everything, it's going to be tempting
to snoop on the message bus and pull data intended for other recipients
instead of passing it cleanly. Now your program is a fragile mess because
everything is dependent on precise implementation details of unrelated parts
of the program.

> Dependencies can be changed on the fly.

Which is another way of saying that you need to handle dependencies changing
without notice. What happens if you're expecting a response but the generator
of that response is _removed_ between request and sending response? What if
you get a timeout? What if you get a partial response?

~~~
piaste
For the first problem, union types are the clear answer (abstract marker
classes can approximate it somewhat).

The compiler guarantees that all sent messages conform to one of a list of
possible valid message types. If the subscribers are static, the compiler can
also guarantee that every valid message type has at least one handler; if the
subscribers are dynamic, you're treading much more dangerous waters but at
least you have a comprehensive list of possible messages to verify at runtime
that each one has at least one handler.

For the second concern, I agree but I would invert it: if a publisher writes
messages intended for a particular recipient, that's a misuse of a message
bus. You'd be better off making a regular method call in that case (nobody
says the bus should solve every one of your problems). Ditto for the third -
if you absolutely need a specific response to your message, don't use a
message bus to send it. The challenge of message-bus-based architecture at
that point becomes minimizing the number of publishers which need to make
direct calls.

------
barrkel
To say that it reduces coupling is deeply misleading, IMO.

In order for your application to do things, certain flows must execute; A is
followed by B which is followed by C for some specific functionality.

Without MOP, you might have A calling B, which calls C. This is
straightforward to read and debug. If the interfaces between parts are
complicated, they may not be easy to test - but this can be fixed.

With MOP, A dumps its output on a bus, and it's picked up by B, which dumps
its output on a bus, and it's picked up by C. Now, in order to understand the
composition, you need to trace the thread through the entire system, probably
using string search - and not be certain you've got it right, because of
modalities that may not be explicit.

It looks like the dependencies have been eliminated; but it's a complete
illusion. They're are still there, they're just dynamic instead of static. C
still needs to follow B which needs to follow A. If any of these bits are
missing, the system is broken because it won't be functioning. And even worse,
this situation won't be found statically without extra tooling.

MOP and its close relative event-driven programming is effectively COMEFROM as
a first-class feature. It promotes write-only programming.

There certainly are reasons to go down that route - for example, you need to
execute a lot of unrelated side-effects based on state changes in the system -
but don't be under the illusion that it reduces coupling. The coupling is
still there because the system breaks when dependencies are absent. It hides
essential complexity, not just incidental complexity. Your incidental
complexity burden needs to be pretty high before the pros outweigh the cons.

~~~
dkarl
_tl;dr_ I think he's using a message bus in a mobile app the way you'd use
Redux in a web app.

First off I have to quibble with his terminology. Usually people say "message"
when the sender specifies who will receive the message — perhaps only by a
name that is resolved later, but at least that much. What he's talking about
sounds more like "events" that are broadcast for anyone to read or ignore as
they please.

Anyway, he isn't entirely specific about how he's using these messages, but if
he's using a message bus global to the app in order to communicate between
different parts of the app, I found it to be tractable and very convenient
when writing an Android app. Before I started using the message bus, wiring up
connections between different parts of the app was a real pain, and it often
had to be redone as functionality changed. When I started routing everything
through the message bus, it was marvelously simpler. I just connected
everything to the message bus. Obviously that's a recipe for disaster in a
large enough system, but I would guess it's a rare app that gets large enough
for this to become intractable.

 _With MOP, A dumps its output on a bus, and it 's picked up by B, which dumps
its output on a bus, and it's picked up by C. Now, in order to understand the
composition, you need to trace the thread through the entire system, probably
using string search - and not be certain you've got it right, because of
modalities that may not be explicit._

You're right that this is a difficult way to think about it, but there's a
much simpler way. You think about elements in isolation and their
responsibilities. You have a component that communicates with the back end;
one of its responsibilities is to signal when the auth credentials are missing
or invalid. You have a component that controls navigation; one of its
responsibilities is to pop over a login window and push it onto the navigation
stack when the user is prompted to log in. You have a behavioral component
whose job is to detect the lack of credentials and signal that the user should
be prompted to log in. Each of those components can be tested in isolation,
and the only thing that unites them is a list of the messages and their
meanings. If the responsibilities of each component are defined in reference
to the same set of messages/events then it's clear that if one thing doesn't
lead to another then some component isn't fulfilling its responsibilities or
the responsibilities haven't been designed correctly.

Again, just as with Redux, I can see managing these interrelationships
becoming intractable in a large enough system, but I think it works well for a
moderately complex app, and simplifying the plumbing is a huge win.

EDIT: In response to _you need to trace the thread through the entire system,
probably using string search_ , if you're working in a statically typed
language, you don't need to rely on string search; you can just search for
usages of the type of each message. For 90% of the event types you'll find two
usages: the place where an event is fired and the place where it's detected
and responded to. Most of the rest will be fired or read in multiple places
but still a very small number.

~~~
WorldMaker
> In response to you need to trace the thread through the entire system,
> probably using string search, if you're working in a statically typed
> language, you don't need to rely on string search; you can just search for
> usages of the type of each message. For 90% of the event types you'll find
> two usages: the place where an event is fired and the place where it's
> detected and responded to. Most of the rest will be fired or read in
> multiple places but still a very small number.

I think this gets to the crux of the nature of the solution. You are trading
one very strongly statically typed message-sending apparatus (function calls;
object member calls) for one more weakly statically typed message-sending
apparatus ("events"/"messages" as standalone objects).

While, yes, you can get static typing hints back, for instance, by using
statically typed Symbols/Enums for "message types" as opposed to using more
stringly typed options, you are still trading very strict static typing (I'm
calling this specific function, I'm acting on this specific object) for weaker
static typing.

There's nothing wrong with using weaker static typing, of course, and it's
nice having a spectrum of options. (It's also why I think it's silly to think
its an either/or between OOP and MOP; they are a paradigm spectrum that isn't
entirely mutually exclusive and often you want some of both worlds.) It's a
trade off, and should be a clear trade off, and knowing that is half the
battle.

------
V-2
> _Coupling between callers and responders is removed. This makes life much
> easier when we want to refactor our code, since objects don’t directly
> reference one another._

This is listed under advantages, but I'd say it's a mixed blessing at best.

Objects no longer directly referencing eachother means it is now way harder to
reason about and analyze the dependencies in your codebase.

The dependency graph no longer exists in design time like it did before,
lending itself to various static code analysis tools that could even draw you
a pretty diagram thereof. Now it's grown and shaped in run-time.

Event buses are sort of like a "goto". It's sure convenient to be just sending
events back and forth through some static event bus, but once things go south,
trying to trace the root cause will turn you into Carrie Mathison.

In Android world, event buses are considered legacy now. One of the most
popular choices, Otto, is explicitly deprecated (in favour of RxJava that the
authors recommend to use instead).

~~~
krzat
The most fun is when one message causes another message to be sent. And then
some components start depending on their order, creating implicit state
machine of madness.

~~~
V-2
Oh yes, and sticky events, and flipping the "is it handled already?" flag on
an event because some subscribers must have exclusivity...

------
RossBencina
The article mentions a Xamarin application -- that sounds interesting. I'd
like to hear more about how far this approach can be taken in a single-process
application. Are they using an existing message bus, or did they build one?

I'm more familiar with the traditional (SmallTalk-style) MVC setup where the
data model entities implement the observer pattern. In that case, observers
subscribe to change events by attaching themselves to model values (event
sources). I guess MOP differs from this by replacing centralised model value
objects with some kind of identifiers used to key message bus subscriptions
(?).

Related ideas for client/server are:

Event Sourcing
[https://martinfowler.com/eaaDev/EventSourcing.html](https://martinfowler.com/eaaDev/EventSourcing.html)

and

CQRS
[https://martinfowler.com/bliki/CQRS.html](https://martinfowler.com/bliki/CQRS.html)

~~~
coldacid
Xamarin.Forms comes with a basic message passing system built in, which is
what I assume the author is using. I've used it myself for a client mobile app
project. It makes more sense with an MVVM setup like what Xamarin.Forms and
WPF expect for user interfaces, since viewmodels can listen for model state
changes that need to be reflected in the UI without requiring loads of
explicitly coded delegates/events.

------
lojack
Isn't this just an event driven architecture?

[https://en.wikipedia.org/wiki/Event-
driven_architecture](https://en.wikipedia.org/wiki/Event-driven_architecture)

~~~
geezerjay
Taken from the blog post

> MOP is a flavour of object-oriented programming (OOP) with the core idea
> that your objects shouldn’t directly call each other, but communicate by
> passing messages via a message bus.

Sounds pretty much like a textbook definition of event-drive programming given
by someone oblivious to event driven programming.

~~~
AstralStorm
The main difference is lack of shared state. Events are not copied necessarily
nor immutable while messages are. Holding to that model also makes it easier
to interpose a mediator or a buffer. Or serialize them to disk or database.

The benefit is easier multithreading, drawback is memory use (incl. GC churn
and bandwidth) and/or complexities of copy on write in multithreaded
environment. The style is also not amenable to passing big amounts of data
around directly. (That is often worked around via explicit memory sharing or a
database - and passing handles instead of data.)

Another name for a different flavour of this is reactive programming.

~~~
bawana
I wonder if its possible/useful to construct a programming paradigm based on
the architecture of the internet and TCP packets. Instead of having just one
queue with messages, one could have multiple queues , each with its own
address (analogous to a url?)

So, there might be a message queue for each object - This seems to be the way
that ROS (the robot operating system) is architected when you have nodes that
process messages. Still it becomes difficult to disentagle a datum that needs
to be accessed and modified by different queues - and avoiding race
conditions, etc. But the solution could use message passing itself. If a queue
modified a shared datum, it could message all the other queues using that
datum and they could then update themselves. Conceivably, each queue could
have its own core and so making parallel processing effortless. You would
never need to worry about messing up shared data since the data would look
after itself. But the 'problem' is that you have traded time and complexity
for storage space - each datum now has a 'train' of addresses it has to visit
and it has to carry those addresses with itself.

------
vbsteven
The benefits listed in the article can also be realised with simple java style
interfaces and dependency injection. That approach also solves all three
disadvantages listed.

~~~
hvidgaard
They're two slightly different tools. In MOP you use a mediator to facilitate
message parsing. Anyone can subscribe to, and send messages, usually
dynamically. That makes highly complex systems more approachable and
distributable. It comes at an indirection cost.

Interfaces and DI are strongly controlled at the composistion root, but it's
not easier for highly complex and distributed systems. However the amount of
indirection is simpler to reason about and it makes sense for single process
systems.

~~~
edem
What I did to solve this: I serialized the stack frames and sent it with the
messages and later when an exception happened it could be easily traced.

~~~
hvidgaard
I'm not sure I follow. What problems are you solving by doing this, it sounds
like a rather leaky and brittle thing to do in a distributed system.

~~~
stareatgoats
Why 'leaky' and 'brittle'? Honest question. Sounds like a good solution to me
in order to trace exceptions back to the source

~~~
hvidgaard
Process A should not care how Process B implements anything, only how it
responds to messages in a platform agnostic manner. There is zero guarantee
that the stack keeps looking the same, hence brittle, and since you need to
know something about how it's implemented to use the stack, it's also leaky.
As soon as an exception happens, it should be logged. Not shipped off to a
different process. From that log you know something about the state and the
messages leading to the exception, and you can debug directly.

If A send "Process this data X" to B, and B fails with an
OutOfMemoryException, it should respond "Unable to process X", or perhaps
"Insufficient ressources to process X".

~~~
stareatgoats
OK thanks, makes sense in genuinely decoupled microservice architecture (with
messages crossing language and environment boundaries). Monolithic systems
(distributed or not) seem not to have the same problems though, in which case
it would still seems like a good idea to me, for debugging purposes.

~~~
hvidgaard
I'm not convinced. If you're trying to debug a process, you should only
operate on the messages and state. The messages you can log, and the state
should ideally come only from messages, so you truly only need the messages.

One of the strong points about this way of implementing interprocess
communication, is that it allows you to have a new and old version of the same
process working concurrently, with the new only running and comparing it's
output with the old one. Once you're convinced they work the same, you can
retire the old process. To fully leverage this, every message should be
platform and implementation agnostic. Even if it's a monolithic system on a
single server.

~~~
stareatgoats
Well, all I can say is that I personally often find the full stack interesting
when debugging exceptions, and might still try and implement passing stack
info in my monolithic system (in spite of this discussion which might have
been above my pay-grade honestly). In case it breaches some important CS
principle that I don't fully grasp yet then it will surely come back and bite
me - which has happened before ... :) . Anyway, thanks for your time.

~~~
hvidgaard
You're welcome. I juggle those concepts and requirements daily, and I value
explaining it to the developers. The "why are we doing it this way" is really
important for their job satisfaction, and it happens that they have an aspect
I didn't consider, or a better solution.

------
bunderbunder
The brand of loose coupling message buses offer seems like a mixed blessing to
me.

In a more oldschool, bus-less message passing system, different modules can't
talk to each other unless they know about each other. That's potential a
source of tight coupling if you're binding to specific classes, but it doesn't
need to be - you can also couple to a protocol, and be willing to chatter with
anyone who speaks that protocol. But still, modules can't talk to other
modules unless they're explicitly told about each other.

Pair this approach with an IoC-style architecture where the communication
graph must be assembled in a single composition root that lives very close to
the application's entry point, and you've got a great tool that gives you an
easy way to monitor and manage the complexity of the application.

With a message bus, you're basically just taking that central composition
point away and replacing it with a big ¯\\_(ツ)_/¯. I've been there, it can be
very flexible when you want to build an application quickly, since you don't
need to think about how messages will be routed. But I haven't found that the
message bus reduces coupling. Since it takes away basically all the need to
stop and think about what you're doing before subscribing to a message, you
can quickly end up with all sorts of crazy functional dependencies that nobody
fully understands because everybody was hacking them together independently.

I guess what I'm cautioning is, don't be too quick to conflate loose coupling
with message buses. The one does require that you do the other, but that
doesn't mean you can't do the other without the one.

------
d3ckard
Or you could just use Erlang/Elixir and be done with it. Presented approach to
message bus has its own share of problems, like mentioned in the article
message-type explosion.

------
markhahn
weird to assume a bus, since normal message-passing in something like
Smalltalk doesn't, and neither does MPI (whose programs are obviously more
prevalent, at least in terms of cycles consumed...) the bus-iness does make it
look more like event programming.

------
butterisgood
When you have to update several data stores via messages to several groups,
what do you do for transactions?

Seems you'll need a consensus layer and the ability to rollback if you don't
want to end up in a terrible mess later right?

~~~
rockwotj
Yeah I don't know why the author doesn't call this out as a con. Error cases
are much harder to reason about, what if another object blows up halfway
through processing a message? How do retries work? Transactions all become a
consensus problem or a two phase commit.

------
Matthias247
I agree with the author roughly about the benefits and disadvantages. And
would probably add to the disadvantages that synchronous sequences are harder
to model, since with pure asynchronous messaging all components will get state
machines.

Regarding the advantages: Interfaces and dependency injection (with or without
frameworks) also solve the first two bullet points also rather nicely. And
they keep the synchronous nature and strictly typed interfaces. The third
point can be achieved with data binding or interfaces which allow for
subscriptions (e.g. via classical "Event"s or Observables).

~~~
elcritch
In your first statement did you intend to mean that asynchronous was an
advantage or disadvantage? I’d take objects being modeled as independent state
machines as a significant benefit.

It doesn’t seem that dependency injection really provides the benefits of the
second advantage as the author describes it. Specifically he mentions that the
recipient can decide wether and/or how to respond to a message. DI doesn’t
provide that. The point isn’t _just_ that you can dynamically swap objects,
which seems precarious in a lot of ways as values/results in referenced
objects could change under your feet which without pervasive usage of
observers means lots of easy to make mistakes for devs.

That means for a DI system to really be dynamic in the same sense that you
need an event system / observer pattern in addition to DI to provide similar
benefits while being more complicated due to now three layers of abstraction
(at least) required. Whereas an “MOP" system encourages devs to think about
outside changes from the start and makes it the path of least resistance while
reusing the same abstraction to enable dynamic target and 1-to-many fanning.
The usage of DI systems results in much more ceremony and generally results in
overly complicated system architecture as compared to a good messaging system
as the author highlights. Now a really complex message topology can become
really confusing which results in similar architectural issues as an DI /
event based system if not checked.

------
Ace17
I think this is a very interesting direction.

Classic programming generally doesn't impose restrictions about which
direction the data flow is. By default, methods take value from their callers
as input, and return values to their callers as output.

An "notification"/"fire-and-forget" interface allowing no data to go back to
the caller (no return value, no "output parameter", no visible change to the
program state, from the caller's POV) has very interesting characteristics
(Like being trivially mockable).

~~~
SimonKinds
I believe this is what Command Query Responsibility Segregation (CQRS) is all
about. By having one object handling writes, and another for reads, things are
easier to reason about among other benefits. An event bus is kind of required
in this kind of architecture if the view that's reading wants to stay updated,
as it may no longer access a return value.

Source:
[https://martinfowler.com/bliki/CQRS.html](https://martinfowler.com/bliki/CQRS.html)

~~~
leoc
I think Fowler would describe the model in the article as more like "Event-
carried State Transfer":
[https://www.youtube.com/watch?v=STKCRSUsyP0](https://www.youtube.com/watch?v=STKCRSUsyP0)
.

------
xumingmingv
"It’s harder to reason out program flow. "

\-- I think this is the key cons which might lead us into nightmare.

~~~
mikekchar
For me the interesting thing is that the very lack of coupling seems like a
problem to me. If A depends on B, I'd actually like B to be coupled to A. Back
in the old days when I believed "generic components" were a cool idea, I'd
always run into that problem. It's too general and so it has not structure.
And then it is actually implicitly coupled (even if it has a network system
and a communications protocol in the middle) -- it's just that you've
abstracted away the interfaces with a bunch of adaptors. It's like having a
plug on your electronic device that can fit any socket, but only one of the
many sockets actually works.

I imagine the author has techniques for dealing with that kind of problem, but
has unfortunately not shared them. Maybe another time.

------
darkxanthos
I like the general idea and have felt the same way for a while. For the
benefits they list a message bus isn’t necessary. Even dispatching to multiple
subscribers can be straight-forward with a proxy object. Instead you can
design your OOP system to behave as though it’s passing messages and get a
majority of the benefit along with a straight-forward path to refactor towards
a message bus if/when you’re ready.

------
mcphage
This seems similar to Cocoa's NSNotificationCenter, where you can post
messages or subscribe to messages.

One of the better Cocoa books motivates it with the following example:

Say you have an app with multiple windows. You also have a settings pane,
where you can change the background color of your windows. You don't want the
"background color" component to keep track of all of the open windows in the
app, that's not at all related to its responsibility. So instead you use the
notification center to post a message "hey the user changed the window
background color to blue", and windows will subscribe to "the user changed the
window background color" messages. This way the color picker and the windows
themselves can work together, without ever caring about the others' existence.

------
mankash666
What is the recommended message passing library for mobie {iOS, Android}? In
particular, if one is mixing javascript within webView with native code,
message passing between the native and js components can be valuable.

~~~
barbs
I've used EventBus in the past on Android. It always felt a bit hacky and like
"cheating", and navigating flows through the app could be tricky, but it kinda
worked?

[https://github.com/greenrobot/EventBus](https://github.com/greenrobot/EventBus)

RxJava is the new hotness at the moment though, which kind of replaces
EventBus, so I don't think you could convince a team to develop an app using
entirely messages. Still, I'd like to try it for a personal project.

~~~
AstralStorm
RxJava works but debugging is a pain in that system. Unlike an explicit
message bugs it is hard to get a real overview in Rx system as the buses are
hidden, multiple and wrapped. You get to add an interposer manually too every
place of use to fix this... quickly gets unwieldy but can convert your
application to MVI model by force.

------
jbverschoor
Windows programming was message passing. WM_CREATE Oh the glory.

------
dwarman
My Lua system uses MOP too, though I never heard that term before. And
structured pipes. I like it and wrote it for a dataflow bsased event driven
system.

------
aninteger
Isn't this just the way Win32 programs were written? Each window could have a
routine that received messages like WM_PAINT, WM_SIZE, etc.

~~~
barrkel
Win32 is much closer to object orientation with duck typing. You normally need
a handle to the window you want to talk to, which is explicitly the kind of
coupling that MOP rejects, inverting the dependency.

Further, with Win32, you can choose between an async or synchronous message
send, depending on whether you are interested in side effects or not.

------
ndh2
I don't think this article is very good. The main achievement of message
passing is to decouple call stacks, and the reason why you do that is that you
get more control about which threads a function is called from. You turn to
message passing because you want to get away from callback spaghetti. The
article doesn't even mention concurrency.

------
dep_b
One important advantage not mentioned is the sending side. You can respond to
websocket messages, push notifications or HTTP callbacks using the same
message structure so every new source of messages can be added with only one
line of code and the receivers can be unaware of a new source.

------
unoti
The author mentions that messages cost more memory, because shared data must
be copied. This can be avoided If you base your classes in immutable data.
Then you can pass references around without worrying about modifications.

------
kwhitefoot
Isn't this rather like Windows Communication Foundation, COM, Soap etc.? Or
even Linux DBus or any other remote procedure call system.

------
jeffreyrogers
Under disadvantages:

> It’s harder to reason out program flow

This seems like a big one. You're basically creating a distributed systems
problem for yourself.

~~~
typon
How is programming in a standard OOP system any easier to reason about? Unless
all your object state is immutable, it becomes hard to reason about program
flow very quickly (non-linearly with code size, usually.)

~~~
barrkel
You can't normally talk to an object without a reference to it. This means
data flow can be used to scope control flow. MOP indirects everything through
a big global variable, so anything can talk to anything, and calls it a
feature.

Object interfaces in static languages are static, most of the time. Thus a lot
more of the conversation can be statically analyzed for correctness.

------
dmh2000
Erlang?

~~~
SuddsMcDuff
More generally, the actor model
([https://en.wikipedia.org/wiki/Actor_model](https://en.wikipedia.org/wiki/Actor_model))

~~~
cturner
It is not a complete match. A quality of the actor concurrency model is
unreliable message delivery. The system described in the post could be done
with reliable delivery expectations.

------
w_t_payne
How does this differ from an Agent based system?

~~~
meheleventyone
Generally in an Actor/Agent based systems messages are still passed to
specific 'entities' rather than using a Pub/Sub bus where 'entities' are only
coupled through the messages they send and those they subscribe for. I think
the article describes the latter.

Although I've written buses in the past that let the user address messages to
different 'entities' and groups, even ad hoc groups defined by features like
spatial locality.

~~~
w_t_payne
Thank you.

