I am debugging an incompatibility problem of two Shopware 6 modules.
One is generating coupon codes in the CheckoutOrderPlacedEvent
The other one if firing this event itself
This causes the codes to be generated twice, for the profit of the customer :-)
In which module is this best fixed?
I tend to say, that we should not fire the CheckoutOrderPlaceEvent in modules, because this is likely to cause side effects.
On the other hand it would be possible to fix it in the coupon module, to detect if the coupons were already generated.
I tend to say, that this event shall only be fired once, as also OrderPlaced flows would be executed twice.
I agree that CheckoutOrderPlaceEvent should only be dispatched once, as the name suggests, when the order is placed. Dispatching the event will also trigger flows from the Flow Builder, so by default another order confirmation mail would be sent, which probably isn't in the interest of most merchants. In addition there might be numerous other flows triggered by the event which might do all kind of things. This might also lead to unexpected behavior. Really the keyword here is expectation and for unsuspecting users dispatching the event multiple times would not meet their expectations. While there is a context state to skip the execution of flows, other plugins might listen to the event as well with the expectation, that it is only fired once in the lifecycle of an order.
Related
I have a BPMN diagram (see below) with some errors that I can't seem to figure out. The diagram depicts the Produce Magazine Article Process, where the writer and Researcher are freelancers who work together to write articles for various publications.
Bigger version: BPMN diagram
There is a bunch of errors here, three of them are logical (two are related), one is BPMN syntax.
Let's start with the syntax.
The message is always a communication between two separate pools s it has to cross pool boundaries. In your case, you have depicted Freelancers as a single pool, so Send information, being between lanes but not pools is a syntax error. Before suggesting a solution though, I will focus on logical errors.
Time event is not used to show the fact that some time goes by between the activities. That is actually something natural in the process It is used to indicate that the flow of time is a trigger of the next action(s). For instance, 7 days after choosing a topic the Publication might contact the Researcher to check on the progress. That would be indicated by timed event. In your case, it seems that the flow continuation is triggered by passing messages so you should indicate it as an Incoming message event. You actually do that in 2 places, one that is obvious (Get article as a "result" of time event) and the second that correlates to a second problem.
The second thing that most probably is a logical question is that since we are talking here about freelancers, most probably Researcher and Writer are two separate entities, not one organisation as your current diagram suggests. If that is the case, you should have them represented as two separate pools. Then your message would be judged, but still rather than "Wait for information" time event you should have "Receive information" incoming message event (that is BTW the starting event for the Writer pool - similarly receiving Article request by Researcher should be handled by Incoming message event).
If you prefer to depict the Freelancer as one "organisation", then you should completely abandon the time event (as again you have used it as an indication of time passing and as I have explained earlier that is not how it should be used). You have a simple flow, where once Researcher finishes their job, it is passed to Writer who carries it over from there. In such case, you should have a simple action flow (solid line) between the actions themselves.
It is also a good practice to be consistent in using End events (and at least recommended - some BPM engines verify that) to always have an End even for every branch of a process. You are missing one or two, depending on how are you going to approach the Freelancers part. Similarly, you should have a Start event for Publication.
Below are the two options shown in the form of diagrams. Note that I also did some minor changes to handle the insufficient information case by Publication. Otherwise, they will be stuck forever waiting for the article to come.
Option with Freelancers as separate pools:
Option with Freelancers considered as a single organisation
In some exceptional situations I need somehow to tell consumer on receiving point that some messages shouldn’t be processed. Otherwise two systems will become out-of-sync (we deal with some outdates external systems, and if, for example, connection is dropped we have to discard all queued operations in scope of that connection).
Take a risk and resolve problem messages manually? Compensation actions (that could be tough to support in my case)? Anything else?
There are a few ways:
You can set a time-to-live when sending a message: await endpoint.Send(myMessage, c => c.TimeToLive = TimeSpan.FromHours(1));, but this will apply to all messages that are sent (or published) like this. I would consider this, after looking at your requirements. This is technical, but it is a proper messaging pattern.
Make TTL and generation timestamp properties of your message itself and let the consumer decide if the message is still worth processing. This is more business and, probably, the most correct way.
Combine tech and business - keep the timestamp and TTL in message headers so they don't pollute your message contracts, and filter them out using a custom middleware. In this case, you need to be careful to log such drops so you won't be left wonder why messages disappear now and then.
Almost any unreliable integration can be monitored using sagas, with timeouts. For example, we use a saga to integrate with Twilio. Since we have no ability to open a webhook for them, we poll after some interval to check the message status. You can start a saga when you get a message and schedule a message to check if the processing is still waiting. As discussed in comments, you can either use the "human intervention required" way to fix the issue or let the saga decide to drop the message.
A similar way could be to use a lookup table, where you put the list of messages that aren't relevant for processing. Such a table would be similar to the list of sagas. It seems that this way would also require scheduling. Both here, and for the saga, I'd recommend using a separate receive endpoint (a queue) for the DropIt message, with only one consumer. It would prevent DropIt messages from getting stuck behind the integration messages that are waiting to be processed (and some should be already dropped)
Use RMQ management API to remove messages from the queue. This is the worst method, I won't recommend it.
From what I understand, you're building a system that sends messages to 3rd party systems. In other words, systems you don't control. It has an API but compensating actions aren't always possible, because the API doesn't provide it or because actions are performed inside the 3rd party system that can't be compensated or rolled back?
If possible try to solve this via sagas. Make sure the saga executes the different steps (the sending of messages) in the right order. So that messages that cannot be compensated are sent last. This way message that can be compensated if they fail, will be compensated by the saga. The ones that cannot be compensated should be sent last, when you're as sure as possible that they don't have to be compensated. Because that last message is the last step in synchronizing all systems.
All in all this is one of the problems with distributed systems, keeping everything in sync. Compensating actions is the way to deal with this. If compensating actions aren't possible, you're in a very difficult situation. Try to see if the business can help by becoming more flexible and accepting that you need to compensate things, where they'll tell you it's not possible.
In some exceptional situations I need somehow to tell consumer on receiving point that some messages shouldn’t be processed.
Can't you revert this into:
Tell the consumer that an earlier message can be processed.
This way you can easily turn this in a state machine (like a saga) that acts on two messages. If the 2nd message never arrives then you can discard the 1st after a while or do something else.
The strategy here is to halt/wait until certain that no actions need to be reverted.
I'm new to NServiceBus and trying to find the best way to model a scenario which uses compensating transactions.
For example, say I have a typical BookHotel scenario:
In the happy case, the messaging flow would proceed as follows:
BookHotelCommand --> BookHotelSaga
BookFlightCommand --> Reply IFlightBookedMessage
BookRentalCommand --> Reply IRentalBookedMessage
ReplyToOriginator --> HotelBookedMessage
How would I model compensating transactions in the above flow? I was initially thinking of calling a "UnbookHotelSaga" in one of the replies above, based on some business conditions. However, I seem to be running into some challenges with getting this working. Can someone with Saga experience comment if this is the right approach.
Here is the scenario I was thinking would work by calling another Saga:
BookHotelCommand --> BookHotelSaga
BookFlightCommand --> Reply IFlightBookedMessage
BookRentalCommand --> (condition satisfied) --> UnbookHotelCommand --> UnbookHotelSaga
UnbookRentalCommand --> Reply IUnbookRentalMessage
UnbookFlightCommand --> Reply IUnbookFlightMessage
UnbookHotelCommand --> ReplyToOriginator --> UnbookedHotelMessage
Can someone please advise on the best-practices approach to implementing compensating transactions?
I'm not really sure I understand the long running process and what it should do. Some more information on functionality would probably help.
One of the first things I noticed was mentioning of IUnbookRentalMessage. First of all, don't use I at the start of messages. The fact that they can be interfaces, has to do with polymorphism and multiple inheritance features of .NET. Messages themselves have no technical meaning on the wire and you should therefore not include the I.
Also, commands are in imperative tense and events in past tense. So BookFlight for a command and FlightBooked for an event.
You could theoretically create multiple sagas that all take part in a single long running business process. A saga called BookingPolicy or BookingProcess or BookingSaga to orchestrate the entire process. And FlightBookingPolicy for the flight and HotelBookingPolicy for the hotel.
If you start out with a BookFlight command, the FlightBookingPolicy could publish an event called FlightBooked. The BookingPolicy could use that event to start its own instance of the saga. So for example, the (ASP.NET) website that sends all the commands, would not have to know about the BookingPolicy. It just sends the appropriate commands with the appropriate data. The same goes for hotel, car, etc.
Then at some point, the website sends a CommitBooking or FinishUpMyVacation command, which does arrive at the BookingPolicy saga and that finalizes the entire booking. It sends an event BookingFinishingUp or something. Based on that event, some handler might deduct money from a creditcard. Another handler does integration with 3rd parties to actually submit the vacation. Another handler sends out emails. Etcetera.
Finally when the BookingPolicy (or even another saga) is finished, the BookingPolicy saga will publish an event called BookingFinished and the appropriate FlightBookingPolicy and HotelBookingPolicy and CarBookingPolicy also wrap up and end their work. Whatever that may be.
Does that make sense? If you want, you can also continue the conversation on https://discuss.particular.net/ or support#particular.net.
I'm struggling to understand how to implement Eventual Consistency with the exposed example of BacklogItems and Tasks from Vaughn Vernon. The statement I've understood so far is (considering the case where he splits BacklogItem and Task into separate aggregate roots):
A BacklogItem can contain one or more tasks. When all remaining hours from a the tasks of a BacklogItem are 0, the status of the BacklogItem should change to "DONE"
I'm aware about the rule that says that you should not update two aggregate roots in the same transaction, and that you should accomplish that with eventual consistency.
Once a Domain Service updates the amount of hours of a Task, a TaskRemainingHoursUpdated event should be published to a DomainEventPublisher which lives in the same thread as the executing code. And here it is where I'm at a loss with the following questions:
I suppose that there should be a subscriber (also living in the same thread I guess) that should react to TaskRemainingHoursUpdated events. At which point in your Desktop/Web application you perform this subscription to the Bus? At the very initialization of your app? In the application code? Is there any reasoning to place domain subscriptors in a specific place?
Should that subscriptor (in the same thread) call a BacklogItem repository and perform the update? (But that would be a violation of the rule of not updating two aggregates in the same transaction since this would happen synchronously, right?).
If you want to achieve eventual consistency to fulfil the previously mentioned rule, do I really need a Message Broker like RabbitMQ even though both BacklogItem and Task live inside the same Bounded Context?
If I use this message broker, should I have a background thread or something that just consumes events from a RabbitMQ queue and then dispatches the event to update the product?
I'd appreciate if someone can shed some clear light over this since it is quite complex to picture in its completeness.
So to start with, you need to recognize that, if the BacklogItem is the authority for whether or not it is "Done", then it needs to have all of the information to compute that for itself.
So somewhere within the BacklogItem is data that is tracking which Tasks it knows about, and the known state of those tasks. In other words, the BacklogItem has a stale copy of information about the task.
That's the "eventually consistent" bit; we're trying to arrange the system so that the cached copy of the data in the BacklogItem boundary includes the new changes to the task state.
That in turn means we need to send a command to the BacklogItem advising it of the changes to the task.
From the point of view of the backlog item, we don't really care where the command comes from. We could, for example, make it a manual process "After you complete the task, click this button here to inform the backlog item".
But for the sanity of our users, we're more likely to arrange an event handler to be running: when you see the output from the task, forward it to the corresponding backlog item.
At which point in your Desktop/Web application you perform this subscription to the Bus? At the very initialization of your app?
That seems pretty reasonable.
Should that subscriptor (in the same thread) call a BacklogItem repository and perform the update? (But that would be a violation of the rule of not updating two aggregates in the same transaction since this would happen synchronously, right?).
Same thread and same transaction are not necessarily coincident. It can all be coordinated in the same thread; but it probably makes more sense to let the consequences happen in the background. At their core, events and commands are just messages - write the message, put it into an inbox, and let the next thread worry about processing.
If you want to achieve eventual consistency to fulfil the previously mentioned rule, do I really need a Message Broker like RabbitMQ even though both BacklogItem and Task live inside the same Bounded Context?
No; the mechanics of the plumbing matter not at all.
I have a WF4 Service with a flowchart as the root activity. It contains multiple correlated receive activites and decision branching to step through an approval process. The receive activities work perfectly until I try and use one as the trigger for a pick branch.
I am running tracking so can see that the receive is opened and in the persistance I can see the associated bookmark. When I send a client message with the receive type it does not trigger. I have a delay pick branch that fires OK but then the subsequent receive also does not work.
I have checked these receive activities individually and they work OK when not used as the pick trigger. I have tried the pick within a Sequence and a While but no difference.
I cannot see any difference between my implementation and may examples on the web. Am I missing something extra required when the receive is encapsulated by a pick branch?
There is nothing special about a PickBranch trigger that would cause a receive to behave differently so I suspect it is something with the Receive itself. What kind of errors are you seeing at the client application?