# Phase 3 Event Bus And Automation Foundation Design

Date: 2026-05-21

Status: approved scope for safe incremental implementation.

## Goal

Phase 3 establishes an event-driven foundation for future CRM, marketing automation, reminders, notifications, WhatsApp orchestration, and analytics without rewriting the platform's sensitive registration, finance, attendance, or Corporate workflows.

The foundation must make business facts discoverable and automation-safe while keeping current operational flows stable.

## Scope Decision

Phase 3 uses **safety over coverage**.

This phase will:

- Create the required lifecycle Event classes as stable contracts.
- Wire events only from safe, already understood seams:
  - Actions extracted in Phase 2.
  - Clear and tested domain services.
  - Narrow observers where the existing behavior is already event-driven.
- Add listener boundary rules and initial orchestration examples where the write path is safe.
- Add event documentation and architecture checks that discourage new side-effect spaghetti.

This phase will not:

- Refactor old payment flows broadly.
- Refactor old registration flows broadly.
- Refactor attendance flows broadly.
- Rewire every Corporate workflow in one pass.
- Build a CRM or full marketing automation engine.

Events that are not safely emitted now remain documented with planned emitters.

## Core Principle

The target lifecycle shape is:

```text
Business Action
  -> Domain Event
    -> Listener / Job / Future Automation
```

UI components may show toasts and update view state, but they must not become long-term owners of cross-module notifications, WhatsApp dispatch, reminders, analytics, or CRM-style follow-up logic.

## Event Ownership

Events live under their module:

```text
app/Domain/{Module}/Events/
app/Domain/{Module}/Listeners/
app/Domain/{Module}/Jobs/
```

Module ownership remains as defined by `docs/domain_boundaries.md`.

## Event Contract Rules

Each lifecycle event must be:

- Immutable.
- Automation-safe.
- Free of Livewire state.
- Free of provider payload dumps.
- Free of secrets, tokens, and private file paths.
- Built from IDs and safe scalar metadata.

Event examples:

- `EnrollmentCreated`
  - `enrollmentId`
  - `courseSessionId`
  - `userId`
  - `actorUserId`
  - `source`
  - `occurredAt`
- `WhatsAppReplySent`
  - `conversationId`
  - `messageId`
  - `actorUserId`
  - `mode`
  - `occurredAt`

## Initial Event Catalog

Phase 3 creates Event classes for:

### Enrollment

- `EnrollmentCreated`
- `EnrollmentConfirmed`
- `EnrollmentCancelled`

### Payments

- `DepositPaymentStarted`
- `DepositPaymentApproved`
- `PaymentReceiptUploaded`
- `BalancePaymentCompleted`

### Attendance

- `AttendanceSessionCreated`
- `AttendanceMarked`
- `StudentNoShowDetected`

### Corporate

- `B2bLeadCreated`
- `B2bLeadConverted`
- `ProposalSent`
- `ProposalAccepted`
- `AgreementActivated`
- `CorporateCohortStarted`
- `CorporateReportGenerated`
- `CorporateInvoiceIssued`
- `CorporatePaymentAllocated`

### WhatsApp

- `WhatsAppLeadCreated`
- `WhatsAppConversationAssigned`
- `WhatsAppReplySent`

### Events And Webinars

- `EventRegistrationCreated`
- `WebinarReminderTriggered`
- `WebinarAttended`

### Tracking

- `ConversionTracked`
- `FunnelStepCompleted`

## Safe Wiring Slice

### Phase 2 Actions

Wire lifecycle events from:

- `ConvertB2bLeadToOrganizationAction`
  - emits `B2bLeadConverted`.
- `CreateB2bLeadFromConversationAction`
  - emits `WhatsAppLeadCreated`.
- `SendWhatsAppReplyAction`
  - emits `WhatsAppReplySent`.
- `CreateAttendanceSessionAction`
  - emits `AttendanceSessionCreated`.

### Existing Narrow Event Seams

