Trigger.OldMap equivalent in ChangeEvent?
The Change Data Capture feature targets synchronization of changes to some persistent store. Because of this fact, each event only contains enough information to apply a specific change to a stored record.
As a result, Change Data Capture events contain neither old values prior to a change nor a complete snapshot of an sObject at any point in time besides its creation or undeletion.
The section Apex Change Event Messages in the Change Data Capture Developer Guide explains what data is actually available in each message:
Create For a new record, the event message contains all fields, whether populated or empty. […]
Update For an updated record, the event message contains field values only for changed fields. Unchanged fields are present and empty (null), even if they contain a value in the record. […]
Delete For a deleted record, all record fields in the event message are empty (null).
Undelete For an undeleted record, the event message contains all fields from the original record, including empty (null) fields and system fields.
The use of the after insert
event is somewhat deceptive: the insert
event in question is really the delivery of the change event, not the original record. By the time the change event is delivered, the old field values are gone in the database (the change was committed).
Since the expectation with the CDC feature is that you, the client, have some persistent store against which you're applying changes to synchronize, it's assumed that you already have the old values. This makes some trigger use cases difficult to adapt to the async model.
The "Async Apex Trigger" name, to me, is a bit overbroad. These triggers cannot replace or asynchronize many standard trigger use cases; their capabilities need to be carefully evaluated against a proposed use due to the specificity of the underlying feature set.