Patterns
Implicit Lock
Let the repository or framework take the lock so application code never has to remember the lock API.
Implicit Lock
The pattern
The lock is acquired by the framework, the layer supertype, or the repository — not by the application code that needs the lock. A repository call like repo.find(id, LockMode.PESSIMISTIC_WRITE) takes the lock without the caller spelling out SELECT FOR UPDATE. The pattern’s job is to remove the lock call from every site that would otherwise need it.
The contrast is with explicit locks, where the application code calls BEGIN; SELECT … FOR UPDATE; … directly. Explicit is more visible but error-prone: every developer has to remember the lock, and every forgotten lock is a bug.
Where it fits
When the team has decided “we always lock this kind of load” and don’t want to trust every developer to remember. When the lock semantics are tied to a specific load mode (read-only vs read-write vs write-only). When the framework or layer supertype already wraps the load anyway — putting the lock there is free.
It does not fit when most loads don’t need the lock (the overhead matters) or when different loads need different lock modes (then the caller must specify).
A worked example
Every aggregate in the Storeboost domain uses Postgres xmin as an implicit optimistic concurrency token, configured once at the EF mapping layer. Application code never writes the version check by hand.
// Creative.cs
/// <summary>Optimistic concurrency token mapped to PostgreSQL <c>xmin</c>.</summary>
public uint Version { get; private set; }
// CreativeConfiguration.cs (excerpt)
internal sealed class CreativeConfiguration : IEntityTypeConfiguration<Creative>
{
public void Configure(EntityTypeBuilder<Creative> builder)
{
// ... other property mappings ...
builder.Property(e => e.Version).IsRowVersion();
}
}
IsRowVersion() makes EF Core emit a WHERE xmin = @original_xmin clause on every UPDATE and translate a zero-rows-affected result into a typed DbUpdateConcurrencyException. The framework, not the application, owns the version handshake. The same convention appears in roughly twenty *Configuration.cs files (Campaign, Panel, Deal, PcaReportShare, CreativeEditor, …) — every aggregate that inherits the mapping automatically inherits optimistic concurrency.
Tradeoffs
- Can’t forget to lock — every load through the framework gets it.
- Consistent semantics across the codebase.
- Hidden behavior — readers of the code don’t see what’s locked.
- Hard to override for the rare load that doesn’t want the lock.
- Framework coupling — switching frameworks means rewriting every load site.
When to use, when not to
Use when the lock is nearly universal and forgetting it would be a bug. Don’t use when most loads don’t need the lock, or when different loads need different lock modes (then it must be explicit). Pair naturally with Coarse-Grained: an Implicit Lock on the root is Coarse-Grained for free.