// Single entry-point for all transitions
public function transition(
Document $document,
DocumentStatus $toStatus,
User $actor,
?string $comment = null
): Document {
// 1. State machine guard
if (!$document->canTransitionTo($toStatus)) {
throw ValidationException::withMessages([...]);
}
// 2. RBAC guard
if (!$actor->canPerformTransition($toStatus)) {
throw new AuthorizationException(...);
}
// 3. Atomic DB write (status + ledger + audit)
DB::transaction(function() { ... });
// 4. Fire event — listeners handle mail async
event(new DocumentTransitioned(...));
return $document->fresh();
}