Skip to content

optimisticUpdate

optimisticUpdate<TEntity>(em, entityType, entity, data, entityName): Promise<void>

Defined in: packages/ddd-mikro-orm/src/concurrency/optimistic-update.ts:59

Executes a single, version-conditioned, autocommitted SQL UPDATE statement on an aggregate entity.

This function handles low-level optimistic locking mechanics for update command repositories:

  • Autocommit Enforcement: Rejects an externally active transaction via assertAutocommit, because multi-statement transactions require commit hooks rather than standalone method decorators to guarantee atomic acknowledgment.
  • Version-Conditioned Write: Issues em.nativeUpdate with filter { id: entity.id, version: entity.getExpectedVersion() } and updates fields including { version: entity.version }.
  • Affected Row Verification:
    • Exactly 1 row affected: Write succeeded; completes cleanly.
    • 0 rows affected: Performs a refreshed diagnostic read (findOne with refresh: true):
      • If the entity is absent, throws EntityNotFoundException.
      • If the entity is present, throws ConcurrencyConflictError.
    • More than 1 row affected: Throws an Error indicating primary-key uniqueness invariant violation.

[!NOTE] This helper does not perform caching, aggregate acknowledgment, or domain event publication. Those concerns are owned by @Cache, @AcknowledgePersisted, and CommandBaseHandler respectively.

TEntity extends VersionedAggregate

EntityManager

The MikroORM EntityManager instance (must be in autocommit mode).

EntityName<TEntity>

The entity class or registered name (e.g. User, Role).

TEntity

The aggregate instance providing id, version, and getExpectedVersion().

EntityData<TEntity>

Field payload to update (the version field is automatically injected from entity.version).

string

Friendly name of the entity for error diagnostics (e.g. 'User').

Promise<void>

If called within an active transaction (em.isInTransaction() === true).

If 0 rows were updated and the entity cannot be found.

If 0 rows were updated and the entity version has diverged.

If unexpected row count (> 1) was affected.

Usage in an update command repository

@PersistedWrite<User>({ cache: { setKey: (user) => cacheKey(User.aggregateName, { id: user.id }) } })
async save(user: User): Promise<UserSnapshot> {
const snapshot = user.toJSON();
await optimisticUpdate(
this.store.em,
User,
user,
{ username: snapshot.username, updatedAt: snapshot.updatedAt },
'User',
);
return snapshot;
}