Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Everyone has to know about the business process logic to me is the opposite of decoupling.

I'd rather limit coupling to a single actor, and let the other actors be reusable.

You can't avoid someone being in charge. It's an argument as old as the universe. Do companies need a CEO? Do we need a government?

Well, someone with an imperative role always emerges sooner or later. Even in nature, only the simplest of animals don't have a centralized nervous system. The subscription model described in the article won't survive complexity, because it's an anarchy. As nature and business shows, imperative is not a bad thing. It allows concern separation.

It's not CancelOrderActor's concern why the order is canceled. The reasons for this may be hundreds. CancelOrderActor's concern is how to cancel the order. And sure, if CancelOrderActor was A.I. we could chat about why OrderActor's practice is good, but until that changes, CancelOrderActor just takes orders. It doesn't decide what's good for the order, as it's not its job.

Someone once said to me "good software design is about separating things that change a lot, from the things that don't change a lot".

Business process changes a lot. Would you rather update all actors' bindings when this happens, or just one supervisor?

Well I might be naive, but I prefer things this way: the business process changes? I change the business process actor. The payment method changes? I change the payment actor.

And not... The business process changes? Change all the things! Because it's not just a matter of whether CancelOrderActor is bound to the FraudEvent by subscription.

It's what the CancelOrderActor does as a result of that event, and that's still defined inside that actor. And in the real world, the options are many, because no one creates an actor for just the purpose of canceling an order anyway. It's mindless boilerplate. An actor has a domain. And how that domain changes in reaction to unrelated events depends on a lot of things.



They're equally coupled. In one system, the coupling flows outward from the business process—the business process must know about the other services. In the other, the business process is domain-driven and knows nothing about processes that are driven by the events that happen within that space. The outer processes (e.g. the fraud service) know about things that happen in the domain (or rather, what messages the domain contract will send).

I've actually written a fraud detection system this way. Worked beautifully. Within the service boundary I suppose you could say it was driven imperatively, but I don't think that's what you're talking about.

> Business process changes a lot. Would you rather update all actors' bindings when this happens, or just one supervisor?

In point of fact, a lot of business processes never, ever change. The way they're handled may change, and at that point, I'd prefer for that concern to be nicely packaged into a service or actor or listener or whatever you want to want to model it with where my domain logic isn't concerned with it at all. And if my domain does change? I shouldn't have to change a fraud detection service at all. And in neither your model nor the OP's would I have to.

> And not... The business process changes? Change all the things! Because it's not just a matter of whether CancelOrderActor is bound to the FraudEvent by subscription.

I fail to see how his process has this problem and yours magically doesn't. You're making a lot of hay out of whether his "reactive" events or your "imperative" messages are better or worse, but they're the same thing: sending messages. The only difference is whether broadcast is better than a direct message sent to a unit.


> I fail to see how his process has this problem and yours magically doesn't. You're making a lot of hay out of whether his "reactive" events or your "imperative" messages are better or worse, but they're the same thing: sending messages. The only difference is whether broadcast is better than a direct message sent to a unit.

No, that's not the only difference. Notice that I have 4 actors, only one is burdened with knowing how the process works. Here's what the other 3 actors see from their PoV:

- PaymentActor gets "process payment" event.

- FraudCheckActor gets "check for fraud" event.

- CancelOrderActor gets "cancel order" event.

This is what I call decoupling. 3/4 of your system is completely isolated from each other and reusable in different business processes. And the one actor who is "burdened" with knowing about the process (OrderActor) is in fact, responsible for the process (and you can open its code in one place, read it, reason about it, and change it in one place, not all over the graph).

And his model:

- PaymentActor gets "new order" event.

- FraudCheckActor gets "payment complete event" event.

- CancelOrderActor gets "fraud detected" event.

Those 3 processes are coupled with each other, as they need to interpret each other's events.

If you want to move to another system where the fraud check doesn't happen right after the payment, you need to edit the FraudCheckActor's "reaction" to that event.

In my case FraudCheckActor is only reacting to the "check for fraud" event and so it doesn't have to have its reaction code edited.

There's nothing "magical" about it, just better concern isolation. If you still see this as the same, that's all I could do to explain things :)


I feel like this would be a much better discussion somewhere else—i have no idea whether you're still reading this thread. :)

I get what you're saying. The way I write my systems when doing evented/reactive logic is that there is a layer between the event and the message to, say, the FraudActor, responsible for mediating between domain events from different bounded contexts (to appropriate a term from DDD) and the system in question.

The main criticism I have about your methodology is that I reject the notion that the OrderActor should be responsible for sending messages to the fraud system. An order's responsibility should be maintaining information about the order and its status—not validating whether its fraudulent, which (if the fraud detector was worth its salt) would involve a lot of historical information about the ordering party, payment information, etc—none of which should bleed into the order system, and which is the kind of thing the fraud system assembles by listening to events from all over the place. In that scenario, were the fraud system's interface to change, there would likely be changes that have to cascade to every system that touches it (regardless of whether its developed with an actor model.)

I realize you probably wouldn't, in reality and given realistic requirements, architect the system entirely the way you've outlined before this. But given the constraints of a blog post, I think the OP can be forgiven the oversimplification of the example, and you (I think) should also be able to see that his implementation has merit in real-world scenarios.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: