davianspace_hosting

Enterprise-grade hosting framework for Dart. Unifies configuration, dependency injection, logging, and lifecycle management into a coherent application model — conceptually equivalent to Microsoft.Extensions.Hosting, expressed idiomatically in Dart.

Dart License: MIT pub package


Table of contents


Features

Feature Description
Unified startup model Single builder wires configuration, DI, and logging together
Hosted services Background workers that participate in the application lifecycle
Application lifetime Started / stopping / stopped event hooks with one-shot semantics
Graceful shutdown Handles SIGINT / SIGTERM with orderly teardown in reverse order
Environment-aware Development / Staging / Production detection and conditional setup
Platform-agnostic Works in CLI apps, servers, background workers, and scheduled jobs
Zero reflection No dart:mirrors, no code generation — full AOT and tree-shaking support
Error resilient Startup rollback, stop-error collection, lifecycle exception aggregation

Installation

Add to your pubspec.yaml:

dependencies:
  davianspace_hosting: ^1.0.3

Then run:

dart pub get

Quick start

import 'package:davianspace_hosting/davianspace_hosting.dart';

void main() async {
  final host = await createDefaultBuilder()
      .configureServices((context, services) {
        services.addHostedService(
          (sp) => MyBackgroundWorker(),
        );
      })
      .build();

  await host.run(); // Blocks until SIGINT/SIGTERM or programmatic shutdown
}

Ecosystem integration

davianspace_hosting is the orchestration layer that unifies the DavianSpace ecosystem:

Package Role
davianspace_configuration Hierarchical configuration (JSON, env vars, in-memory)
davianspace_dependencyinjection Service collection & provider (singleton, scoped, transient)
davianspace_logging Structured logging with providers and filtering
davianspace_options Options pattern for strongly-typed settings
davianspace_hosting Orchestrates all of the above
davianspace_http_resilience (optional) Retry, circuit breaker, timeout policies
davianspace_http_ratelimit (optional) HTTP rate limiting

Architecture

┌───────────────────────────────────────────────────────────┐
│                        Host.run()                         │
├───────────────────────────┬───────────────────────────────┤
│    ApplicationLifetime    │    HostedServiceExecutor       │
├───────────────────────────┴───────────────────────────────┤
│               ServiceProvider (DI Container)               │
├────────────────┬──────────────────┬───────────────────────┤
│  Configuration │   LoggerFactory  │       Options          │
└────────────────┴──────────────────┴───────────────────────┘

For a deep dive into internal design decisions, see doc/architecture.md.

Host

The central runtime that manages an application's lifecycle. All interactions flow through the Host interface:

abstract interface class Host {
  Configuration get configuration;
  ServiceProvider get services;
  LoggerFactory get loggerFactory;

  Future<void> start();
  Future<void> stop();
  Future<void> run();
  Future<void> dispose();
}

Lifecycle flow:

build() → start() → [running...] → stop() → dispose()
                      ↑                ↑
                      │                │
                  onStarted()    SIGINT/SIGTERM
                                 requestShutdown()

HostBuilder

Fluent builder for composing the host. All configure* methods are cumulative and chainable:

final host = await createDefaultBuilder()
    .configureConfiguration((ctx, config) {
      config.addInMemory({'App:Name': 'MyApp'});
    })
    .configureServices((ctx, services) {
      services.addSingleton<IOrderService, OrderService>();
    })
    .configureLogging((ctx, logging) {
      logging.addConsole().setMinimumLevel(LogLevel.debug);
    })
    .build();

Build pipeline order:

  1. Configuration callbacks → Configuration built
  2. Environment resolved → HostContext finalised
  3. Logging callbacks → LoggerFactory built
  4. Framework services registered → User service callbacks
  5. DI container built → Host returned

Default builder

createDefaultBuilder() provides sensible defaults suitable for most applications:

Phase Default behaviour
Configuration appsettings.jsonappsettings.{Env}.json → env vars → CLI args
Logging Console logger; debug in Development, info otherwise
Services Configuration, LoggerFactory, Logger, ApplicationLifetime, HostedServiceCollection, HostedServiceExecutor

Configuration layering (later sources override earlier):

appsettings.json
  → appsettings.Development.json
    → environment variables
      → --key=value arguments

Hosted services

Background tasks that participate in the host lifecycle. Implement HostedService and register with addHostedService:

final class PingService implements HostedService {
  Timer? _timer;

  @override
  Future<void> start() async {
    _timer = Timer.periodic(
      const Duration(seconds: 30),
      (_) => print('ping'),
    );
  }

  @override
  Future<void> stop() async {
    _timer?.cancel();
  }
}

// Registration:
builder.configureServices((ctx, services) {
  services.addHostedService((_) => PingService());
});

Service lifecycle guarantees:

  • Services start in registration order.
  • Services stop in reverse registration order.
  • If a service fails during startup, all previously started services are stopped (best-effort).
  • During shutdown, errors are collected — all services get a chance to stop.

Application lifetime

React to lifecycle events with one-shot callbacks:

final lifetime = host.services.getRequired<ApplicationLifetime>();

lifetime.onStarted(() => logger.info('Application started'));
lifetime.onStopping(() => logger.info('Graceful shutdown initiated'));
lifetime.onStopped(() => logger.info('Shutdown complete'));

Event semantics:

  • Callbacks fire at most once per event.
  • Late-registered callbacks on an already-fired event execute immediately.
  • Exceptions are collected and thrown as a single LifetimeEventException.

Environment detection

The environment controls conditional configuration, logging levels, and DI validation:

