| This guy seems uneasy with Actors. His example is wrong and then he tries to solve it with a solution that's worse than the problem he thinks he has. Let's untangle that stuff. Actors are about conversations between folks organized, like you would find in a good company. So let's use the conversation metaphor. His example of "classical" actors: - PaymentActor (to herself): Someone told me to process a payment from this order. I'm done. - PaymentActor: Hey, FraudCheckActor, check if this order I'm giving you is a fraud. - FraudCheckActor (to herself): Damn, this is a fraud. - FraudCheckActor: Hey, CancelOrderActor, cancel that order I'm giving you! - CancelOrderActor (to himself): I canceled the order. Now I see an immediate problem here. This is anarchy. No one is in charge of the whole process, everyone runs to talk to someone else, and passing around that order data and there's no conversation happening here. Would someone in a real company organize things this way? Not in a good company, for sure! Let's see what his solution is: - PaymentActor (to herself): Someone told me to process a payment for this order. - PaymentActor (screaming): Everyone! Payment is complete for this order! (screams the order info to everyone) - FraudCheckActor (to herself): I heard that, I'll check it. Damn, this is a fraud. - FraudCheckActor (screaming): Everyone! This order is a fraud! (screams the order info to everyone) - CancelOrderActor (to himself): I heard that, I'll cancel that order. - CancelOrderActor (to himself): I cancelled it. Now I see an even bigger problem here. It's still a god damn anarchy, but now also everyone is screaming at everyone, including the entire order data, or talking to themselves. How is this better again? Plus notice, there's still state in the system - the subscription setup. The only thing achieved here is that it'll be nearly impossible to reason about how this thing works once it becomes more like a real-world scenario. Here's how you design it with Actors, once you have some experience: - OrderActor: Hey, PaymentActor, process this payment and tell me what happened. - PaymentActor: Hey, OrderActor, I processed it. It's good! - OrderActor: Hey, FraudCheckActor, check this order for fraud and tell me what you found. - FraudCheckActor: Hey, OrderActor, damn! This is a fraud! - OrderActor: Hey, CancelOrderActor, cancel that order and tell me when done. - CancelOrderActor: Hey, OrderActor, I canceled it! Now what do we see here? We see structure. We see clear responsibilities. We see teamwork. No screaming and no chaos. Only OrderActor has the entire order data. He's passing along only the needed bits to each actor according to their responsibilities. And best of all, PaymentActor, FraudCheckActor and CancelOrderActor are not coupled to each other. They just respond to their events, limited to their own responsibilities. Good job OrderActor & co. OrderActor is a supervising actor (that's an actual term). In my example, he's managing the workflow and he is stateful and as you see it's not that scary and out-of-order as the author is telling you. After all, he needs to know the payment went through, and then remember to ask for a fraud check and know what to do after that. (Note: technically the supervisor in this example doesn't have to have state, he can just react to the events by Payment, FraudCheck, and Cancel, but this only works in this very simple example. Real world scenarios never fit into a completely stateless world. We're talking about the order state here!) The other actors have no state (that is seen in this example). So proper design isolates concerns, and may have stateless actors, but stateful actors are essential to the model. Of course, the linked article demonstrates how you can shoot yourself in the foot even with the best model out there. I find it ironic that the author was unhappy with over-complications and coupling, yet coupled everything in a complicated way with his "subscriptions" model, instead of simply using a supervisor. |
You're just advocating for an imperative solution which may or may not be asynchronous. Nothing particularly wrong with that, but its not the only "right" way to do it. In point of fact, the underpinning of languages like Haskell is that state is conveyed by the arguments to functions (i.e. events), and the terminal "state" of a process is just the integration of those events over time or composition of functions. State is useful as a convenience. Its not essential.
Also, not for nothing, but oversimplified metaphors like shouting in an office are just obnoxious, and also irrelevant. Shouting might be a problem in an office. Software, it turns out, is capable of paying attention to whatever it wants to pay attention to and ignoring the rest.