Rules Hub
Code Ruleset
Rule priority, scope & exceptions
Use this to align rules with the senior-level structure (P0/P1/P2, scope, exceptions/tradeoffs).
Apply the same locking strategy across all related write paths
If one write path that mutates a shared balance/aggregate takes a pessimistic lock, the sibling write paths that mutate the same data must lock too, or concurrent requests corrupt it.
Bad example
| 1 | // createTransaction takes setLock('pessimistic_write')... |
| 2 | // ...but createDividend / updateDividend touch the same balance with no lock |
Explanation (EN)
A writer outside the shared concurrency protocol can overwrite a concurrent balance update.
Objašnjenje (HR)
Good example
| 1 | await dataSource.transaction(async (manager) => { |
| 2 | const portfolio = await manager.getRepository(Portfolio) |
| 3 | .createQueryBuilder("p").setLock("pessimistic_write") |
| 4 | .where("p.id = :id", { id }).getOneOrFail(); |
| 5 | await updateBalance(manager, portfolio, change); |
| 6 | }); |
Explanation (EN)
Every cooperating writer takes the existing aggregate lock before invariant-dependent reads and writes, on the same transaction manager.
Objašnjenje (HR)
Notes (EN)
For a read-modify-write invariant, identify every writer and the database isolation/locking protocol; acquire the same existing aggregate lock before reading dependent state in one transaction.
Exceptions / Tradeoffs (EN)
Atomic SQL updates, constraints or optimistic version checks can enforce the invariant without a pessimistic lock. Use a consistent lock order and handle retryable conflicts.