Files
bakery-ia/docs/TENANT_DELETION_IMPLEMENTATION_GUIDE.md
2025-11-01 21:35:03 +01:00

13 KiB

Tenant Deletion Implementation Guide

Overview

This guide documents the standardized approach for implementing tenant data deletion across all microservices in the Bakery-IA platform.

Architecture

Phase 1: Tenant Service Core ( COMPLETED)

The tenant service now provides three critical endpoints:

  1. DELETE /api/v1/tenants/{tenant_id} - Delete a tenant and all associated data

    • Verifies caller permissions (owner/admin or internal service)
    • Checks for other admins before allowing deletion
    • Cascades deletion to local tenant data (members, subscriptions)
    • Publishes tenant.deleted event for other services
  2. DELETE /api/v1/tenants/user/{user_id}/memberships - Delete all memberships for a user

    • Only accessible by internal services
    • Removes user from all tenant memberships
    • Used during user account deletion
  3. POST /api/v1/tenants/{tenant_id}/transfer-ownership - Transfer tenant ownership

    • Atomic operation to change owner and update member roles
    • Requires current owner permission or internal service call
  4. GET /api/v1/tenants/{tenant_id}/admins - Get all tenant admins

    • Returns list of users with owner/admin roles
    • Used by auth service to check before tenant deletion

Phase 2: Service-Level Deletion (IN PROGRESS)

Each microservice must implement tenant data deletion using the standardized pattern.

Implementation Pattern

Step 1: Create Deletion Service

Each service should create a tenant_deletion_service.py that implements BaseTenantDataDeletionService:

# services/{service}/app/services/tenant_deletion_service.py

from typing import Dict
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, delete, func
import structlog

from shared.services.tenant_deletion import (
    BaseTenantDataDeletionService,
    TenantDataDeletionResult
)

class {Service}TenantDeletionService(BaseTenantDataDeletionService):
    """Service for deleting all {service}-related data for a tenant"""

    def __init__(self, db_session: AsyncSession):
        super().__init__("{service}-service")
        self.db = db_session

    async def get_tenant_data_preview(self, tenant_id: str) -> Dict[str, int]:
        """Get counts of what would be deleted"""
        preview = {}

        # Count each entity type
        # Example:
        # count = await self.db.scalar(
        #     select(func.count(Model.id)).where(Model.tenant_id == tenant_id)
        # )
        # preview["model_name"] = count or 0

        return preview

    async def delete_tenant_data(self, tenant_id: str) -> TenantDataDeletionResult:
        """Delete all data for a tenant"""
        result = TenantDataDeletionResult(tenant_id, self.service_name)

        try:
            # Delete each entity type
            # 1. Delete child records first (respect foreign keys)
            # 2. Then delete parent records
            # 3. Use try-except for each delete operation

            # Example:
            # try:
            #     delete_stmt = delete(Model).where(Model.tenant_id == tenant_id)
            #     result_proxy = await self.db.execute(delete_stmt)
            #     result.add_deleted_items("model_name", result_proxy.rowcount)
            # except Exception as e:
            #     result.add_error(f"Model deletion: {str(e)}")

            await self.db.commit()

        except Exception as e:
            await self.db.rollback()
            result.add_error(f"Fatal error: {str(e)}")

        return result

Step 2: Add API Endpoints

Add two endpoints to the service's API router:

# services/{service}/app/api/{main_router}.py

@router.delete("/tenant/{tenant_id}")
async def delete_tenant_data(
    tenant_id: str,
    current_user: dict = Depends(get_current_user_dep),
    db = Depends(get_db)
):
    """Delete all {service} data for a tenant (internal only)"""

    # Only allow internal service calls
    if current_user.get("type") != "service":
        raise HTTPException(status_code=403, detail="Internal services only")

    from app.services.tenant_deletion_service import {Service}TenantDeletionService

    deletion_service = {Service}TenantDeletionService(db)
    result = await deletion_service.safe_delete_tenant_data(tenant_id)

    return {
        "message": "Tenant data deletion completed",
        "summary": result.to_dict()
    }


