# Phase 7.0 — Loyalty Service Foundation

| Field | Value |
| --- | --- |
| Status | Complete (validated + self-healed) |
| Module | loyalty |
| Version | 0.7.0.0 |
| Database | `loyalty_db` |
| API Port | 8004 |
| ADR | ADR-001, ADR-003, ADR-006, ADR-011 |

## Goal

Establish the Enterprise Loyalty Platform as an independent shared service foundation and architectural boundaries only.
Not part of CRM. Reusable by all business modules via API + Events.

## Loyalty Responsibilities (Phase 7.0)

Loyalty owns foundation aggregates only:

- LoyaltyProgram
- MembershipTier (shell for Phase 7.1)
- Member (shell for Phase 7.1)
- PointAccount shell — **no mutable balance** (ledger in Phase 7.2)
- Reward catalog shell (Phase 7.3)
- Campaign shell with versioned rules counter (Phase 7.4)
- LoyaltyAuditLog
- OutboxEvent (ADR-006 transactional publish)

## Service Boundaries

| Loyalty owns | Loyalty does not own |
| --- | --- |
| Aggregates above | CRM sales entities |
| Loyalty HTTP APIs under `/api/v1/*` | Accounting / Posting Engine |
| `loyalty.*` permissions (enforced on routes) | Notification / Communication delivery |
| Publish-only Loyalty events via outbox | Analytics store (Phase 7.8) |
| Platform provider **interfaces** | Identity / Wallet UI |
| | Restaurant / Marketplace / Ecommerce domain data |

Communication with platform and business services is **API + Events only**. No cross-DB access.

## Owned Modules (aggregates)

Independent aggregates in `backend/services/loyalty/app/models/foundation.py`:

1. `LoyaltyProgram`
2. `MembershipTier`
3. `Member`
4. `PointAccount`
5. `Reward`
6. `Campaign`
7. `LoyaltyAuditLog`
8. `OutboxEvent` (infra)

Cross-aggregate links use UUID references inside `loyalty_db` only (no SQLAlchemy relationship graphs).

## External Platform Dependencies (contracts only)

Defined in `app/providers/contracts.py` — **no implementations**:

- `NotificationProvider`
- `AnalyticsProvider`
- `Customer360Provider`
- `AIProvider`
- `ModuleIntegrationProvider`
- `CRMProvider`
- `CommunicationProvider`
- `FileStorageProvider`

## Published Events

| Event | Aggregate |
| --- | --- |
| `loyalty.program.created` / `updated` / `deleted` | loyalty_program |
| `loyalty.tier.created` / `updated` / `deleted` | membership_tier |
| `loyalty.member.created` / `updated` / `enrolled` / `deleted` | member |
| `loyalty.point_account.opened` / `updated` / `deleted` | point_account |
| `loyalty.reward.created` / `updated` / `deleted` | reward |
| `loyalty.campaign.created` / `updated` / `deleted` | campaign |

Events are written to `outbox_events` in the same DB transaction as the mutation (ADR-006). Phase 7.0 does **not** consume platform events.

## API Contracts

| Method | Path |
| --- | --- |
| CRUD + soft delete | `/api/v1/programs` |
| CRUD + soft delete | `/api/v1/tiers` |
| CRUD + enroll + soft delete | `/api/v1/members` |
| Open / list / update / soft delete | `/api/v1/point-accounts` |
| CRUD + soft delete | `/api/v1/rewards` |
| CRUD + soft delete | `/api/v1/campaigns` |
| Audit read | `/api/v1/audit?entity_type=&entity_id=` |
| Health | `/health` |

## Permissions

Route-enforced trees (admin roles or explicit permission strings):

- `loyalty.*`
- `loyalty.programs.*`
- `loyalty.tiers.*`
- `loyalty.members.*`
- `loyalty.point_accounts.*`
- `loyalty.rewards.*`
- `loyalty.campaigns.*`
- `loyalty.audit.*`

## Architecture Decisions

