Skip to main content

What is a Domain?

A domain is a logical namespace that provides isolation and organizational boundaries in Cadence. All workflow executions, activities, and task lists belong to a specific domain.
Think of domains as separate workspaces - similar to Kubernetes namespaces or AWS accounts. Each domain has its own workflows, task lists, and configuration.

Why Domains Matter

Domains provide several critical capabilities:
  1. Isolation: Workflows in different domains are completely isolated
  2. Multi-tenancy: Support multiple teams or customers on the same cluster
  3. Environment Separation: Different domains for dev, staging, and production
  4. Security: Control access at the domain level
  5. Configuration: Different retention policies and settings per domain
  6. Failover: Cross-region replication for disaster recovery

Domain Structure

DomainInfo

type DomainInfo struct {
    Name        string            `json:"name,omitempty"`
    Status      *DomainStatus     `json:"status,omitempty"`
    Description string            `json:"description,omitempty"`
    OwnerEmail  string            `json:"ownerEmail,omitempty"`
    Data        map[string]string `json:"data,omitempty"`
    UUID        string            `json:"uuid,omitempty"`
}
  • Name: Unique domain identifier (e.g., “production-orders”)
  • Status: Domain state (REGISTERED, DEPRECATED, DELETED)
  • Description: Human-readable description
  • OwnerEmail: Contact information for domain owner
  • Data: Key-value metadata
  • UUID: System-generated unique identifier

DomainStatus

type DomainStatus int32

const (
    DomainStatusRegistered  DomainStatus = iota  // Active and accepting workflows
    DomainStatusDeprecated                       // No new workflows, existing continue
    DomainStatusDeleted                          // Marked for deletion
)

DomainConfiguration

type DomainConfiguration struct {
    WorkflowExecutionRetentionPeriodInDays int32
    EmitMetric                              bool
    HistoryArchivalStatus                   ArchivalStatus
    HistoryArchivalURI                      string
    VisibilityArchivalStatus                ArchivalStatus
    VisibilityArchivalURI                   string
    BadBinaries                             *BadBinaries
    IsolationGroups                         *IsolationGroupConfiguration
    AsyncWorkflowConfig                     *AsyncWorkflowConfiguration
}

How Domains Work Internally

Domain Registration

When you register a domain:
  1. Validation: Name is checked for uniqueness and format
  2. Persistence: Domain metadata is stored in the system
  3. Replication: Domain info is replicated to all clusters (if configured)
  4. Cache: Domain is loaded into the domain cache
  5. Ready: Domain is now available for workflow executions

Domain Cache

Cadence maintains an in-memory cache of domain metadata:
type DomainCacheInfo struct {
    ID                      string
    Name                    string
    Status                  DomainStatus
    Description             string
    OwnerEmail              string
    Data                    map[string]string
    RetentionDays           int32
    // ... other fields
}
The cache provides:
  • Fast domain lookups
  • Configuration access
  • Replication state

Cross-Region Replication

For global domains:
type DomainReplicationConfiguration struct {
    ActiveClusterName string
    Clusters          []*ClusterReplicationConfiguration
}

Code Examples

package main

import (
    "context"
    "go.uber.org/cadence/client"
)

func main() {
    // Create admin client
    domainClient, err := client.NewDomainClient(
        client.Options{HostPort: "localhost:7933"},
    )
    if err != nil {
        panic(err)
    }
    defer domainClient.Close()

    // Register a domain
    err = domainClient.Register(context.Background(), &client.RegisterDomainRequest{
        Name:        "production-orders",
        Description: "Production order processing workflows",
        OwnerEmail:  "[email protected]",
        WorkflowExecutionRetentionPeriodInDays: 7,
        EmitMetric: true,
        Data: map[string]string{
            "team":        "payments",
            "environment": "production",
        },
    })
    if err != nil {
        panic(err)
    }
}

Domain Configuration

Retention Policies

// Workflow execution retention period
type DomainConfiguration struct {
    WorkflowExecutionRetentionPeriodInDays int32
}
Retention Period determines how long closed workflow histories are kept:
  • Minimum: 1 day
  • Maximum: Configurable (typically 30 days)
  • After retention: History is archived or deleted

Archival Configuration

type ArchivalStatus int32

const (
    ArchivalStatusDisabled ArchivalStatus = iota
    ArchivalStatusEnabled
)
domainClient.Register(context.Background(), &client.RegisterDomainRequest{
    Name: "archived-domain",
    Configuration: &client.DomainConfiguration{
        HistoryArchivalStatus: client.ArchivalStatusEnabled,
        HistoryArchivalURI:    "s3://my-bucket/history-archive",
    },
})

Best Practices

1. Domain Naming Conventions

production-orders
staging-payments
dev-user-workflows
customer-acme-prod

2. Environment Separation

// Development domain
domainClient.Register(ctx, &client.RegisterDomainRequest{
    Name:        "dev-orders",
    Description: "Development environment",
    WorkflowExecutionRetentionPeriodInDays: 1,  // Short retention
    EmitMetric: false,  // Disable metrics
})

// Production domain
domainClient.Register(ctx, &client.RegisterDomainRequest{
    Name:        "prod-orders",
    Description: "Production environment",
    WorkflowExecutionRetentionPeriodInDays: 30,  // Long retention
    EmitMetric: true,  // Enable metrics
    HistoryArchivalStatus: client.ArchivalStatusEnabled,  // Archive histories
})

3. Multi-Tenancy

// Separate domain per customer
for _, customer := range customers {
    domainClient.Register(ctx, &client.RegisterDomainRequest{
        Name:        fmt.Sprintf("customer-%s", customer.ID),
        Description: fmt.Sprintf("Workflows for %s", customer.Name),
        OwnerEmail:  customer.Email,
        Data: map[string]string{
            "customer-id":   customer.ID,
            "customer-tier": customer.Tier,
        },
    })
}

4. Domain Metadata

// Use Data field for custom metadata
domainClient.Register(ctx, &client.RegisterDomainRequest{
    Name: "production-orders",
    Data: map[string]string{
        "team":         "payments",
        "cost-center":  "cc-1234",
        "environment":  "production",
        "region":       "us-west-2",
        "slack-channel": "#team-payments",
        "oncall":       "[email protected]",
    },
})

5. Global Domains for Failover

// Register global domain with replication
domainClient.Register(ctx, &client.RegisterDomainRequest{
    Name:          "global-orders",
    IsGlobalDomain: true,
    ActiveClusterName: "us-west-2",
    Clusters: []*client.ClusterReplicationConfiguration{
        {ClusterName: "us-west-2"},
        {ClusterName: "us-east-1"},
        {ClusterName: "eu-west-1"},
    },
})

// Failover to different cluster
domainClient.Update(ctx, &client.UpdateDomainRequest{
    Name: "global-orders",
    ReplicationConfiguration: &client.DomainReplicationConfiguration{
        ActiveClusterName: "eu-west-1",  // Failover to EU
    },
})

Domain Lifecycle

Active State

type DomainInfo struct {
    Status *DomainStatus  // DomainStatusRegistered
}
  • Accepts new workflow executions
  • All operations are allowed
  • Normal operation mode

Deprecated State

// Deprecate a domain
domainClient.Update(ctx, &client.UpdateDomainRequest{
    Name: "old-domain",
    UpdatedInfo: &client.UpdateDomainInfo{
        Status: client.DomainStatusDeprecated,
    },
})
  • No new workflow executions allowed
  • Existing workflows continue
  • Use for graceful domain shutdown

Deleted State

Domain Deletion is Permanent: Once deleted, all workflow histories and data are lost. Use deprecation instead for graceful shutdown.

Domain Errors

DomainNotActiveError

type DomainNotActiveError struct {
    Message        string
    DomainName     string
    CurrentCluster string
    ActiveCluster  string
    ActiveClusters []string
}
Thrown when:
  • Accessing a global domain from non-active cluster
  • Domain is in process of failing over
  • Requires redirect to active cluster

DomainAlreadyExistsError

type DomainAlreadyExistsError struct {
    Message string
}
Thrown when:
  • Attempting to register domain with existing name
  • Domain name conflicts with deleted domain

Domain Isolation Groups

Isolation groups allow routing workflows to specific worker pools within a domain:
type IsolationGroupConfiguration struct {
    IsolationGroups []*IsolationGroupPartition
}

type IsolationGroupPartition struct {
    Name  string
    State IsolationGroupState
}
Use cases:
  • GPU vs CPU workloads
  • High vs low priority
  • Different SLA tiers

Monitoring and Observability

Domain Metrics

domainClient.Register(ctx, &client.RegisterDomainRequest{
    Name:       "monitored-domain",
    EmitMetric: true,  // Enable metric emission
})
Key metrics:
  • cadence_workflow_started
  • cadence_workflow_completed
  • cadence_workflow_failed
  • cadence_workflow_timeout
  • cadence_activity_started

Domain Filter

Filter operations by domain:
type DomainFilter struct {
    DomainIDs    []string
    ReverseMatch bool
}
  • Workflows - Execute workflows within domains
  • Task Lists - Task lists are scoped to domains
  • Workers - Workers connect to specific domains
  • Archival - Configure domain-level archival

Further Reading

Build docs developers (and LLMs) love