@router.get("/tenant/{tenant_id}/deletion-preview")
async def preview_tenant_deletion(
    tenant_id: str,
    current_user: dict = Depends(get_current_user_dep),
    db = Depends(get_db)
):
    """Preview what would be deleted (dry-run)"""

    # Allow internal services and admins
    if not (current_user.get("type") == "service" or
            current_user.get("role") in ["owner", "admin"]):
        raise HTTPException(status_code=403, detail="Insufficient permissions")

    from app.services.tenant_deletion_service import {Service}TenantDeletionService

    deletion_service = {Service}TenantDeletionService(db)
    preview = await deletion_service.get_tenant_data_preview(tenant_id)

    return {
        "tenant_id": tenant_id,
        "service": "{service}-service",
        "data_counts": preview,
        "total_items": sum(preview.values())
    }

Services Requiring Implementation

Completed:

  1. Tenant Service - Core deletion logic, memberships, ownership transfer
  2. Orders Service - Example implementation complete

🔄 In Progress:

  1. Inventory Service - Template created, needs testing

Pending:

  1. Recipes Service

    • Models to delete: Recipe, RecipeIngredient, RecipeStep, RecipeNutrition
  2. Production Service

    • Models to delete: ProductionBatch, ProductionSchedule, ProductionPlan
  3. Sales Service

    • Models to delete: Sale, SaleItem, DailySales, SalesReport
  4. Suppliers Service

    • Models to delete: Supplier, SupplierProduct, PurchaseOrder, PurchaseOrderItem
  5. POS Service

    • Models to delete: POSConfiguration, POSTransaction, POSSession
  6. External Service

    • Models to delete: ExternalDataCache, APIKeyUsage
  7. Forecasting Service (Already has some deletion logic)

    • Models to delete: Forecast, PredictionBatch, ModelArtifact
  8. Training Service (Already has some deletion logic)

    • Models to delete: TrainingJob, TrainedModel, ModelMetrics
  9. Notification Service (Already has some deletion logic)

    • Models to delete: Notification, NotificationPreference, NotificationLog
  10. Alert Processor Service

    • Models to delete: Alert, AlertRule, AlertHistory
  11. Demo Session Service

    • May not need tenant deletion (demo data is transient)

Phase 3: Orchestration & Saga Pattern (PENDING)

Goal

Create a centralized deletion orchestrator in the auth service that:

  1. Coordinates deletion across all services
  2. Implements saga pattern for distributed transactions
  3. Provides rollback/compensation logic for failures
  4. Tracks deletion job status

Components Needed

1. Deletion Orchestrator Service

# services/auth/app/services/deletion_orchestrator.py

class DeletionOrchestrator:
    """Coordinates tenant deletion across all services"""

    def __init__(self):
        self.service_registry = {
            "orders": OrdersServiceClient(),
            "inventory": InventoryServiceClient(),
            "recipes": RecipesServiceClient(),
            # ... etc
        }

    async def orchestrate_tenant_deletion(
        self,
        tenant_id: str,
        deletion_job_id: str
    ) -> DeletionResult:
        """
        Execute deletion saga across all services
        Returns comprehensive result with per-service status
        """
        pass

2. Deletion Job Status Tracking

CREATE TABLE deletion_jobs (
    id UUID PRIMARY KEY,
    tenant_id UUID NOT NULL,
    initiated_by UUID NOT NULL,
    status VARCHAR(50), -- pending, in_progress, completed, failed, rolled_back
    services_completed JSONB,
    services_failed JSONB,
    total_items_deleted INTEGER,
    error_log TEXT,
    created_at TIMESTAMP,
    completed_at TIMESTAMP
);

3. Service Registry

Track all services that need to be called for deletion:

