davianspace_hosting 1.0.3 copy "davianspace_hosting: ^1.0.3" to clipboard
davianspace_hosting: ^1.0.3 copied to clipboard

Enterprise-grade hosting framework for Dart. Unifies configuration, logging, dependency injection, and lifecycle management into a coherent application model.

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.

0
likes
160
points
94
downloads

Documentation

API reference

Publisher

verified publisherdavian.space

Weekly Downloads

Enterprise-grade hosting framework for Dart. Unifies configuration, logging, dependency injection, and lifecycle management into a coherent application model.

Repository (GitHub)
View/report issues
Contributing

Topics

#hosting #dependency-injection #configuration #architecture #lifecycle

License

MIT (license)

Dependencies

davianspace_configuration, davianspace_dependencyinjection, davianspace_logging, davianspace_options

More

Packages that depend on davianspace_hosting