refactor(form-builder): make identity-match listener synchronous

TriggerPersonIdentityMatchOnFormSubmit was queued, which meant
identity_match_status stayed null in the HTTP response body for
public event_registration submissions — the portal confirmation
page rendered without the IdentityMatchBanner until a queue worker
caught up.

Eager state transitions belong in the request lifecycle. The
listener is now an orchestrator that writes pending/matched/none
synchronously. When FORM-05 proper lands with heavy matching
logic (PersonIdentityService::detectMatchesByValues, fuzzy name
matching over the whole org), the heavy work will dispatch as a
separate queued job from within this same listener — sync state
transition + async resolution.

- Remove ShouldQueue, InteractsWithQueue trait, $queue property
- Existing try/catch error containment unchanged (sibling
  listeners §31.10 tag sync, §31.3 shift provisioning keep running)
- Add HTTP-response contract test locking in sync behaviour:
  submit returns data.identity_match.status='pending' on first
  response, without any queue worker running.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-23 21:18:08 +02:00
parent 1875e79ce1
commit fda8033633
2 changed files with 57 additions and 7 deletions

View File

@@ -9,8 +9,6 @@ use App\Events\FormBuilder\FormSubmissionSubmitted;
use App\Models\FormBuilder\FormSubmission;
use App\Models\Person;
use App\Services\PersonIdentityService;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Support\Facades\Log;
/**
@@ -28,13 +26,18 @@ use Illuminate\Support\Facades\Log;
* Failure mode per §31.1: log at error level, never rethrow so sibling
* listeners on the same event (§31.10 tag sync, §31.3 shift provisioning)
* keep running.
*
* Runs synchronously (no ShouldQueue) so identity_match_status is
* already written by the time the HTTP submit-response serialises the
* submission the portal's IdentityMatchBanner then renders on first
* confirmation-page load instead of after a queue worker tick. When
* FORM-05 proper adds heavier value-based matching, that work will
* dispatch as a separate queued job from within this listener so the
* eager state transition stays sync and the slow resolution stays
* async.
*/
final class TriggerPersonIdentityMatchOnFormSubmit implements ShouldQueue
final class TriggerPersonIdentityMatchOnFormSubmit
{
use InteractsWithQueue;
public string $queue = 'default';
public function __construct(
private readonly PersonIdentityService $identityService,
) {}