SERVICE_DELETION_ENDPOINTS = {
    "orders": "http://orders-service:8000/api/v1/orders/tenant/{tenant_id}",
    "inventory": "http://inventory-service:8000/api/v1/inventory/tenant/{tenant_id}",
    "recipes": "http://recipes-service:8000/api/v1/recipes/tenant/{tenant_id}",
    "production": "http://production-service:8000/api/v1/production/tenant/{tenant_id}",
    "sales": "http://sales-service:8000/api/v1/sales/tenant/{tenant_id}",
    "suppliers": "http://suppliers-service:8000/api/v1/suppliers/tenant/{tenant_id}",
    "pos": "http://pos-service:8000/api/v1/pos/tenant/{tenant_id}",
    "external": "http://external-service:8000/api/v1/external/tenant/{tenant_id}",
    "forecasting": "http://forecasting-service:8000/api/v1/forecasts/tenant/{tenant_id}",
    "training": "http://training-service:8000/api/v1/models/tenant/{tenant_id}",
    "notification": "http://notification-service:8000/api/v1/notifications/tenant/{tenant_id}",
}

Phase 4: Enhanced Features (PENDING)

1. Soft Delete with Retention Period

  • Add deleted_at timestamp to tenants table
  • Implement 30-day retention before permanent deletion
  • Allow restoration during retention period

2. Audit Logging

  • Log all deletion operations with details
  • Track who initiated deletion and when
  • Store deletion summaries for compliance

3. Deletion Preview for All Services

  • Aggregate preview from all services
  • Show comprehensive impact analysis
  • Allow download of deletion report

4. Async Job Status Check

  • Add endpoint to check deletion job progress
  • WebSocket support for real-time updates
  • Email notification on completion

Testing Strategy

Unit Tests

  • Test each service's deletion service independently
  • Mock database operations
  • Verify correct SQL generation

Integration Tests

  • Test deletion across multiple services
  • Verify CASCADE deletes work correctly
  • Test rollback scenarios

End-to-End Tests

  • Full tenant deletion from API call to completion
  • Verify all data is actually deleted
  • Test with production-like data volumes

Rollout Plan

  1. Week 1: Complete Phase 2 for critical services (Orders, Inventory, Recipes, Production)
  2. Week 2: Complete Phase 2 for remaining services
  3. Week 3: Implement Phase 3 (Orchestration & Saga)
  4. Week 4: Implement Phase 4 (Enhanced Features)
  5. Week 5: Testing & Documentation
  6. Week 6: Production deployment with monitoring

Monitoring & Alerts

Metrics to Track

  • tenant_deletion_duration_seconds - How long deletions take
  • tenant_deletion_items_deleted - Number of items deleted per service
  • tenant_deletion_errors_total - Count of deletion failures
  • tenant_deletion_jobs_status - Current status of deletion jobs

Alerts

  • Alert if deletion takes longer than 5 minutes
  • Alert if any service fails to delete data
  • Alert if CASCADE deletes don't work as expected

Security Considerations

  1. Authorization: Only owners, admins, or internal services can delete
  2. Audit Trail: All deletions must be logged
  3. No Direct DB Access: All deletions through API endpoints
  4. Rate Limiting: Prevent abuse of deletion endpoints
  5. Confirmation Required: User must confirm before deletion
  6. GDPR Compliance: Support right to be forgotten

Current Status Summary

Phase Status Completion
Phase 1: Tenant Service Core Complete 100%
Phase 2: Service Deletions 🔄 In Progress 20% (2/10 services)
Phase 3: Orchestration Pending 0%
Phase 4: Enhanced Features Pending 0%

Next Steps

  1. Immediate: Complete Phase 2 for remaining 8 services using the template above
  2. Short-term: Implement orchestration layer in auth service
  3. Mid-term: Add saga pattern and rollback logic
  4. Long-term: Implement soft delete and enhanced features

Files Created/Modified

New Files:

  • /services/shared/services/tenant_deletion.py - Base classes and utilities
  • /services/orders/app/services/tenant_deletion_service.py - Orders implementation
  • /services/inventory/app/services/tenant_deletion_service.py - Inventory template
  • /TENANT_DELETION_IMPLEMENTATION_GUIDE.md - This document

Modified Files:

  • /services/tenant/app/services/tenant_service.py - Added deletion methods
  • /services/tenant/app/services/messaging.py - Added deletion event
  • /services/tenant/app/api/tenants.py - Added DELETE endpoint
  • /services/tenant/app/api/tenant_members.py - Added membership deletion & transfer endpoints
  • /services/orders/app/api/orders.py - Added tenant deletion endpoints

References