Event Sourcing command or event from external syst

2019-03-19 04:52发布

问题:

In most cases I understand the distinction between a command and an event in a CQRS + ES system. However, there is one situation that I can't figure out.

Suppose I am building a personal finance tracking system, where a user can enter debits/credits. Clearly these are commands, and once they are validated the domain model gets updated and an event is published. However, suppose that credit/debit information also comes directly from external systems e.g. the user's florist sends a message that he has charged the user's credit card for his recurring Mother's Day delivery. In this case the message seems like it is an event (the charge has already occurred), but it is possible that the message is malformed and will be rejected. So is it really a command? But then there needs be a way to send an ACK/NACK, which isn't the case here (the florist only sends the message once he knows that the debit has taken place).

EDIT: Just to clarify, I'm not talking about the external system knowing anything about my events and/or commands. I have a component that receives data from the external system and then has to publish an event or send a command. The question is, which of event or command should my component use?

回答1:

This is a perfect example of bounded contexts.

An event that has occurred in another system (or bounded context) that represent the same or business process step, should not be treated as an event that has occurred in the receiving system.

The Florist System Debit event might have a different structure than the Debit event that would have occurred in the Finance Tracking System.

To solve this I would have an endpoint that would listen to the Florist System events and from that I would make the Finance Tracking System issue a command based on the information in the event and maybe combine it with information in the Finance Tracking System if needed. Where this happens could be thought of as a client to the Finance Tracking System or maybe even a "domain service" if you will.

In this particular case an event from the Florist system comes in with information of a transaction that has occurred in the Florist system (bounded context). I would not make any decisions in the domain service, but issue a command to the Finance Tracking System domain where the decision takes place and Finance Tracking System events may be emitted. If the Florist system event appears malformed in the Finance Tracking System you probably don't want to tell the Florist system about that in a Request/Response or Ack/Nack manner. The event was published from the Florist and you would break messaging patterns if you implement something like that. Your messaging infrastructure should allow you to retry the message or even fix the receiving code and retry the message with the new code.

However, if you really need to communicate back the Florist bounded context, the Florist system could subscribe to events from the Finance Tracking System in order to find out whether the transaction was successfully handled. That would only be needed if the main system is deciding whether a transaction may occur or not.

In the case you are describing the Finance Tracking System is more of a transaction log and the only thing you should have to do is to not treat Florist events as Finance Tracking System events. Put something in between that issues commands that result in Finance Tracking System events.

EDIT:

As a response to your edit. You receiving component should send commands to the Finance Tracking System domain which in turn would emit events (as usual).



回答2:

I found this question while wondering the same thing.

I liked Mikael's answer and upvoted it. However, I found another answer on the DDD/CQRS forum that takes a different approach, so figured I'd post it here for others who are looking too.

Here's a quote from Greg Young on a similar question about tracking external state:

In most inventory systems there are no commands.

Or to paraphrase:

Systems that exclusively track external state will have no commands.

Let's rewind and think through how your system might evolve based on this idea (let's also assume you're not building it for yourself):

  • You decide to create a personal financial tracker that records transactions from the outside world, displays graphs, aggregated totals, etc.
  • Because events are for tracking state changes, not commands, you record those. You also write projections in the event handlers for aggregating the events into totals and graph data points.
  • Then you get users. They praise the app, but usage is still low. After digging deeper, you discover that they still use other apps to track finances when, say, there's no API for the local card store for recurring Mother's Day cards, they want to "split" an external transaction into multiple, their bank has no online access, they want to track cash, etc.
  • So you add user-entered transactions. You do so by adding commands that track user-entered transactions. Now we're at the same system as your question.

Note that this variant accepts both commands and events. You wouldn't go back and convert all existing events to commands just because you now allow the user to change things. Instead, you have a system that accepts external transaction events, commands for user-entered transactions and transaction corrections (which create events that modify internal state), and projections to combine these internal and external events for display in the app.

I'm curious though. In hindsight, did the accepted answer end up being a good approach? Or would recording and handling events from the Florist directly like Greg suggests be better?