1. Database-per-service (`loyalty_db`) — ADR-001 / ADR-011
2. Row-level `tenant_id` from request tenant context — ADR-003
3. Transactional outbox (`outbox_events`) + EventEnvelope — ADR-006
4. Layering: API → Services → Repositories → Models
5. Direct balance modification forbidden; PointAccount DTOs `extra='forbid'`
6. Optimistic locking (`version` required) on Program / Member / PointAccount updates
7. Campaign `rule_version` increments when rules change
8. Soft delete releases unique business keys (`__del__{id}`) so codes can be reused
9. At most one `is_default` program per tenant (app clear + partial unique index)
10. Invalid `X-Tenant-ID` → `400 invalid_tenant_id`
11. Permission inheritance: `loyalty.view`, resource `*.manage`, `loyalty.manage`
12. Production rejects default DB credentials and `debug=True`

## Database / ER (logical)

```
LoyaltyProgram 1──* MembershipTier
LoyaltyProgram 1──* Member 1──1 PointAccount
LoyaltyProgram 1──* Reward
LoyaltyProgram 1──* Campaign
* ── LoyaltyAuditLog (by entity_type/entity_id)
* ── OutboxEvent
```

Indexes cover tenant+status / program / tier / outbox status. No cross-DB FKs.

## Enterprise Validation (Self-Heal)

| Gate | Result |
| --- | --- |
| Architecture / boundaries | Pass |
| Module ownership (not CRM) | Pass |
| Repository / service layering | Pass |
| Tenant isolation | Pass |
| Soft delete + unique reclaim | Pass (healed) |
| Outbox-ready events | Pass (healed) |
| Permission enforcement + tree inheritance | Pass (healed) |
| Security (auth deny / invalid tenant / prod guards) | Pass (healed) |
| Audit read API + JSON-safe changes | Pass (healed) |
| Balance forbid at HTTP | Pass (healed) |
| Null required-field updates | Pass (healed) |
| Performance indexes + default uniqueness | Pass (healed) |
| Documentation / ADR / registry | Pass |
| Automated tests | **52 passed** |

Audit report: [loyalty-phase-7-0-audit.md](loyalty-phase-7-0-audit.md)

## Tests Executed

| Suite | Result |
| --- | --- |
| Architecture / dependency / migration | Pass |
| Permissions / security / tenant isolation | Pass |
| Repository / API / business rules / audit / outbox | Pass |
| Performance indexes / docs | Pass |

Command: `cd backend/services/loyalty && pytest -q`

## Known Limitations

- Membership engine depth (lifecycle rules, eligibility) → Phase 7.1
- Immutable point ledger / earn-redeem / expiration → Phase 7.2
- Full reward redemption flows → Phase 7.3
- Campaign segments/triggers/rule engine → Phase 7.4
- Referral / wallet / gift card / analytics / public APIs → Phases 7.5–7.9
- Outbox worker / real message bus flush → platform bus maturity (rows are persisted PENDING)
- Soft-delete of program does not cascade-close children (explicit Phase 7.1+ policy)
- JWT↔tenant claim binding and Core entitlement feature checks → platform-wide follow-up

## Next Phase

Phase 7.1 — Membership Engine (**not started**)

## Completion Checklist

- [x] Code completed
- [x] Architecture / dependency / repository / migration / API / permission / security / tenant / docs / business-rule / performance tests
- [x] Documentation updated
- [x] Module registry updated
- [x] ADR-011 accepted
- [x] Enterprise validation + self-heal completed (including incomplete 7.1 residue cleanup)
- [x] No CRM ownership of Loyalty
- [x] No TODO placeholders in foundation
- [x] No Phase 7.1 APIs/migrations present

## Related Documents

- [Progress](progress.md)
- [Next Steps](next-steps.md)
- [Module Registry — loyalty](module-registry.md#loyalty)
- [Loyalty Phase Area](phases/Loyalty/README.md)
- [ADR-011](architecture/adr/ADR-011.md)
- [Audit Report](loyalty-phase-7-0-audit.md)
- [Service README](../backend/services/loyalty/README.md)
