Business decisions creeping into a Process Manager (Event Sourcing)

817 views
Skip to first unread message

Graeme Foster

unread,
Jun 9, 2015, 10:48:34 PM6/9/15
to ddd...@googlegroups.com
Hi all,

Can anyone help clarify about the responsibilities of a Process Manager to me in Event-Sourcing. I've trawled this forum, and online posts, but have found it hard to get good concrete examples of when not to use a process-manager, or what their responsibilities should not be.

My simplistic understanding is that they are used to orchestrating business processes that cross aggregates. 
My main question is to what extent should they be involved in making a decision about that process, as opposed to just pure routing semantics?

It feels weird that all my process-managers are starting to require read-models which are themselves event-sourced from the domains I'm orchestrating, to be able to make decisions about the commands to issue to the domains. It feels like domain responsibilities are creeping into process-managers.

For a more concrete example - 

1. Customer pays a scheduled payment of an order into an account using internet banking,
2. The internet-payment domain picks this up. Raises an event to say money has been added to an internet-payment account
3. This is subscribed to by a Process-Manager which is involved in orchestrating payments to the customer-accounts domain
    a. There are some rules around how to distribute payments.
          - If the payment matches the next due payment then issue a command to take it.
          - If the payment matches the total due payment then issue a command to take it.
          - If the payment matches the sum of next due payments of all that customers accounts then issue a command to pay them all.
          - If the payment matches the total balance of all that customers accounts then issue a command to pay them all.

To be able to decide what to do, I've brain-stormed ways this Process Manager could work
 1. Try the payment attempts in a preferred order (expressed by the business) one-by-one listening for success events, and trying the next on failure,
 2. Consult a private read-model so it can make this decision pro-actively, even though it still needs to handle failure scenarios due to eventual-consistency issues
 3. Fire a command which contains the preferred strategies straight to the customer-accounts domain
 4. Move the concept of payments that occur via this particular channel into the customer-accounts domain, and expose a command which understands this decision making process.
 5. Process Managers where the process is expected to very quickly complete are probably OTT and the customer-accounts domain should be able to listen to events and act accordingly

Solution 1 feels like it fits the idea of a process-manager, but also feels naive because it feels like nothing more than a proxy to a single domain.
Solution 2 feels like business logic has bled into what should have been simple orchestration, leading to additional complexity owing to the requirement to build loads of read-models (each process seems to get one).
Solution 3 feels like it buries the business process into a command which feels like an awkward place to put it
Solution 4 feels like it puts the behaviour in a more appropriate place, but could lead to a proliferation of unused commands if we ever wanted to change the process
Solution 5 feels like it loses the advantage of a process-manager and un-necessarily couples the customer-accounts domain to the epayments via these events.

I'd really appreciate any input on this,

Thanks,

Graeme

Message has been deleted

Tom Janssens

unread,
Jun 10, 2015, 2:38:55 PM6/10/15
to ddd...@googlegroups.com
>> I had a typo, let's try again

AFAIK your problem is this:

a. There are some rules around how to distribute payments.

You should delegate this responsibility to an AR

Account.ProcessPayment(id, amount) {
    for (var toPay in this.PaymentsDue) {
       if (amount == 0) break;
       var amountPaid = Math.Min(amount, toPay.amount);
       changes.paymentAssigned(id, toPay.id, amountPaid);
       amount -= amountPaid;
    }
}


Op woensdag 10 juni 2015 04:48:34 UTC+2 schreef Graeme Foster:

Graeme Foster

unread,
Jun 10, 2015, 8:46:04 PM6/10/15
to ddd...@googlegroups.com
Thanks for the response Tom,

In our case at current the Accounts Domain has AR's of single accounts, but I can see where you're going with that.
I guess my original question still stands though: 

Should a Process Manager be involved in decision making processes if it is orchestrating across domains, when to do so may necessitate read-models which will contain stale(ish) data?

or,

Should a Process Manager solely contain routing information?

Following on,

