본문으로 건너뛰기
가이드

이벤트와 비동기 작업 처리하기

업무 저장과 Event를 함께 커밋하고 인가·복구 가능한 소비자를 선택합니다.

이벤트와 비동기 작업 처리하기

Note 수정과 같은 트랜잭션에서 사실을 발행합니다. NoteController::update()에서 인가와 입력 검증을 마친 뒤 기존 단독 update를 다음으로 바꾸세요. $note는 해석된 레코드, $validated는 기존 검증 결과입니다.

코드 예시
PHP
use Illuminate\Support\Facades\DB;
use Nexia\Events\Contracts\EventPublisher;
use Nexia\Events\EventDraft;
use Nexia\Events\LegalEntityScope;

DB::transaction(function () use ($note, $validated): void {
    $note->update($validated);
    app(EventPublisher::class)->publish(new EventDraft(
        eventName: 'workshop.note.updated',
        payload: ['note_public_id' => (string) $note->public_id],
        legalEntityScope: LegalEntityScope::explicit((int) $note->legal_entity_id),
        aggregateType: 'workshop.note',
        aggregateId: (string) $note->public_id,
        schemaVersion: 1,
    ));
});

이 예시는 App 내부 사실을 추가합니다. Scaffold에 해당 Event 선언이 있는 것은 아닙니다. 다른 App이나 Process에 공개하려면 NoteModule에 정확한 payload와 schema version의 lifecycle 계약을 먼저 선언하세요. 독립 공개 연동 Event는 EventDescriptor를 사용하며 같은 Event를 두 경로에 중복 선언하지 않습니다. 전체 형식은 SDK 계약에 있습니다.

Tenant·producer·시각·전송 ID는 Core가 채웁니다. 사용자 위임 작업은 이미 인가된 context의 검증된 actor reference를 전달하고 실행 시 다시 인가합니다. Actor ID만으로 권한이 생기지 않습니다. 공개 payload에는 공개 ID와 선언된 필드를 넣고 모델·보호값·숫자 PK는 보내지 않습니다.

호스트를 통한 소비자 등록

효과의 책임에 따라 등록 방식을 정합니다.

효과SDK 등록
사용자 권한과 독립적인 시스템 projectionAppEventListeners::listen()
Event actor가 위임한 보호 동작ActorDelegatedAppEventListeners::listenDelegated()와 EventActorAuthorizer

두 방식 모두 소유 App을 명시합니다. Provider boot에서는 tenant 조회 없이 등록만 합니다. 호스트는 실행 시 tenant를 복원하고 App·선행 App의 동작 가능 여부를 확인합니다. 위임 작업은 actor를 복원해 실행 직전에 재인가합니다. Event::listen('outbox:...') 직접 등록은 사용하지 않습니다.

EventConsumerRegistration에 고정 consumer key, 정확한 지원 schema version, 복구 모드를 선언합니다. App 간 exact 구독은 활성 catalog Event가 필요합니다. AllPublicEvents에는 빈 Event 목록을 전달하며 호환되는 활성 공개 Event만 받습니다.

실제 소비자 등록 읽기

People App의 src/PeopleCoreServiceProvider.php에 있는 등록 예제입니다. 같은 App의 src/Actions/ConsumeRecruitingAcceptedOffer.php가 처리합니다. 사용자 위임 보호 동작이 아닌 시스템 소유 온보딩 초안을 만듭니다. Workshop에서 People 클래스를 import하지 말고 Workshop 효과에는 자체 소비자를 구현하세요.

코드 예시
PHP
use Amuzcorp\Nexia\PeopleCore\Actions\ConsumeRecruitingAcceptedOffer;
use Nexia\Events\Contracts\AppEventListeners;
use Nexia\Events\EventConsumerRegistration;
use Nexia\Events\EventRecoveryMode;

// Inside PeopleCoreServiceProvider::boot(), in the People App repository.
$this->app->make(AppEventListeners::class)->listen(
    'people',
    'outbox:recruiting.job_offer.accepted',
    ConsumeRecruitingAcceptedOffer::class,
    new EventConsumerRegistration(
        consumerKey: ConsumeRecruitingAcceptedOffer::CONSUMER_KEY,
        recoveryMode: EventRecoveryMode::Replay,
        supportedSchemaVersion: 1,
    ),
);

Handler는 InboxConsumer::run($envelope, self::CONSUMER_KEY, ...)으로 실행하고 producer와 v1 payload를 검사한 뒤 멱등 업무 변경을 적용합니다. 등록과 실행의 consumer key를 맞추세요. Reconcile을 선택하면 별도 reconciler도 등록해야 합니다.

복구와 중복 처리

모드App이 실행 불가능할 때복구 시
ReplayInbox 메시지를 일시 정지 상태로 저장소비자 순서로 재생, dead 행은 명시적 재시도
Reconciledirty generation 기록등록된 멱등 reconciler 실행
Ephemeralpayload 없이 누락 횟수 기록선언한 손실 수용

Claim·효과·처리 표시를 원자적으로 묶는 InboxConsumer를 사용합니다. 다른 경로로도 같은 업무가 실행될 수 있다면 업무 멱등 키나 잠금 upsert도 필요합니다. 처리 여부 조회 후 별도 쓰기는 경쟁 조건을 만듭니다.

Payload 적용 전 Event 이름·producer·schema·tenant·필요 Legal Entity·reference·상관관계를 검사합니다. 업무 위임은 별도 결과 Event로 응답하며 수락·거부·실패·결과 불명을 구분합니다. Timeout만으로 실패를 확정하지 않습니다. 외부 호출은 DB rollback으로 되돌릴 수 없으므로 자체 멱등성과 재시도 처리가 필요합니다.

비동기 경로 실행

Core 루트에서 런타임 서비스를 시작하고 변경한 선언을 갱신합니다.

코드 예시
Shell
task dev:up:runtime
task artisan -- nexia-runtime:generate-app-map
task artisan -- nexia-apps:validate-app-descriptors workshop

Rollback·중복 전달·비활성 소유 App·tenant 누락·위임 권한 회수를 검사하세요. 실행 방법은 App 테스트하기에 있습니다. 과거 사실이 아니라 현재 데이터가 필요한 요청은 App 간 연동 방식 선택하기에서 방식을 고릅니다.

원본 위치: docs/developers/content/ko/building-apps/handle-events-and-async-work.md