service_scope library
This library enables one to create a service scope in which code can run.
A service scope is an environment in which code runs. The environment is a
Zone with added functionality. Code can be run inside a new service scope
by using the fork(callback)
method. This will call callback
inside a new
service scope and will keep the scope alive until the Future returned by the
callback completes. At this point the service scope ends.
Code running inside a new service scope can
- register objects (e.g. a database connection pool or a logging service)
- look up previously registered objects
- register on-scope-exit handlers
Service scopes can be nested. All registered values from the parent service
scope are still accessible as long as they have not been overridden. The
callback passed to fork()
is responsible for not completing it's returned
Future until all nested service scopes have ended.
The on-scope-exit callbacks will be called when the service scope ends. The
callbacks are run in reverse registration order and are guaranteed to be
executed. During a scope exit callback the active service scope cannot
be modified anymore and lookup()
s will only return values which were
registered before the registration of the on-scope-exit callback.
One use-case of this is making services available to a server application. The server application will run inside a service scope which will have all necessary services registered. Once the server app shuts down, the registered on-scope-exit callbacks will automatically be invoked and the process will shut down cleanly.
Here is an example use case:
import 'dart:async';
import 'package:gcloud/service_scope.dart' as scope;
class DBPool { ... }
DBPool get dbService => scope.lookup(#dbpool);
Future runApp() {
// The application can use the registered objects (here the
// dbService). It does not need to pass it around, but can use a
// global getter.
return dbService.query( ... ).listen(print).asFuture();
}
main() {
// Creates a new service scope and runs the given closure inside it.
ss.fork(() {
// We create a new database pool with a 10 active connections and
// add it to the current service scope with key `#dbpool`.
// In addition we insert a on-scope-exit callback which will be
// called once the application is done.
var pool = new DBPool(connections: 10);
scope.register(#dbpool, pool, onScopeExit: () => pool.close());
return runApp();
}).then((_) {
print('Server application shut down cleanly');
});
}
As an example, the package:appengine/appengine.dart
package runs request
handlers inside a service scope, which has most package:gcloud
services
registered.
The core application code can then be independent of package:appengine
and instead depend only on the services needed (e.g.
package:gcloud/storage.dart
) by using getters in the service library (e.g.
the storageService
) which are implemented with service scope lookups.
Functions
-
fork(
Future func(), {Function? onError}) → Future -
Start a new zone with a new service scope and run
func
inside it. -
lookup(
Object key) → Object? - Look up an item by it's key in the currently active service scope.
-
register(
Object key, Object value, {ScopeExitCallback? onScopeExit}) → void -
Register a new
object
into the current service scope using the givenkey
. -
registerScopeExitCallback(
ScopeExitCallback onScopeExitCallback) → void -
Register a
onScopeExitCallback
to be invoked when this service scope ends.
Typedefs
- ScopeExitCallback = FutureOr Function()