# 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)