1. 6
  1.  

  2. 2

    This concept isn’t new: I’ve even used a language before that did it. I’m drawing blanks on that but I think it was agent-oriented programming. Trying to remember anything else you might find interesting that’s similar. Coordination languages like Linda come to mind where they’re separate components moving typed objects into and out of some shared space.

    Wait, the other things were called blackboard architectures. The agents were independent like actor or OOP models with communication happening in a shared blackboard that was pub-sub in style. Here’s an example. This style got a lot of effort in the 1980’s-1990’s with AI Winter killing it off. It came back a bit in agent-oriented programming for languages like Telescript where developers would build programs that could move from place to place to do computation locally on behalf of users for things like marketplaces. This paper describes a few models. Between proprietary licensing and implementation concerns, stuff like Telescript died off with the model turned into libraries or middleware for languages like Java (esp Java). Might be some good ideas in those old works for the folks trying to use actor models to solve everything.

    Main problems I recall were way higher overhead and turning sequential problems into distributed ones that were harder to verify. I only used it for some toys I built studying AI. It was impractical at least back. Except for the bandwidth savings on dial-up of moving small agents to do data analysis.

    1. 2

      Main problems I recall were way higher overhead and turning sequential problems into distributed ones that were harder to verify.

      This can partly be true, but nowadays with microservices almost everything is distributed already, with the very problems you mention. (At least in my field I mostly see these, so take “everything” with a grain of salt). On the other hand multithreaded programs can be much like distributed systems (in some aspects at least) having similar dynamic properties and problems.

      That is also true that this works best with pipeline like processing (where a unit of work is larger than the message-passing overhead), or broadcast like messaging (eg. application events published to UI anybody interested, eg. UI elements from multiple possible views in a multi-threaded GUI application, or mutiple parallel processings for same input, eg. video transcoding to different formats and resolutions, thumbnailing, etc.)

      This patter is not a silver bullet, just like neither other is, but is worth to have in one’s toolkit.

      1. 2

        “On the other hand multithreaded programs can be much like distributed systems”

        What you say is true of microservices. Far as multithreaded, it’s long been understood how to do them safely with either formal modelling (eg SPIN model checker), safer concurrency models like Eiffel’s SCOOP, or static analyzers that can spot races. They’re just simpler than most distributed designs. This might not be advantageous if the distributed model is itself as easy to analyze. It agree it probably works best with pipelines that can absorb the overhead.

        “This patter is not a silver bullet, just like neither other is, but is worth to have in one’s toolkit.”

        Oh no. I was just telling you and other readers about the prior work in case you found it useful. Also, letting you know the gist of the pattern already has a name with a lot of R&D into it already.

    2. 1
      1. 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 seems like a negative to me. How do you know what your system actually does without running it?

      1. 3

        You still have types, and you can see where a given type is submitted to the message broker, and where are subscriptions for given message types.

        The true benefit in my opinion is the enforced decoupling, you pretty much like having single method interfaces only. This way you cannot accidentally use larger surface of an API than absolutely necessary, makes you design cleaner APIs.

        Also it creates a more functional design, focusing more clearly on dataflow.

        I can imagine usecases when this can be inconvenient, but this is pretty much the same as Dependency Injection, simply not for constructor arguments, but for method calls. With DI you also don’t know what will you actually receive, where will it exactly come from. With message queues you also don’t know where exactly the message came from, and where exactly it will got, you only focus on your task, and have minimal assumptions about other parts of the system.

        1. 1

          To clarify your post, you say:

          with the core idea that your objects shouldn’t directly call each other, but communicate by passing messages via a message bus.

          Does this mean that except for the message bus API, your program never calls a method on another class?

          I have written applications using a MOP style as well, but the granularity of where message passing happened was much larger than a class.

          1. 1

            Disclaimer: the article is not mine (i did not check that checkbox. I’m new to the site I don’t know how that manifests on the UI). I only submitted it as I agree with the contents and find the pattern worth to follow.

            Does this mean that except for the message bus API, your program never calls a method on another class? I have written applications using a MOP style as well, but the granularity of where message passing happened was much larger than a class.

            I have also used the pattern in a similar style mostly, but the component size worth to separate largely differs. I use it between larger complex components, but also between single classes handling very specific tasks (Example: downloading stuff, notifying interested parties of completion. In this example the UI model and the business classes interested can all be notified at once). I don’t think any hard rules can be made on this pattern.

            Where I used it, it did work out well for me. (Backend side, java, using Guava message broker) Another added benefit: when splitting the service to use an external message queue it is also pretty straightforward to do.

            I have used it in UI programs also, to a limited extent (I’m not an UI guy). A colleage has implemented a custom sampling profiler in MOP pattern with Swing, quite some years ago.

            Actually the reactive programming pattern (Reactive Framework, RxJava, Rx.js), but the TPL Dataflow library for .net, or the Actor model are all related toolkits/patterns. All of these emphasize messages and unidirectional dataflow.

            On the backend side we extensively use message queues between microservices in our daily operations. (both broadcast and push-pull type worload dispatcher) Often these evolve from in-app message brokers.

            1. 2

              If you check the authored box, the submission would say authored by (your name) instead of “via” with your name in blue. Im not sure if it stays green on new accounts but it would say authored by.