First Shadowfax 'Test Drive'
In the last 2 weeks I worked long hours in order to get the main subsystems migrated and a full prototype solution for testing the basic functionality. A brief overview of this work is expressed in the following lines. The overall experience was very pleasant and the final result was a cleaner and robust architecture then the original version.
The Project
The MBI architecture is currently installed on a large list of big to mid-sized clients along several Latin American countries and some strong constrains are paramount to respect in order to get a successful project. Here we have the main requirements for the new MBI version (3.0) called internally, “MBI powered by Shadowfax”.
-
All installed custom code (business actions, custom
extensions, etc.) must be run without modifications or
recompilation (just version binding redirection).
-
All public interfaces in MBI will be supported and some
classes might be marked as “Obsolete” providing new
functions that will use the Shadowfax infrastructure.
-
All main features of the current version of MBI shall
be preserved and the new version will be enhanced with
all the known features of WS and IIS, as well as all the
features and goodies provided by Shadowfax.
With these main constraints at hand, we start building a prototype that contains the main base line architecture subsystems and as seen on the following diagram, we use extensive use of the double pipeline pattern. The picture shows one main Interface Pipeline for the security and exception handling functions, and three Implementation Pipelines for the different kind of Business Actions.
First of all, we choose the WS-InProc communications transports (WS for the Interface Pipeline and InProc for the Implementation Pipeline) with a subtle variation of the InProc Target. The new Target called “InProcAppDomain” will handle the communication with the Implementation Pipeline that might be “InProc” or “OutProc” via a Web Service Target or whatever dispatching transport we choose and this will depend on what application belongs the Action that we want to execute.
On the Interface Pipeline, we included the Security Handlers (Authentication and Authorization) as well as the handler that creates the MBI Command object that all the upstream subsystems will use in the call chain up to the Action (Business Action in Shadowfax jargon) execution. In front of these handlers we put one that will translate the old MBI exceptions into Shadowfax exceptions so we can receive on the client side the packed MBI exception and re throw it in order to comprise with the fist premise of backward compatibility state above.
On the Implementation Pipeline we put all the handlers that will translate and managed the MBI services and finally we implemented a new Business Action Target now called ActionTarget that will process the Action (aka Business Action) and pack the Response object into the Shadowfax payload.
With this nutshell overview, we’ll see the pros and cons that we found in the Shadowfax Architecture when building this new model.
The Goodies
-
Double Pipeline Pattern: We have done an extensive use of the pipeline
extensibility model by refactoring many subsystems in
Handlers and Targets as well. This promotes a very clean
and very easy to extend model so we could achieve the
majority of the backward compatibility tenets by just
wrapping much of the functionality with the
Handler-Target combination.
-
Instrumentation Events: We made use of al the EIF infrastructure built inside
Shadowfax and hence we got all the out-of the box
functionality provided. However, we have some nice to
have issues listed in the next section.
-
FrameworkHelpers: This interface factory pattern proves to be very
useful for all “helpers” like services that delivers
Shadowfax as well as MBI, like Tracing, DB Connections,
Date Services, Application Logging, etc.
The Nice-to-have
-
Handlers Inheritance: Unfortunately when the “sealed” modifier was removed
from all Handlers (and every class as well) the private
constructor left on many classes and the lack of
virtual/protected members set mostly useless the
possibility of inherit from the base out-of-the box
provided handlers. Some refactoring here will quickly
fix this issue.
-
One Time Call: On the StatefulAround Handler, there is an implicit
rule that denies the possibility to call twice the same
“nextHandler()” delegate. This rules was introduced to
promote good programming practices (at least in this
context usage) and assure that the call chain will never
be called more then once, hence disabling any strange
side effect that could arise. However, this behavior
limits the flexibility that a custom code might need,
for example, if you catch some kind of exception and you
want to process the same chain but with some changes on
the message. Actually you can solve this, locating the
reprocessing code inside the Target (assuming you have a
custom Target). In some scenarios, this solution might
be suboptimal because you might be loosing some of the
isolation provided by the Handler for that particular
case. Perhaps with a careful refactoring of your code
you might achieve the same result with a more “academic”
solution (or more complex one) but you will have to take
care of the costs related to this.
-
WS Interface Adapter: The Web Service Interface Adapter is implemented as a
SoapExtension class. This is a very handy strategy
because you only have to add the
“WebServiceInterfaceAdapter” attribute to your WebMethod
but you can’t control the Pipeline firing or the Soap
Headers management process because all of this is done
inside this class. If you want to customize all this
behavior you should write a new SoapExtension class that
mimics the adapter functionality. You might inherit from
the provided adapter but this might not give you the
expected results. Again, a refactoring work on this area
should provide enhanced extensibility and customization.
-
RequestHeader and ResponseHeader: These objects carry the outbound message information
that will be use by some handlers or by the pipeline
itself. Basically this classes that inherits from the
“SoapHeader” class, pack this info in a jagged object
array of key/value pairs. Besides the lack of a typed
interface (ok, this has the flexibility of accept any
object) you have to pay the price of the boxing issue
for Value Types. However if you just pass your own
SoapHeader implementation, the Header processing inside
the WebServiceInterfaceAdapter will correctly add your
header to the Context object so you will be able to use
it whenever you have access to the context instance
(almost everywhere in Shadowfax).
-
Exceptions sent to the Client: There are is only one kind of exception that is
returned to the client (with the WS Interface
Transport): The SoapException (SE). With the help of the
Exception Code provided by the SE, you could know if
this exception originates from a Server technical
exception or from a BusinessRule informative exception.
In neither case you will be able to reconstruct in the
client side the original exception except from the
message property. It’s a good practice not to reveal any
technical detail about the exception down to the client
(security vulnerabilities and usability issues) and
translate it to a more user friendly and useful message.
But in some scenarios (like the one we have here), we
want to preserve the original exception thrown on the
Business Action and re throw it on the client. The
current version does not provide any mechanism to do
this (only sent the original message description) so we
have to do some kind of a hack to solve this. We
serialized and base64 encoded the original exception
inside a BusinessRuleException message and we reverted
the process on the client side, so we achieved the
desired behavior. It might be interesting to provide the
full BusinessRuleException transporting (Xml Serializing
the BRE instance) so the client will receive this Xml
serialized exception and use it to rebuild the original
custom exception.
-
Custom Event Source: The EventPublisher class might provide an overload
that let pass the event source as an argument or better
yet use an abstract class so an application might
override its behavior by changing the event source name.
Another approach might be to take the event source name
from configuration (EIF config or Shadowfax config file)
instead of taking it from the resource file.
Note: This wish list is actually under revision in the current V2 of Shadowfax that has started a couple of weeks ago.
In this first review of the Shadowfax Architecture under certain constrains stated by the main goals of the MBI migration project, I showed how well (and how much of nice to have) adapts Shadowfax to an existing base line architecture that must preserve the existing client code base but at the same time make use of many of the features and enhancements that Shadowfax provides.
The opinions expressed herein are personal and do not necessarily reflect the position of neither my employee nor Microsoft.