Existing observers and provider registrations already emit or consume selected events for enrollment/payment messaging. Phase 3 may adapt narrow event contracts at these seams only when regression coverage is already clear.

If a seam requires broad state mutation redesign, it is documented as a planned emitter instead of changed now.

## After-Commit Rule

Business events must be dispatched only after successful persistence.

Preferred approaches:

- Dispatch with `DB::afterCommit(...)` when the Action owns the transaction.
- Use event classes that support after-commit dispatch where the event boundary is already well-defined.

No listener should observe a lifecycle event for a transaction that later rolls back.

## Listener Boundaries

Listeners may:

- Queue notifications.
- Queue WhatsApp messages.
- Schedule reminders.
- Record tracking facts.
- Create future CRM tasks through owned Actions when that layer exists.

Listeners must not:

- Mutate another module's workflow state directly.
- Confirm payments, create enrollments, or bypass scheduling ownership directly.
- depend on UI-only data.

When a listener needs a business mutation, it calls an Action owned by that module.

## Idempotency Foundation

Phase 3 establishes idempotency rules before introducing broad automation:

- Provider sends and reminder scheduling need stable idempotency keys.
- Tracking listeners must tolerate duplicate delivery.
- Job retries must not duplicate user-visible messages.
- Existing message logs/outbox records should be reused where they already provide duplicate resistance.

The first implementation slice may include idempotent listener scaffolding and documentation even where the actual future CRM listener is not yet active.

## Notification And Tracking Direction

Notifications and analytics should progressively listen to lifecycle events instead of being embedded in workflows.

Phase 3 specifically prepares for:

- Enrollment notifications after enrollment/confirmation events.
- WhatsApp actions after lifecycle events instead of UI side effects.
- Tracking reactions to `EnrollmentConfirmed`, `DepositPaymentApproved`, `WebinarAttended`, and `B2bLeadConverted`.

Where current implementation already uses legacy messaging events, Phase 3 documents the bridge rather than replacing every path at once.

## Event Registration Strategy

The project currently registers listeners through service providers rather than a dedicated `EventServiceProvider`.

Phase 3 keeps registration discoverable by:

- Grouping new registrations by module provider or a focused event bus provider.
- Avoiding scattered listener registration in Livewire or Actions.
- Documenting listeners in `docs/event_catalog.md`.

## Architecture Checks

`php artisan app:architecture-check` will gain baseline/strict checks for:

- Direct notification orchestration calls in Livewire that should be reviewed.
- Direct WhatsApp provider sends inside business Actions outside the WhatsApp module boundary.
- Direct analytics dispatch from UI surfaces where lifecycle events should be used.
- Cross-module state writes that bypass approved Actions.

Heuristics may report baseline debt instead of blocking when certainty is low.

## Documentation Outputs

Phase 3 creates:

- `docs/event_catalog.md`
  - event name
  - owner
  - payload
  - current listeners
  - current emitter or planned emitter
  - automation safety
  - idempotency note
- `docs/event_driven_architecture.md`
  - lifecycle event conventions
  - after-commit rule
  - listener boundaries
  - retry and failure strategy
  - CRM readiness

## Testing Strategy

Create architecture tests for:

- Event contracts existing with safe scalar payload shape.
- Events emitted from the safe wiring slice.
- After-commit behavior at newly wired transactional seams.
- Listener registration and queued behavior where added.
- Duplicate/idempotent behavior for initial orchestration scaffolding.
- Architecture checks covering side-effect anti-patterns.

Regression tests for touched workflows remain mandatory.

## Acceptance Criteria

- Required lifecycle Event classes exist.
- Safe events are emitted from selected Actions/services/observers only.
- Non-wired events are documented with planned emitters.
- Event catalog and event-driven architecture docs are present.
- Listener boundary and idempotency rules are explicit.
- Architecture check discourages new spaghetti side effects.
- No broad legacy payment, registration, attendance, or Corporate refactor is introduced in this phase.
