So let me rephrase my previous comment and adjust it to your particular example.
With a command being the API in question you have
- command handlers which act as producers
- command senders which act as consumers
The change you are talking about “add some extra field(s)” is
- breaking change for all producers. They must know how to handle the new field as new consumers may be using it.
- non-breaking change for all consumers. They can still use the old version.
With that in mind you have several options:
- Update the command keeping the FQCN and update the handler(s) to use the new data. Then you have the following options regarding handling old commands:
- Reject the command if data is missing.
- Add some logic in the command that generates the missing data.
- Make sure all handlers can handle missing data.
- Introduce a new command with different FQCN and respective new command handler(s). Keep the old commands and handlers (perhaps extract common logic to avoid duplication). Threat both path the same from domain logic perspective.
Please note that things would be different if you were to remove or rename a field. Then you are actually breaking everyone.
I’m sorry but I can’t answer “What’s the best approach”. Based on what you’ve said so far, the above are conceptually the approaches you can possibly take. Which one makes most sense is something you need to evaluate in the context of the specific application taking into account its overall impact on the system (including any side effects).
Regarding the event upcasters you mentioned, that is essentially a way to implement option 1.2
from the above. Arguably that is the only option that makes sense for processing persistent immutable events. With commands and queries you simply have more options and thus no silver bullet.
I hope that helps even though it does not directly answer your question.