builder.configureServices((context, services) {
  if (context.isDevelopment) {
    services.addSingleton<ICache, InMemoryCache>();
  } else {
    services.addSingleton<ICache, RedisCache>();
  }
});

Resolution order:

Priority Source Example
1 DART_ENVIRONMENT env var DART_ENVIRONMENT=Development
2 Hosting:Environment config key Set in appsettings.json
3 Default Production

Well-known environments: Development, Staging, Production (via HostEnvironments constants).

Configuration

The default builder loads configuration from multiple sources with a layered override model:

final host = await createDefaultBuilder(args: args)
    .configureConfiguration((ctx, config) {
      // Add custom sources — these override defaults
      config.addInMemory({
        'Database:ConnectionString': 'Server=localhost;Database=mydb',
        'Cache:Enabled': 'true',
      });
    })
    .build();

// Access configuration anywhere via DI:
final connString = host.configuration['Database:ConnectionString'];

Logging

Logging is configured through the builder and uses the structured logging pipeline from davianspace_logging:

final host = await createDefaultBuilder()
    .configureLogging((ctx, logging) {
      logging
        .addConsole()
        .setMinimumLevel(
          ctx.isDevelopment ? LogLevel.debug : LogLevel.warning,
        );
    })
    .build();

// Create loggers via DI:
final logger = host.loggerFactory.createLogger('MyComponent');
logger.info('Application configured successfully');

Advanced usage

Multiple hosted services

Register multiple services — they start in order and stop in reverse:

builder.configureServices((ctx, services) {
  services
    ..addHostedService((sp) => DatabaseMigrationService(
          config: sp.getRequired<Configuration>(),
        ))
    ..addHostedService((sp) => CacheWarmupService(
          logger: sp.getRequired<LoggerFactory>().createLogger('CacheWarmup'),
        ))
    ..addHostedService((sp) => HealthCheckService());
});

Programmatic shutdown

Trigger shutdown from within your application code:

final class GracefulShutdownService implements HostedService {
  GracefulShutdownService(this._lifetime);

  final ApplicationLifetime _lifetime;

  @override
  Future<void> start() async {
    // Perform one-time work, then request shutdown
    await _performMigration();
    _lifetime.requestShutdown();
  }

  @override
  Future<void> stop() async {}

  Future<void> _performMigration() async {
    // Migration logic...
  }
}

Custom host builder

Build a host without the default configuration for full control:

final host = await HostBuilderImpl()
    .configureConfiguration((ctx, config) {
      config.addInMemory({'Hosting:Environment': 'Production'});
    })
    .configureLogging((ctx, logging) {
      logging.addConsole();
    })
    .configureServices((ctx, services) {
      services.addHostedService((_) => MyService());
    })
    .build();

await host.run();

Concurrent service execution

The HostedServiceExecutor supports concurrent start/stop for independent services:

// Register a concurrent executor manually:
builder.configureServices((ctx, services) {
  // Override the default sequential executor
  services.addSingletonFactory<HostedServiceExecutor>(
    (sp) => HostedServiceExecutor(
      logger: sp.getRequired<LoggerFactory>().createLogger('Executor'),
      concurrent: true,
    ),
  );
});

Error handling

The hosting framework provides structured error handling at every lifecycle stage:

Scenario Behaviour
Service fails to start Previously started services are rolled back (stopped); HostedServiceException thrown
Service fails to stop Error collected; remaining services still get a chance to stop; HostedServiceStopException thrown
Multiple lifetime callbacks fail Errors collected; single LifetimeEventException thrown
Dispose fails Error logged but not thrown (best-effort cleanup)

Testing

# Run the full test suite
dart test

# Run with verbose output
dart test --reporter expanded

# Run a specific test group
dart test --name "Host lifecycle"

The package includes 44 tests covering:

  • Host builder wiring and build pipeline
  • Host context and environment detection
  • Host lifecycle (start / stop / run / dispose)
  • Hosted service execution order (sequential and concurrent)
  • Application lifetime event hooks
  • Graceful shutdown (SIGINT/SIGTERM and programmatic)
  • Error resilience (startup rollback, stop error collection)
  • Async lock concurrency safety
  • Default builder configuration

API reference

Core types

Type Description
Host Abstract interface for the application runtime
HostBuilder Abstract interface for the fluent builder
HostContext Contextual data during builder callbacks
HostEnvironments Well-known environment name constants
HostedService Abstract interface for background services

Implementations

Type Description
HostImpl Default Host implementation with AsyncLock-protected lifecycle
HostBuilderImpl Default HostBuilder with 3-phase build pipeline
createDefaultBuilder() Factory function with sensible defaults

Lifecycle

Type Description
ApplicationLifetime Started/stopping/stopped event hooks with programmatic shutdown
LifetimeEvents One-shot callback list with error collection
LifetimeEventException Aggregated exception for failed callbacks

Services

Type Description
HostedServiceCollection Registry for hosted-service factories
HostedServiceExecutor Orchestrates start/stop (sequential or concurrent)
HostedServiceException Thrown when a service fails to start
HostedServiceStopException Thrown when services fail to stop
HostingServiceCollectionExtensions addHostedService() / addHostedServiceInstance()

Utilities

Type Description
AsyncLock Async mutex for preventing concurrent lifecycle transitions

Full API documentation is available via dart doc.

Contributing

See CONTRIBUTING.md for development setup, coding guidelines, and the pull request process.

Security

See SECURITY.md for the vulnerability reporting process and supported versions.

License

MIT — see LICENSE for details.

Libraries

davianspace_hosting
Enterprise-grade hosting framework for Dart.