SkillAgentSearch skills...

Borg

A powerful, coroutine-based dependency initialization orchestrator for Android applications. Borg ensures your components are initialized in the correct order, with maximum parallelization and bulletproof thread safety.

Install / Use

/learn @kibotu/Borg

README

Borg 🤖

Maven Central Version Android CI API API API Gradle Version Kotlin License

Resistance is futile - your components will be assimilated in perfect order.

🚀 About Borg is a high-performance Android initialization orchestrator that brings order to your app's startup chaos. It automatically manages complex dependency graphs, parallelizes independent initializations, and provides bulletproof thread safety - all with the elegance of Kotlin coroutines.

Check out the article on Medium

screenshot

Table of Contents 📑

Why Borg? 🤔

Modern Android apps face complex initialization challenges:

Common Problems

  • ❌ Race conditions in component initialization
  • ❌ Unclear dependency ordering
  • ❌ Blocking main thread during setup
  • ❌ Hard to test initialization logic
  • ❌ Difficult error recovery
  • ❌ Poor performance from sequential initialization

Borg's Solutions

  • ✅ Thread-safe, deterministic initialization
  • ✅ Automatic dependency resolution
  • ✅ Non-blocking coroutine-based setup
  • ✅ Easy to test with constructor injection
  • ✅ Structured error handling
  • ✅ Maximum parallel initialization

Key Features 🌟

  • Type-Safe Dependencies: Compile-time verification of dependency graph
  • Parallel Initialization: Automatic parallelization of independent components
  • Coroutine Support: Native suspend function support for async operations
  • Thread Safety: Bulletproof concurrency handling with deadlock prevention
  • Error Handling: Rich exception hierarchy with detailed context
  • Testing Support: Easy mocking and testing through constructor injection
  • Performance: Optimal initialization order with parallel execution
  • Flexibility: Generic context type for any initialization needs

Installation 📦

Maven Central

allprojects {
    repositories {
        mavenCentral()
    }
}

dependencies {
    implementation 'net.kibotu:Borg:{latest-version}'
}

JitPack (Alternative)

  1. Add JitPack repository:
// settings.gradle.kts
dependencyResolutionManagement {
    repositories {
        maven { url = uri("https://jitpack.io") }
    }
}
  1. Add the dependency:
// build.gradle.kts
dependencies {
    implementation("com.github.kibotu:Borg:latest-version")
}

Quick Start 🚀

1. Define Your Components

Create a drone for each component that needs initialization:

// 1. Simple configuration
class ConfigDrone : BorgDrone<AppConfig, Context> {
    override suspend fun assimilate(context: Context, borg: Borg<Context>) =
        AppConfig.load(context.assets.open("config.json"))
}

// 2. Database with config dependency
class DatabaseDrone : BorgDrone<AppDatabase, Context> {
    override fun requiredDrones() = listOf(ConfigDrone::class.java)
    
    override suspend fun assimilate(context: Context, borg: Borg<Context>): AppDatabase {
        val config = borg.requireAssimilated(ConfigDrone::class.java)
        return Room.databaseBuilder(context, AppDatabase::class.java, config.dbName)
            .build()
    }
}

// 3. Repository combining multiple dependencies
class RepositoryDrone : BorgDrone<Repository, Context> {
    override fun requiredDrones() = listOf(
        DatabaseDrone::class.java,
        ApiDrone::class.java
    )
    
    override suspend fun assimilate(context: Context, borg: Borg<Context>) = Repository(
        database = borg.requireAssimilated(DatabaseDrone::class.java),
        api = borg.requireAssimilated(ApiDrone::class.java)
    )
}

2. Initialize the Collective

class App : Application() {

    private val applicationScope = CoroutineScope(SupervisorJob() + Dispatchers.Main)
    
    override fun onCreate() {
        super.onCreate()
        
        lifecycleScope.launch {
            try {
                // Create and initialize the collective
                val borg = Borg(setOf(
                    ConfigDrone(),
                    DatabaseDrone(),
                    ApiDrone(),
                    RepositoryDrone()
                ))
                
                // Assimilate all components
                borg.assimilate(applicationContext)
                
                // Store initialized components
                appContainer.repository = borg.requireAssimilated(RepositoryDrone::class.java)
                
            } catch (e: BorgException) {
                handleInitializationError(e)
            }
        }
    }
}

