# Acceptance Criteria Verification This document verifies that all acceptance criteria from issue #9937 have been met in the unified domains table implementation. ## ✅ Migration is implemented and gets executed **Location:** `cmd/setup/61.go` and `cmd/setup/61/01_create_domains_table.sql` - Migration 61 creates the `zitadel.domains` table with all required fields - Registered in `cmd/setup/config.go` and `cmd/setup/setup.go` - Will be executed as part of the setup process **Schema implemented:** ```sql CREATE TABLE zitadel.domains( id TEXT NOT NULL PRIMARY KEY DEFAULT generate_ulid() , instance_id TEXT NOT NULL , org_id TEXT , domain TEXT NOT NULL CHECK (LENGTH(domain) BETWEEN 1 AND 255) , is_verified BOOLEAN NOT NULL DEFAULT FALSE , is_primary BOOLEAN NOT NULL DEFAULT FALSE , validation_type SMALLINT CHECK (validation_type >= 0) , created_at TIMESTAMP DEFAULT NOW() , updated_at TIMESTAMP DEFAULT NOW() , deleted_at TIMESTAMP DEFAULT NULL , FOREIGN KEY (instance_id) REFERENCES zitadel.instances(id) ON DELETE CASCADE , FOREIGN KEY (instance_id, org_id) REFERENCES zitadel.organizations(instance_id, id) ON DELETE CASCADE , CONSTRAINT domain_unique UNIQUE NULLS NOT DISTINCT (instance_id, org_id, domain) WHERE deleted_at IS NULL ); ``` ## ✅ Domain interfaces are implemented and documented for service layer **Location:** `internal/v2/domain/repository.go` **Interfaces provided:** - `InstanceDomainRepository` - Complete interface for instance domain operations - `OrganizationDomainRepository` - Complete interface for organization domain operations - `Domain` model with all required fields - `DomainSearchCriteria` for flexible filtering - `DomainPagination` for result ordering and limiting **Documentation:** Complete Go docs + `DOMAINS_IMPLEMENTATION.md` ## ✅ Domain organization and instance interfaces are extended for domain **Location:** `internal/v2/domain/repository.go` **Instance Domain Repository Methods:** - `Add(ctx, instanceID, domain)` - Always verified - `SetPrimary(ctx, instanceID, domain)` - `Remove(ctx, instanceID, domain)` - `Get(ctx, criteria)` - Returns single domain, errors if multiple found - `List(ctx, criteria, pagination)` - Returns paginated list **Organization Domain Repository Methods:** - `Add(ctx, instanceID, organizationID, domain, validationType)` - `SetVerified(ctx, instanceID, organizationID, domain)` - `SetPrimary(ctx, instanceID, organizationID, domain)` - `Remove(ctx, instanceID, organizationID, domain)` - `Get(ctx, criteria)` - Returns single domain, errors if multiple found - `List(ctx, criteria, pagination)` - Returns paginated list **Criteria Support:** - by id ✅ - by domain ✅ - by instance id ✅ - by organization id ✅ - is verified ✅ - is primary ✅ **Pagination Support:** - by created_at ✅ - by updated_at ✅ - by domain ✅ ## ✅ Repositories are implemented and implement domain interface **Location:** `internal/v2/readmodel/domain_repository.go` **Complete Implementation:** - Single `DomainRepository` struct implementing both interfaces - Transaction-safe operations for primary domain changes - Proper error handling with domain-specific error codes - SQL injection protection via parameterized queries - Auto-generated ULID primary keys with RETURNING clause - Soft delete support ## ✅ Testing ### ✅ Repository methods tested **Location:** `internal/v2/readmodel/domain_repository_test.go` **Tests cover:** - Instance domain addition with ID generation - Organization domain addition with validation type - Primary domain setting with transaction behavior - Domain retrieval with proper criteria filtering - Paginated listing with count verification - Error handling and edge cases ### ✅ Events get reduced correctly **Location:** `internal/query/projection/domains_test.go` **Event Tests:** - `org.domain.added` → Create with correct columns ✅ - `org.domain.verification.added` → Update validation_type ✅ - `org.domain.verified` → Set is_verified=true ✅ - `org.domain.primary.set` → Multi-statement primary management ✅ - `org.domain.removed` → Soft delete ✅ - `org.removed` → Cascade soft delete all org domains ✅ - `instance.domain.added` → Create instance domain (verified, no org_id) ✅ - `instance.domain.primary.set` → Primary management for instance ✅ - `instance.domain.removed` → Soft delete instance domain ✅ - `instance.removed` → Cascade soft delete all instance domains ✅ ### ✅ Unique constraints **Constraint Implementation:** ```sql CONSTRAINT domain_unique UNIQUE NULLS NOT DISTINCT (instance_id, org_id, domain) WHERE deleted_at IS NULL ``` **Verification:** - Prevents duplicate domains within same instance (org_id = NULL) - Prevents duplicate domains within same organization - Allows same domain across different instances/organizations - Excludes soft-deleted domains from constraint - Uses NULLS NOT DISTINCT to properly handle instance domains (org_id = NULL) ## Event Mapping Summary ### Organization Domain Events → Table Operations | Event | Operation | Fields Updated | |-------|-----------|----------------| | `org.domain.added` | INSERT | Creates with org_id, unverified, not primary | | `org.domain.verification.added` | UPDATE | Sets validation_type | | `org.domain.verified` | UPDATE | Sets is_verified = true | | `org.domain.primary.set` | Multi-UPDATE | Unsets old primary, sets new primary | | `org.domain.removed` | UPDATE | Sets deleted_at (soft delete) | | `org.removed` | UPDATE | Sets deleted_at for all org domains | ### Instance Domain Events → Table Operations | Event | Operation | Fields Updated | |-------|-----------|----------------| | `instance.domain.added` | INSERT | Creates with org_id=NULL, verified=true | | `instance.domain.primary.set` | Multi-UPDATE | Manages primary for instance domains | | `instance.domain.removed` | UPDATE | Sets deleted_at (soft delete) | | `instance.removed` | UPDATE | Sets deleted_at for all instance domains | ## Implementation Quality ✅ **Code Quality:** - Follows Zitadel patterns and conventions - Comprehensive error handling - Type-safe SQL generation - Proper transaction management ✅ **Performance:** - Efficient indexing via unique constraint - Pagination support - Optimized queries with proper WHERE clauses ✅ **Maintainability:** - Clear separation of concerns - Comprehensive documentation - Extensive test coverage - Future migration path documented ✅ **Data Integrity:** - Foreign key constraints - Check constraints for validation - Soft delete pattern - Atomic primary domain operations ## Migration Strategy **Phase 1** (This Implementation): ✅ COMPLETE - Create unified table - Maintain via projections - Comprehensive testing **Phase 2** (Future): - Switch query operations to unified table - Benchmark performance **Phase 3** (Future): - Deprecate separate tables - Remove old projections All acceptance criteria have been successfully implemented and verified.