I don’t think there’s a good, authoritative answer for this — the literature is kind of a mess.
My suggestion is to start by trying to understand the problem from a domain modeling perspective alone; after that, take those ideas and try to see how the event sourcing patterns fit.
Very broad picture: what we are trying to do is express the dynamics of our domain using the domain’s own vocabulary, but the tools that we are using to do that are general purpose.
The information coming to us over the network is, at one level of detail, just a sequence of bytes. We take that sequence of bytes, and start parsing it into more specialized forms (is it an HTTP request? does it have a UTF-8 document in the payload? Does that document conform to the json standard? do the fields in that json document conform to our more specialized schema, and so on). And eventually we can say “this is a Refund request for Order 12345”.
To understand the dynamics of that request – we are generally going to need to consult other information about Order 12345 that we have somewhere else. So our information, in its specialized form, needs to be turned into some more general purpose thing that is, in effect, a query for the data. We send that off to our data store, and get a general purpose message back, that also has to run through a parser obstacle course until we have Order.
Once we have all of the information in its domain representation, we can now apply the domain rules to it. At the top level, this is expressed in the vocabulary of the domain. But what we are really doing is manipulating information, which is captured in data structures. It’s not turtles all the way down, but again a transition from domain concepts to general purpose data structures and primitives.
These transitions are normally handled by parsers. (Note: this is one of the places where I think the literature is weak, as it tends to emphasize checking in constructors — which isn’t a bad idea, but it is a different idea, it serves a different purpose).
So, where do we parse? The usual answer is that we want to parse out near the boundaries, where things aren’t object oriented anyway. After all, parsing isn’t a domain concern – it’s a compensation for the fact that we’re changing the information we need into different shapes.
Thus, the general rule is that you are parsing information into its modeled form before the dynamics are invoked.
the conversion could fail.
Yes, it could, but you need to be really careful about what you mean by that. Being strict about untrusted information is a good idea. If you want to say that your automated happy path should only permit the introduction of new email addresses that are RFC 822 compliant addr-spec, that’s probably fine. But if the admin updates your trusted store to deliberately introduce a non-compliant address, your automation probably shouldn’t be vetoing that decision. But if the data and the checksum don’t match, you might want to put a full stop on the entire world until things get fixed, because the admin shouldn’t ever do that on purpose, and so on.
Event sourcing… well, event sourcing adds a second layer of bad literature on top of the mess we already have.
RefundApproved is a domain concept, and should therefore be modeled in your domain. Because the decision to approve the refund is coming out of your domain dynamics, the interface to create that event should be expressed in the domain language, not in general purpose language. When you query RefundApproved to get an Amount, you are getting a domain value, not a primitive.
Because domain values are interchangeable (nobody cares which 10 USD the refund amount is), you’ve got some freedom to decide whether RefundApproved is a facade in front of a general purpose data structure, or if it is a facade in front of a graph of domain values.
When you parse the event, to turn it into a representation that you can store, you will eventually need to get to some general purpose representation of the information.