# Domain Boundaries Phase 2 Design

Date: 2026-05-21

## Goal

Phase 2 turns the architecture audit into enforceable modular-monolith boundaries without rewriting the platform. It extracts the highest-risk UI-owned workflows first, creates public module APIs, and adds architecture checks that stop new high-risk coupling while reporting known legacy debt.

## Design Choice

### Recommended approach: baseline plus strict high-risk checks

The architecture check starts with two behaviors:

- Strict failures for forbidden new coupling and legacy model resurrection.
- Report/baseline findings for broad existing debt that needs staged refactoring.

Strict examples:

- Runtime use of legacy Corporate ownership terms outside allowed compatibility locations.
- WhatsApp writing Enrollment or Payment state directly instead of calling module APIs.
- Architecture tests failing when extracted workflow Actions disappear.

Baseline/report examples:

- Existing large write-heavy Livewire components.
- Existing admin route protection style drift.
- Existing navigation hardcoded URL findings that predate the check.

This prevents new damage immediately without blocking Phase 2 on every historical hotspot.

## Public Module APIs In This Slice

### Common contracts

`app/Domain/Common/Contracts` defines cross-module capabilities:

- `CreatesEnrollments`
- `SendsMessages`
- `AllocatesPayments`
- `GeneratesSchedules`
- `DispatchesTrackingEvents`

Contracts remain small. A module consumes a capability, not a concrete class, when the dependency crosses an ownership boundary.

### Common data

`app/Domain/Common/Data` provides command-style DTOs for the extracted workflows:

- `ConvertB2bLeadToOrganizationData`
- `SendWhatsAppReplyData`
- `CreateB2bLeadFromConversationData`
- `AttendanceSessionData`

These DTOs carry validated values. Livewire remains responsible for validation and UI state, then passes DTOs to Actions.

## First Workflow Extractions

### Corporate lead conversion

Extract `ConvertB2bLeadToOrganizationAction` from `B2BLeadDetailPage`.

Action owns:

- Matching or creating the Organization.
- Lead conversion status.
- Activity and organization audit writes.
- Transaction boundary.
- `B2bLeadConverted` dispatch after commit.

Livewire keeps:

- Authorization.
- Existing-organization confirmation state.
- Flash/refresh behavior.

### WhatsApp conversation actions

Extract:

- `CreateB2bLeadFromConversationAction`
- `SendWhatsAppReplyAction`

Actions own:

- B2B lead linking and activity writes.
- WhatsApp outbound message row creation.
- Cloud provider send attempt and hybrid outbox fallback.
- Conversation/contact timestamp state updates.
- Transaction ownership where multi-model state changes occur.

Livewire keeps:

- Authorization.
- Reply/body validation.
- Toast notifications.

### Manual attendance sessions

Extract Actions:

- `CreateAttendanceSessionAction`
- `UpdateAttendanceSessionAction`
- `CancelAttendanceSessionAction`
- `ReopenAttendanceSessionAction`

Actions own:

- Session persistence and status workflow writes.
- Audit writes.
- Transaction boundaries.
- `AttendanceSessionCreated` after create.

`AdminCohortSessionsPage` keeps preview UI, form validation, and existing conflict validation for this slice unless a safe shared validator can be moved with the create/update Actions.

## Query Boundary Slice

Create initial Query classes where render logic is actively reused or safety-sensitive:

- `WhatsAppConversationListQuery`
- `OrganizationInvoicesQuery`
- `CorporateReceivablesQuery`
- `InstructorScheduleQuery`
- `EnrollmentPaymentsQuery`

This phase may introduce the Query types and move the targeted Livewire reads when the calling surface is touched. It does not require every existing query in the project to move immediately.

## Events

Introduce the requested stable event classes:

- `EnrollmentCreated`
- `DepositPaymentApproved`
- `AttendanceSessionCreated`
- `AttendanceMarked`
- `B2bLeadConverted`
- `WhatsAppLeadCreated`
- `CorporateInvoiceIssued`
- `CorporatePaymentAllocated`

Phase 2 dispatches events from extracted workflows where the write path is touched now. Other events are created as contracts and wired when their owner Action is introduced or already has a safe workflow seam.

Events carry IDs and safe scalar metadata only. They are after-commit ready.

## Architecture Check

Add:

```bash
php artisan app:architecture-check
```

The command emits grouped findings and returns non-zero only for strict findings.

Initial checks:

1. Forbidden legacy runtime references.
2. Forbidden direct WhatsApp writes to Enrollment/Payment.
3. Write-heavy Livewire authorization inventory.
4. Admin route permission inventory.
5. Sensitive download route inventory.
6. Admin/corporate navigation hardcoded URL inventory.

The command is intentionally conservative. It reports uncertain findings rather than pretending static heuristics are a perfect security proof.

## Tests

Add:

- `DomainBoundaryTest`
  - verifies required Actions/contracts exist.
  - verifies forbidden legacy references are not reintroduced in runtime application code.
  - verifies WhatsApp does not directly own Enrollment/Payment writes in the extracted UI flow.
  - verifies the architecture command can run.
- `WorkflowActionTest`
  - conversion action behavior.
  - WhatsApp B2B link/reply action behavior.
  - manual attendance session create/cancel/reopen action behavior.

Existing workflow feature tests remain the regression safety net.

## Documentation

Create `docs/domain_boundaries.md` with:

- Ownership matrix.
- Allowed and forbidden dependencies.
- DTO/Action/Query/Event patterns.
- Transaction rule.
- Architecture-check modes and baseline strategy.

## Non-Goals

- Rewriting every Livewire component.
- Moving every existing query in one pass.
- Replacing all global Services.
- Full event-driven rebuild of CRM/automation before owner Actions exist.
- Renaming `Teacher`/`Instructor` or `Session`/`Cohort` in Phase 2.