Should a Process Manager have affinity to a bounded context (i.e. can read data from the master copy of the AR's within it directly) if it orchestrates procedures across multiple bounded contexts?
(I noticed in an MS quick-start sample there were Process Managers which lived within a bounded context).

Any responses would be great,

Thanks,

Graeme

Tom Janssens

unread,
Jun 11, 2015, 3:05:42 AM6/11/15
to ddd...@googlegroups.com
My personal preference: no. For me personally the process manager has very little behavior:
generate a command, either now or in the future based on one or more events.

If I keep/check state in a PM, it's usually just a count/id and maybe a date or something similar. I prefer to push most logic to the AR's.

YMMV.


Op donderdag 11 juni 2015 02:46:04 UTC+2 schreef Graeme Foster:

Tom Janssens

unread,
Jun 11, 2015, 3:12:58 AM6/11/15
to ddd...@googlegroups.com
Re-reading your answer: you talk about the "accounts domain"; assuming you meant "BC" that seems a bit odd to me... My example was assuming you had a separate payment BC where you gather all the payments from other BCs, and based on your info I'd have a customer in there with different accounts and payments due.

Once payments are processed other BCs would pick up on these events, and mark invoices as paid etc...

(I tend to prefer my BCs as small as possible, but again I assume that's a personal preference.)


Op donderdag 11 juni 2015 02:46:04 UTC+2 schreef Graeme Foster:
Thanks for the response Tom,

Graeme Foster

unread,
Jun 11, 2015, 4:04:07 AM6/11/15
to ddd...@googlegroups.com
Hey Tom,

A customer can have multiple "accounts" that need paying off.
Let's say he made 5 separate orders, each with its own schedule of payments.
Each of those orders is a AR inside an Orders BC.
Each order has a unique payment reference for internet-banking payment (which it received from another BC which deals with internet payments).

The "internet payment account" is itself an AR inside its own BC. So when a payment comes in, we register we've received it, but we need to somehow distribute it to the orders.
Ideally the payment would match the next due payment of an order but it doesn't always. A customer might use the wrong payment reference and pay off one of their other orders. The way we can see this is by checking the next due payments of the orders to look for matches.

And this is the logic which I'm trying to get a nice home for!

Currently we raise an event to say 'Funds were added to a Internet Payment account" which has the account id, and the payment amount.
And then a Process Manager works out which orders to pay off. This is where it uses a read-model.
It ends up firing commands to pay off the accounts.

But it feels weird that that decision process is outside of the AR in a process manager, using a potentially stale read-model.

Cheers

--
You received this message because you are subscribed to a topic in the Google Groups "DDD/CQRS" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/dddcqrs/fENSgemiTNE/unsubscribe.
To unsubscribe from this group and all its topics, send an email to dddcqrs+u...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Tom Janssens

unread,
Jun 12, 2015, 6:51:50 AM6/12/15
to ddd...@googlegroups.com
Maybe something like this (pseudo-DSL): 

Order:InvoiceSent -[Order/Payment PM]-> Payment:RegisterPaymentDue // Keeps track of all payments for a PaymentAccount

Payment:PaymentRegistered -[Payment PM]-> Payment:ProcessPayment // When a payment was received

Payment:ProcessPayment -[Payment:PaymentAccount AR]-> 0..n times Payment:InvoicePaid(amount) // check all payments for the PaymentAccount and emit proper events


I don't see why payment needs to flow back to the orders BC TBH?


Op donderdag 11 juni 2015 10:04:07 UTC+2 schreef Graeme Foster:

pd

unread,
Jun 18, 2015, 5:25:25 AM6/18/15
to ddd...@googlegroups.com
Hi Tom, great explanation.

In the last row there though, ("Payment:ProcessPayment -[Payment:PaymentAccount AR]-> 0..n times Payment:InvoicePaid(amount)")
I don't quite follow the semantics.

The Payment AR has gotten its method ProcessPayment called, by the commandhandler that handled ProcessPayment cmd, but what does this represent:
-[Payment:PaymentAccount AR]->" ?

The Payment AR seems to publish the InvoicePaid event, but it could be done by an [..]Account AR too as I see it. It is the account that is responsible for the actual funds.

pd

unread,
Jun 18, 2015, 5:32:02 AM6/18/15
to ddd...@googlegroups.com
Oh, and while we're at it:

Since the PM handles events from a messaging system, the possibility of duplicate events exist (even though using duplicate detection in the bus for example).

In a PM that receives several events of the same type before changing state (waiting for n events of the type, before considering the process complete for example), would it be proper to keep track of some sort of id for every event, so that no command is issued twice if that same event instance would to appear twice?
Or is it better that the command is issued again (along with that id), and that the AR receiving it keeps track of received ids, and throws if that actual command has already been handled (i.e. the event appeared twice at the PM).

Greg Young

unread,
Jun 18, 2015, 5:42:50 AM6/18/15
to ddd...@googlegroups.com
Another option if using event store is deterministic message ids +
built in idempotency
> --
> You received this message because you are subscribed to the Google Groups
> "DDD/CQRS" group.
> To unsubscribe from this group and stop receiving emails from it, send an
--
Studying for the Turing test

pd

unread,
Jun 18, 2015, 6:22:12 AM6/18/15
to ddd...@googlegroups.com
That sounds good. I can make sequential guids. I don't know how the rest would be implemented though, the "built in idempotency" part.
Where is it built in and how?

The event picked up by the PM can be duplicate somewhere after being stored to EventStore. With a bus that has duplicate detection for a generous amount of time, that risk should be almost totally removed.
If the PM stores the event ids seen, then there's another layer of duplicate detection. This alone should be enough, but together with the service bus detection, risk for duplicate commands is virtually zero?

The problem as I see it with sequential guids, is that while it hinders earlier messages to be processed again, it can mess up order.
Let's say I have two commands issued, the first one takes some time to reach the validator, so the second one comes first. Since its guid is greater than last seen it is accepted. It shouldn't, since there was one command issued before it that should be processed first.

So, one solution to this would be to generate an ordered range of command guids at server, serve the client with a new batch every now and then, and only accept the commands in that exact order. Wouldn't need to be sequential then, as the server keeps track of the order.

But is it a good solution? Any others?

pd

unread,
Jun 18, 2015, 6:23:43 AM6/18/15
to ddd...@googlegroups.com
also, the same user at different devices needs to have some coordination of these command guid batches.

Greg Young

unread,
Jun 18, 2015, 6:24:07 AM6/18/15
to ddd...@googlegroups.com
Deterministic != Sequential

On Thu, Jun 18, 2015 at 12:23 PM, pd <pyd...@gmail.com> wrote:
> also, the same user at different devices needs to have some coordination of
> these command guid batches.
>

pd

unread,
Jun 18, 2015, 6:30:32 AM6/18/15
to ddd...@googlegroups.com
True.
Anyhow, still: "I don't know how the rest would be implemented though, the "built in idempotency" part.

Where is it built in and how?"

About where to hinder duplicate messages with PMs:
I think, that since the event is handled by a messaging system, the PM should fence off duplicates. Regardless of previous layers of duplication detection.
Also, since the command might be issued twice in a PM, the AR should also fence off any duplicate commands (be they from PM or user).

Are we talking about overlapping techniques here?

Den torsdag 18 juni 2015 kl. 12:24:07 UTC+2 skrev Greg Young:
Deterministic != Sequential

Greg Young

unread,
Jun 18, 2015, 6:32:00 AM6/18/15
to ddd...@googlegroups.com
If you are using deterministic uuids and use event store as your
transport it can handle these cases for you.

Try writing a message twice (same id/stream) to event store. What happens?

pd

unread,
Jun 18, 2015, 6:45:18 AM6/18/15
to ddd...@googlegroups.com
I get it now.

So event comes in: SomethingHappenedEvent.
The id of that event is used to hash the eventid of PMHandledSomethingHappened.
If duplicate -> ES throws -> no command is dispatched (assuming PM state is stored before dispatching command).

I'm logging commands to ES. For not impacting response times, I've put the logging after the ack of the command is returned to user.
If logging comes first, then I could use ES to avoid duplication, at cost of response time.

The PMs could log the commands that way too.

Would you recommend using ES as transport system for commands?
Right now I use a relay and an in memory dispatcher and ack the write of the AR state to ES, as opposed to ack that the system received the cmd.

Greg Young

unread,
Jun 18, 2015, 6:48:02 AM6/18/15
to ddd...@googlegroups.com
If duplicate ES will not throw. ES will 201 created but only create it
once. "Idempotency Exception TM" is a really bad idea.

This is of course just one way of handling the problem in a generic
way and it may not be right for every situation

pd

unread,
Jun 18, 2015, 6:48:38 AM6/18/15
to ddd...@googlegroups.com
oh, and also:

Would you consider this:


About where to hinder duplicate messages with PMs:
I think, that since the event is handled by a messaging system, the PM should fence off duplicates. Regardless of previous layers of duplication detection.
Also, since the command might be issued twice in a PM, the AR should also fence off any duplicate commands (be they from PM or user).


 a valid way of handling this if not going by the cmd-transport-by-ES route?

Greg Young

unread,
Jun 18, 2015, 6:49:54 AM6/18/15
to ddd...@googlegroups.com
Depends. I would also consider all the work involved in doing this to
be "busy work" and a waste of time in many scenarios.

At the end of the day you likely don't need a gold plated outhouse.

pd

unread,
Jun 18, 2015, 6:57:08 AM6/18/15
to ddd...@googlegroups.com
Thanks. Good advice.

Tom Janssens

unread,
Jun 18, 2015, 7:06:13 AM6/18/15
to ddd...@googlegroups.com
I'd make the assumption that an account means different things in the Payment and the Order BC.

In the order BC, an account would most likely represent a customer.
In the payment BC, an account could represent a bank account etc...

Op donderdag 18 juni 2015 11:25:25 UTC+2 schreef pd:
Reply all
Reply to author
Forward
0 new messages