For keys bound to commands, the key event is indeed preventDefault-ed, and the command fires a transaction itself.
For text input, the native behavior goes through unmolested. The editor has a MutationObserver (in view/src/domobserver.js) on its editable content, and notices that something happens. It then reads the changed region and compares it to the corresponding range of state.doc (in view/src/domchange.js). If there’s a difference, it is converted to some step—usually a replace step, but if the DOM change looks like it just adds or removes a mark it might be a mark step. That is then added to a transaction and dispatched.
The entire new state is computed before any DOM is updated. Once a new state is available, it is passed to updateState, which syncs the DOM to the new state. If the canonical DOM for the new document corresponds to the DOM changes already produced by the typed key, there may indeed not be a need to mutate the DOM at all.