20 ms·
Message-Oriented Programming (2017)
- RossBencina 9y agoThe 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 9y agoXamarin.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.
- Matthias247 9y agoI 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 9y agoIn 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.
- deleted 9y ago[deleted]
- lojack 9y agoIsn't this just an event driven architecture? https://en.wikipedia.org/wiki/Event-driven_architecture https://en.wikipedia.org/wiki/Event-driven_architecture
- c54 9y agoSeems like it to me to: JS, C# events are great examples of MOP/event-driven
- geezerjay 9y agoTaken 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 9y agoThe 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 9y agoI 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.
- mankash666 9y agoWhat 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 9y agoI'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 9y agoRxJava 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.
- markhahn 9y agoweird 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.
- dmh2000 9y agoErlang?
- SuddsMcDuff 9y agoMore generally, the actor model (https://en.wikipedia.org/wiki/Actor_model https://en.wikipedia.org/wiki/Actor_model)
- cturner 9y agoIt 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.
- Jtsummers 9y agoKind of. The proposal in the link is more pub/sub than Erlang offers by default (you can do this in Erlang, but it's not the typical way). So with the article's presentation they're using a message bus that multiple subscribers can listen to. So you send a message X and subscribers A, B, C each receive it and do the right thing. A may be a logger, just track that an X was sent and when. Useful for metric gathering and tracking, but can be disabled (it's unneeded from a functional perspective). B may contain some actual business logic, take X and transform it into a Y and broadcasting this new message. If we need to change how Y is constructed we can come directly here. C may use X to update values in a database. Isolating it here allows you to easily replace the DB backend. Create a D process which handles the new database, evaluate that it's working correctly (by comparing to the database C is using) and then disable C later. These 3 processes are all listening to the same message broadcast. in Erlang we'd need one process which received X and rebroadcasts to an arbitrary number of subscribers (again, easily doable, but not the default behavior people go for). In Go you have channels you can pass around, but as I understand once read the data is removed so only the first subscriber gets it. You'd similarly need some "bus" intermediary that took the input from one channel and rebroadcasts on subscriber channels. Really, they're describing CORBA and similar systems.
- fahayekwasright 9y agoIs this the thing that is new again now?
- dwarman 9y agoMy 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.
- vbsteven 9y agoThe 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 9y agoThey'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 9y agoWhat 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 9y agoI'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 9y agoWhy 'leaky' and 'brittle'? Honest question. Sounds like a good solution to me in order to trace exceptions back to the source
- hvidgaard 9y agoProcess 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".
- Ace17 9y agoI 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 9y agoI 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 9y agoI 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 9y ago"It’s harder to reason out program flow. " -- I think this is the key cons which might lead us into nightmare.
- dep_b 9y agoIf you think it’s necessary you can still explicitly list receivers on the message handler instead of using a list of objects that implement a certain interface. It makes code a bit more kludgey though.
- mikekchar 9y agoFor 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.
- simula67 9y agoAlan 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-October/017019.html http://lists.squeakfoundation.org/pipermail/squeak-dev/1998-...
- leoc 9y agoOn a few occasions he's actually said that the Linda model is better than straight-up message-passing.
- mbrodersen 9y agoWhat he actually talks about is Erlang style programming. Erlang is the most OO language out there according to his definition. Which is funny because Erlang is also a functional programming language.
- ndh2 9y agoI 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.
- darkxanthos 9y agoI 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.
- sktrdie 9y agoHonestly, 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 9y agoOn 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 9y agoI 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. :)
- twic 9y agoI'm not too worried about reading bad programming advice, because i'm confident i will ignore it (this is not arrogance - i ignore all programming advice, good or bad). But there are a lot of impressionable minds who take HN quite seriously, and bad advice risks escaping through them into the real world.
- jbverschoor 9y agoWindows programming was message passing. WM_CREATE Oh the glory.
- d3ckard 9y agoOr 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.
- leoc 9y agoAs 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 9y agoI 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.
- w_t_payne 9y agoHow does this differ from an Agent based system?
- meheleventyone 9y agoGenerally 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 9y agoThank you.
- deleted 9y ago[deleted]
- w_t_payne 9y agoI 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).
- corey_moncure 9y agoThink this could be ported to .NET Core? I'd love to take a look at it. I'm building something on similar lines with Redis and Protobuf, but I'm in over my head a little bit
- w_t_payne 9y agoIt is mostly written in Python: Most of the work is done at 'compile-time' -- reading configuration files and generating code for the runtime. It would not be too difficult to port the runtime, I suspect.
- Game_Ender 9y agoSome quick questions: - How do you version your messages? - What problem domain are you working on? - What kind of process orchestration system do you use?
- dep_b 9y agoOne 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.
- aninteger 9y agoIsn'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 9y agoWin32 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.
- gilgoomesh 9y agoHighly 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 9y agoFor 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.
- butterisgood 9y agoWhen 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 9y agoYeah 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.
- V-2 9y ago> 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 9y agoThe 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 9y agoOh yes, and sticky events, and flipping the "is it handled already?" flag on an event because some subscribers must have exclusivity...
- jeffreyrogers 9y agoUnder 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 9y agoHow 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 9y agoYou 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.
- bunderbunder 9y agoThe 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.
- unoti 9y agoThe 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.
- barrkel 9y agoTo 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.
- ndh2 9y ago"Now, in order to understand the composition, you need to trace the thread through the entire system" I don't think that's necessarily true. It depends on the implementation. In an actor based system, the syntax might not change that much. You just sprinkle in some async() here and some yield() there. So the code still looks like it is using function calls, and uses the stack/instruction pointer for state management, but is running asynchronously. I do very much agree with you point about coupling.
- dkarl 9y agotl;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.
- mcphage 9y agoThis 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.
- kwhitefoot 9y agoIsn't this rather like Windows Communication Foundation, COM, Soap etc.? Or even Linux DBus or any other remote procedure call system.