Published by Marco on 20. Nov 2015 16:57:58
------------------------------------------------------------------------

Quino has long included support for connecting to an application server instead
of connecting directly to databases or other sources. The application server
uses the same model as the client and provides modeled services
(application-specific) as well as CRUD for non-modeled data interactions.

We wrote the first version of the server in 2008. Since then, it's acquired
better authentication and authorization capabilities as well as routing and
state-handling. We've always based it on the .NET HttpListener.

[Old and Busted]

As late as Quino 2.0-beta2 (which we had deployed in production environments
already), the server hierarchy looked like screenshot below, pulled from issue
"QNO-4927" :

[image]

This screenshot was captured after a few unneeded interfaces had already been
removed. As you can see by the class names, we'd struggled heroically to deal
with the complexity that arises when you use inheritance rather than
composition.

The state-handling was welded onto an authentication-enabled server, and the
base machinery for supporting authentication was spread across three hierarchy
layers. The hierarchy only hints at composition in its naming: the "Stateful"
part of the class name CoreStatefulHttpServerBase had already been moved
to a state provider and a state creator in previous versions. That support is
unchanged in the 2.0 version.

[Implementation Layers]

We mentioned above that implementation was "spread across three hierarchy
layers". There's nothing wrong with that, in principle. In fact, it's a good
idea to encapsulate higher-level patterns in a layer that doesn't introduce too
many dependencies and to introduce dependencies in other layers. This allows
applications not only to be able to use a common implementation without pulling
in unwanted dependencies, but also to profit from the common tests that ensure
the components works as advertised.

In Quino, the following three layers are present in many components:

   1. Abstract: a basic encapsulation of a pattern with almost no dependencies
      (generally just Encodo.Core).
   2. Standard: a functional implementation of the abstract pattern with
      dependencies on non-metadata assemblies (e.g. Encodo.Application,
      Encodo.Connections and so on)
   3. Quino: an enhancement of the standard implementation that makes use of
      metadata to fill in implementation left abstract in the previous layer.
      Dependencies can include any of the Quino framework assemblies (e.g.
      Quino.Meta, Quino.Application and so on).


[The New Hotness [1]]

The diagram below shows the new hotness in Quino 2. [2]

[image]

The hierarchy is now extremely flat. There is an IServer interface and a Server
implementation, both generic in TListener, of type IServerListener. The server
manages a single instance of an IServerListener.

The listener, in turn, has an IHttpServerRequestHandler, the main implementation
of which uses an IHttpServerAuthenticator.

As mentioned above, the IServerStateProvider is included in this diagram, but is
unchanged from Quino 2.0-beta3, except that it is now used by the request
handler rather than directly by the server.

You can see how the abstract layer is enhanced by an HTTP-specific layer (the
Encodo.Server.Http namespace) and the metadata-specific layer is nice
encapsulated in three classes in the Quino.Server assembly.

[Server Components and Flow]

This type hierarchy has decoupled the main elements of the workflow of handling
requests for a server:

  * The server manages listeners (currently a single listener), created by a
    listener factory
  * The listener, in turn, dispatches requests to the request handler
  * The request handler uses the route handler to figure out where to direct the
    request
  * The route handler uses a registry to map requests to response items
  * The request handler asks the state provider for the state for the given
    request
  * The state provider checks its cache for the state (the default support uses
    persistent states to cache sessions for a limited time); if not found, it
    creates a new one
  * Finally, the request handler checks whether the user for the request is
    authenticated and/or authorized to execute the action and, if so, executes
    the response items

It is important to note that this behavior is unchanged from the previous
version -- it's just that now each step is encapsulated in its own component.
The components are small and easily replaced, with clear and concise interfaces.

Note also that the current implementation of the request handler is for HTTP
servers only. Should the need arise, however, it would be relatively easy to
abstract away the HttpListener dependency and generalize most of the logic in
the request handler for any kind of server, regardless of protocol and
networking implementation. Only the request handler is affected by the HTTP
dependency, though: authentication, state-provision and listener-management can
all be re-used as-is.

Also of note is that the only full-fledged implementation is for metadata-based
applications. At the bottom of the diagram, you can see the metadata-specific
implementations for the route registry, state provider and authenticator. This
is reflected in the standard registration in the IOC.

These are the service registrations from Encodo.Server:


return handler
  .RegisterSingle()
  .RegisterSingle,
HttpServerListenerFactory>()
  .Register>();

And these are the service registrations from Quino.Server:


handler
  .RegisterSingle,
StandardMetaServerRouteRegistry>()
  .RegisterSingle,
MetaPersistentServerStateProvider>()
  .RegisterSingle,
MetaServerStateCreator>()
  .RegisterSingle,
MetaHttpServerAuthenticator>()
  .RegisterSingle>()

As you can see, the registration is extremely fine-grained and allows very
precise customization as well as easy mocking and testing.

--------------------------------------------------------------------------------


[1] Any Men in Black fans out there? Tommy Lee Jones was "old and busted" while
    Will Smith was "the new hotness"? No? Just me? All righty then...


[1] This diagram brought to you by the diagramming and architecture tools in
    ReSharper 9.2. Just select the files or assemblies you want to diagram in
    the Solution Explorer and choose the option to show them in a diagram. You
    can right-click any type or assembly to show dependent or referenced modules
    or types. For type diagrams , you can easily control which relationships are
    to be shown (e.g. I hide aggregations to avoid clutter) and how the elements
    are to be grouped (e.g. I grouped by namespace to include the boxes in my
    diagram).