Files
zitadel/DOMAINS_IMPLEMENTATION.md
2025-07-14 20:11:28 +00:00

4.4 KiB

Unified Domains Table Implementation

This implementation provides a unified domains table (zitadel.domains) that consolidates both organization and instance domains into a single table structure.

Architecture

The implementation follows Zitadel's established patterns:

Database Layer

  • Migration 61: Creates the unified domains table with proper constraints
  • Table Structure: Uses nullable org_id to distinguish between instance domains (NULL) and organization domains (NOT NULL)
  • Soft Deletes: Implements deleted_at for data preservation
  • Unique Constraints: Ensures domain uniqueness within instance/organization scope

Domain Layer

  • Interfaces: Clean separation between instance and organization domain operations
  • Models: Unified domain model with optional organization ID

Repository Layer

  • Implementation: Single repository handling both instance and organization domains
  • Transactions: Atomic operations for primary domain changes
  • Query Building: Type-safe SQL generation using squirrel

Projection Layer

  • Event Handling: Processes both org and instance domain events
  • Data Synchronization: Maintains consistency with event sourcing

Event Mapping

Organization Domain Events

  • org.domain.added → Creates domain record with org_id
  • org.domain.verification.added → Updates validation_type
  • org.domain.verified → Sets is_verified = true
  • org.domain.primary.set → Manages primary domain flags
  • org.domain.removed → Soft deletes domain
  • org.removed → Soft deletes all org domains

Instance Domain Events

  • instance.domain.added → Creates domain record with org_id = NULL, is_verified = true
  • instance.domain.primary.set → Manages primary domain flags
  • instance.domain.removed → Soft deletes domain
  • instance.removed → Soft deletes all instance domains

Usage Examples

Instance Domain Operations

// Add instance domain (always verified)
domain, err := repo.AddInstanceDomain(ctx, "instance-123", "api.example.com")

// Set primary instance domain
err := repo.SetInstanceDomainPrimary(ctx, "instance-123", "api.example.com")

// Remove instance domain
err := repo.RemoveInstanceDomain(ctx, "instance-123", "api.example.com")

// List instance domains
criteria := v2domain.DomainSearchCriteria{
    InstanceID: &instanceID,
}
pagination := v2domain.DomainPagination{
    Limit: 10,
    SortBy: v2domain.DomainSortFieldDomain,
    Order: database.SortOrderAsc,
}
list, err := repo.List(ctx, criteria, pagination)

Organization Domain Operations

// Add organization domain
domain, err := repo.AddOrganizationDomain(ctx, "instance-123", "org-456", "company.com", domain.OrgDomainValidationTypeHTTP)

// Verify organization domain
err := repo.SetOrganizationDomainVerified(ctx, "instance-123", "org-456", "company.com")

// Set primary organization domain
err := repo.SetOrganizationDomainPrimary(ctx, "instance-123", "org-456", "company.com")

// Find specific domain
criteria := v2domain.DomainSearchCriteria{
    Domain: &domainName,
    InstanceID: &instanceID,
}
domain, err := repo.Get(ctx, criteria)

Testing

The implementation includes comprehensive tests:

Repository Tests

  • CRUD operations for both instance and organization domains
  • Transaction behavior verification
  • Error handling and edge cases
  • SQL query parameter validation

Projection Tests

  • Event reduction logic for all supported events
  • Column and condition verification
  • Multi-statement handling for primary domain changes
  • Soft delete behavior

Migration Strategy

This table is designed to work alongside existing tables initially:

  1. Phase 1 (This PR): Create unified table and maintain via projections
  2. Phase 2 (Future): Migrate query logic to use unified table
  3. Phase 3 (Future): Deprecate separate org_domains2 and instance_domains tables

Performance Considerations

  • Indexing: The unique constraint provides efficient domain lookups
  • Queries: Nullable org_id allows efficient filtering between domain types
  • Pagination: Supports sorting by created_at, updated_at, and domain name
  • Soft Deletes: WHERE deleted_at IS NULL conditions optimize active domain queries

Validation

The table enforces:

  • Domain length between 1-255 characters
  • Non-negative validation_type values
  • Foreign key integrity to instances and organizations
  • Unique domain constraints per instance/organization scope
  • Automatic updated_at timestamp management