That's a good point and one of the reasons why calling non-API methods alone is not sufficient. Camunda implements a command pattern, i.e. every API interaction is a command (you will find that when you have a look at the service implementations). Around every command, the process engine does cross-cutting things like starting transactions or logging some things. Throughout a command, an entity cache is managed that is always flushed before the command ends. This flush is what makes changes to the database. So, the general approach would be implementing a new command that implements the migration logic and updates the in-memory entities. The existing infrastructure takes care of updating the persisted state. To do this, good knowledge about engine internals is required, so I recommend to study the sources a little and understand how things work in the engine, for example by debugging simple API interactions.
However, there is a specific reason why I think CMMN migration cannot be easily built even when using internal API: A case execution's definition ID is so far not supposed to be updated, so it is not part of the mybatis object-relational mapping (see https://github.com/camunda/camunda-bpm-platform/blob/7.8.0-alpha3/engine/src/main/resources/org/camunda/bpm/engine/impl/mapping/entity/CaseExecution.xml#L46-L55). It is not easily possible to extend these mappings without directly modifying the engine sources. That and the inherent complexity of this topic (e.g. ensuring that the resulting case instance state is consistent) is why I think that having this as a core engine feature makes more sense than building on top of the engine.
Sure, I will be there