Some terms:
Services are discovered by reflecting over all assemblies in a services directory in the host bin directory. Any class implementing IDreamService is inspected to generate a service blueprint, which is derived from the attribute markup on the class and services. The Dreamhost stores blueprints by service identifier (a SID:// uri) for later instantiation.
Currently services are generally subclasses of the DreamService baseclass, providing the common features. A sample service would be defined like this:
[DreamService("MindTouch Dream Atom", "Copyright (c) 2006-2009 MindTouch, Inc.",
Info = "http://doc.opengarden.org/Dream/Reference/Services/Atom",
SID = new string[] { "sid://mindtouch.com/2007/06/dream/atom" }
)]
[DreamServiceConfig("default-ttl", "double?", "Default time in seconds for events to live (default: 3600 seconds).")]
[DreamServiceConfig("feed.uri", "uri", "Feed service to retrieve original feed from")]
public class AtomService : DreamService {
...
}
The DreamService attribute defines the SID, which is just a unique identifier for the service. Since inside the Dream environment services are only known by their SIDs it is also the decoupling mechanism, i.e. anyone could provide an alternative implementation by providing a services implementing the same blueprint and declaring the same SID. A service may have more than one SID corresponding to different versions or behaviors of the service.
DreamServiceConfig attributes are used to declare configuration dependencies, which may be simple values, but could also be a URI to another service feature that the this service depends on. All service-to-service interactions are HTTP requests using the Plug class, which simplifies creation of requests via a fluent interface, hooks into Dream's asynchronous call infrastructure (using a Yield pattern) and allows interception so that calls to local services (same process) never hit the wire, but stay in-process.
A service declares a number of endpoints, or Features like this:
[DreamFeature("GET:feed", "Retrieve the raw feed")]
[DreamFeatureParam("limit", "int?", "max items to fetch return")]
internal Yield ProxyFeed(DreamContext context, DreamMessage request, Result<DreamMessage> response) {
Result<DreamMessage> proxyResponse;
yield return proxyResponse = Plug.New(_feedUri).With("limit",context.GetParam("limit")).GetAsync();
response.Return(DreamMessage.Ok(proxyResponse.Value));
yield break;
}
DreamFeature declares the {Method}:{Path} of the endpoint, while DreamFeatureParam declare the expected arguments.
The various Dream* attributes are used to build the service's blueprint, or public contract.
Services are instantiated either via host configuration, or by explicit service creation calls against the host. Initialization requires a SID and an XML configuration document. The host will create an instance (currently requires a no argument constructor and uses Activator.CreateInstance()) and then initializes the services with configuration via a PUT against the @config feature (required on any IDreamService).
While we have requirements on an IDreamService to have certain DreamFeatures, the class could very well also serve other dependencies that the plug-in provider declared. We simply need an instance with the features declared in the blueprint available on the class. Theoretically we may even deprecate the IDreamService requirements for services in the future and rely purely on attribute markup to discover and define a class as service for Dream.
It would be beneficial if the discovery and assembly loading could be offloaded to MEF, so that the DreamHost can simply ask for all IDreamService types. It would be further beneficial is this discovery process inside of MEF could hook into the logic for building the blueprints and register these blueprints by SID, so that the host can simply ask for a list of all service blueprints.
The requirement for a no-argument constructor is simply because we are responsible for creating an instance and our initialization happens via feature calls (i.e. web requests). In other words we treat instances as uninitialized, blank services, that need to be provided the service state. In the future we may want to have alternative instantiations where we restore a service state to a previously captured state rather than initialize it.
Rather than be responsible for creation of the instance, the Host could use MEF to request an instance via a SID, allowing the service class to have its own set of dependencies that MEF satisfies before handing the instance over to us. Ideally, we'd provide both a SID and the endpoint URI the service will live at to identify the instance, sot that MEF knows when to return an existing vs. new instance.
| Images 0 | ||
|---|---|---|
| No images to display in the gallery. |
Copyright © 2011 MindTouch, Inc. Powered by