Advanced Usage 🔧

Parallel Initialization

Borg automatically parallelizes initialization of independent components:

val drones = setOf(
    AnalyticsDrone(),     // No dependencies - Parallel
    ConfigDrone(),        // No dependencies - Parallel
    DatabaseDrone(),      // Depends on Config - Waits for Config
    ApiDrone()           // Depends on Config - Waits for Config
)

// Visualization of parallel execution:
// Time →
// Analytics   ▓▓▓▓▓▓▓
// Config      ▓▓▓▓
// Database         ▓▓▓▓ (starts after Config)
// Api              ▓▓▓▓ (starts after Config)

Handling Optional Dependencies

Use getAssimilated() for optional dependencies:

class AnalyticsDrone : BorgDrone<Analytics, Context> {
    override fun requiredDrones() = listOf(UserDrone::class.java)
    
    override suspend fun assimilate(context: Context, borg: Borg<Context>): Analytics {
        val analytics = FirebaseAnalytics.getInstance(context)
        
        // Optional user identification
        borg.getAssimilated(UserDrone::class.java)?.let { user ->
            analytics.setUserId(user.id)
        }
        
        return analytics
    }
}

Fallback Handling

Implement graceful degradation:

class RemoteConfigDrone : BorgDrone<Config, Context> {
    override suspend fun assimilate(context: Context, borg: Borg<Context>): Config {
        return try {
            // Try remote config first
            FirebaseRemoteConfig.getInstance()
                .fetchAndActivate()
                .await()
                .let { RemoteConfig() }
        } catch (e: Exception) {
            // Fall back to local config
            LocalConfig.fromAssets(context)
        }
    }
}

Enabling Logging 📝

To enable logging in Borg, you can use the enableLogging parameter in the Borg constructor. This will automatically set up logging for all Borg operations.

Example:

val borg = Borg(
    drones = setOf(
        ConfigDrone(),
        DatabaseDrone(),
        ApiDrone(),
        RepositoryDrone()
    ),
    enableLogging = true
)

By setting enableLogging to true, Borg will log important lifecycle events and errors, helping you to monitor and debug the initialization process effectively.

You can still configure the logging level and customize the logger as needed, but the enableLogging parameter provides a quick and easy way to get started with logging in Borg.

Best Practices 💡

1. Keep Drones Focused

// ❌ Bad: Too many responsibilities
class MonolithicDrone : BorgDrone<AppServices, Context> {
    override suspend fun assimilate(context: Context, borg: Borg<Context>): AppServices {
        val db = Room.databaseBuilder(/*...*/).build()
        val api = Retrofit.Builder().build()
        val analytics = FirebaseAnalytics.getInstance(context)
        return AppServices(db, api, analytics)
    }
}

// ✅ Good: Single responsibility
class DatabaseDrone : BorgDrone<AppDatabase, Context> {
    override suspend fun assimilate(context: Context, borg: Borg<Context>) =
        Room.databaseBuilder(context, AppDatabase::class.java, "app.db").build()
}

2. Handle Errors Gracefully

class ApiDrone : BorgDrone<ApiClient, Context> {
    override fun requiredDrones() = listOf(ConfigDrone::class.java)
    
    override suspend fun assimilate(context: Context, borg: Borg<Context>): ApiClient {
        try {
            val config = borg.requireAssimilated(ConfigDrone::class.java)
            
            // Validate configuration
            require(config.apiUrl.isNotBlank()) { "API URL is required" }
            
            return ApiClient(config.apiUrl)
                .also { client ->
                    // Verify connectivity
                    client.ping()
                        .await()
                        .also { response ->
                            require(response.isSuccessful) {
                                "API not reachable: ${response.code()}"
                            }
                   
View on GitHub
GitHub Stars13
CategoryDevelopment
Updated1mo ago
Forks0

Languages

Kotlin

Security Score

95/100

Audited on Feb 20, 2026

No findings