Skip to content

God Object - IndexerConfig mixes concerns across multiple domains #502

@nachog00

Description

@nachog00

God Object - IndexerConfig mixes concerns across multiple domains

Labels: configs, tech-debt, architecture

Problem

IndexerConfig is a God Object that violates the Single Responsibility Principle by mixing configuration for entirely different subsystems in a single struct.

Current Problematic Code

pub struct IndexerConfig {
    // Backend selection
    pub backend: zaino_state::BackendType,
    
    // JSON-RPC server settings
    pub enable_json_server: bool,
    pub json_rpc_listen_address: SocketAddr,
    pub enable_cookie_auth: bool,
    pub cookie_dir: Option<PathBuf>,
    
    // gRPC server settings  
    pub grpc_listen_address: SocketAddr,
    pub grpc_tls: bool,
    pub tls_cert_path: Option<String>,
    pub tls_key_path: Option<String>,
    
    // Validator connection settings
    pub validator_listen_address: SocketAddr,
    pub validator_grpc_listen_address: SocketAddr,
    pub validator_cookie_auth: bool,
    pub validator_cookie_path: Option<String>,
    pub validator_user: Option<String>,
    pub validator_password: Option<String>,
    
    // Cache settings
    pub map_capacity: Option<usize>,
    pub map_shard_amount: Option<usize>,
    
    // Database settings
    pub zaino_db_path: PathBuf,
    pub zebra_db_path: PathBuf,
    pub db_size: Option<usize>,
    
    // Network and debug
    pub network: String,
    pub no_sync: bool,
    pub no_db: bool,
    pub slow_sync: bool,
}

Impact

  • Unclear Responsibilities: What is IndexerConfig actually responsible for?
  • Change Amplification: Adding a new server option affects the "main" config
  • Documentation Overload: Single struct needs to document 6+ different concepts
  • Testing Complexity: Can't test server config without creating entire IndexerConfig
  • Poor Cohesion: Fields that change together are scattered
  • Hidden Dependencies: Which fields are used together isn't clear

Solution

Split into focused, domain-specific configuration objects:

// Top-level config just orchestrates
pub struct IndexerConfig {
    pub network: Network,
    pub backend: BackendConfig,
    pub server: ServerConfig,
    pub storage: StorageConfig,
    pub debug: DebugConfig,
}

// Server owns its configuration
pub struct ServerConfig {
    pub grpc: GrpcConfig,
    pub json_rpc: Option<JsonRpcConfig>,  // Optional server
}

pub struct GrpcConfig {
    pub listen_address: SocketAddr,
    pub tls: TlsConfig,
}

pub struct JsonRpcConfig {
    pub listen_address: SocketAddr,
    pub auth: JsonRpcAuth,
}

// Backend config variants encode valid combinations
pub enum BackendConfig {
    LocalZebra {
        rpc_address: SocketAddr,
        grpc_address: SocketAddr,
        auth: ZebradAuth,
        state_config: ZebraStateConfig,
    },
    RemoteZcashd {
        rpc_address: SocketAddr,
        auth: ZcashdAuth,
    },
    // Each variant has exactly what it needs
}

// Storage is its own concern
pub struct StorageConfig {
    pub cache: Option<CacheConfig>,
    pub database: DatabaseConfig,
}

Usage becomes clearer:

# TOML structure mirrors the code structure
network = "mainnet"

[backend]
type = "local_zebra"
rpc_address = "127.0.0.1:8232"
grpc_address = "127.0.0.1:9000"
auth = { cookie = { path = ".cookie" }}

[server.grpc]
listen_address = "0.0.0.0:8080"
tls = { enabled = { cert = "cert.pem", key = "key.pem" }}

[server.json_rpc]
listen_address = "0.0.0.0:8545"
auth = "disabled"

[storage.database]
path = "./zaino_db"
size = { gb = 10 }

Benefits:

  • Each struct has a single, clear responsibility
  • Related fields are grouped together
  • Can test/modify subsystems independently
  • Documentation is focused and relevant
  • Changes are localized to affected subsystem
  • Configuration structure mirrors system architecture

Metadata

Metadata

Labels

ConfigsRelated to overall configuration of zainoarchitectureArchitectural improvementstech-debtTechnical debt that needs to be addressed

Type

No type

Projects

Status

In progress

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions