<warning>This is a long post that attempts to describe a positive experience leveraging multiple frameworks</warning>
TL;DR. Static Typing / IoC == AWESOME FLEXIBILITY
Recently while working at Dovetail I had the absolute pleasure to provide a solution to a customer request. We were adding SAML authentication to our product, and we wanted to provide an extensibility point to handle unknown users. By default the product would show a not authorized screen if we encountered a valid SAML assertion referencing an unknown user. This was pretty easy to setup, but as I said, we really wanted this to be extensible. What I wanted to support was redirecting the user to a different page, maybe one that a customer has specified which would give the user the information needed to request access to our application through the approved channels. That would have been pretty easy with just a configuration entry, but we decided that really wasn’t enough. We also wanted to be able to handle a request to make a user right there. Well, now we need the ability to execute some code, a configuration file isn’t going to handle that very well. Even if we kept the config file approach we would have to redirect the user to somewhere else in the app, and then figure out how to shuffle all of the required data around. It just didn’t feel like a solid solution.
Thankfully, FubuMVC, Bottles and StructureMap came to the rescue. FubuMVC gives us controllers that can easily take dependencies on services (nothing to fancy here, you can do this in ASP.NET MVC too). So our SAML Authentication controller (or action in Fubu land) takes in a service that acts as a policy (SuccessfulSamlAssertionPolicy) it takes a valid SAML assertion, and attempts to locate the user in the database, if it finds them, the policy logs them in, and then redirects the user to the home page. If we can’t find the user, then we invoke the UnknownUserPolicy (another service/policy thing which is injected in the SuccessfulSamlAssetionPolicy) the default implementation of this policy simply redirects them to an error page of ours, but since it’s a interface/class pair, we can easily swap that out. This is where IoC/DI/StructureMap make things really nice.
So, the above may seem a bit complex (I definitely didn’t explain it that well). So lets break it down and make it concrete. I have an interface that looks like this
public interface UnknownUserPolicy
//FubuContinuation is a way to redirect the user
FubuContinuation HandleUnknownUser(SamlAssertion assertion);
we ship with the following implementation
public class DefaultUnknownUserPolicy : UnknownUserPolicy
public FubuContinuation HandleUnknownUser(SamlAssertion assertion)
return FubuContinuation.RedirectTo<LoginController>(c => c.Login(null));
Super easy code, nothing too hard here. We wire it up in StructureMap using a convention, so there is really nothing else to show. Now, lets imagine we have a customer, that doesn’t want them redirected to the login page, they should instead go to: https://silly.corporate.subdomain.company.com/request_access.cfm . Using StructureMap, this is as easy as registering our policy later in the container (after the default registrations) by adding a line like this:
That will override the default policy and boom! We have just customized our application for this one customer. So that’s great and all – but I want that in only ONE deployed instance of the application. I do not want to modify the base product for this customer, and this is where Bottles comes to my rescue.
Bottles is a framework for assisting us in loading dynamic code in a controlled and predictable manner. Its built with an IoC container in mind, but is completely agnostic of them and FubuMVC entirely. When our application starts up, we go through all the usual stuff for a .Net / IoC project. We initialize logging, spin up our container, blah, blah, blah. The one new step that we add with Bottles though, is to run through every discovered Bottle, and look for ‘StructureMap Registries’ and load them into the container. Now, a bottle is pretty much a glorified zip file holding .Net assemblies — among other things. The bottles, being a separate file from the base product, can be used to modify our existing application. In this case, we have a Customer Bottle that does one thing. It executes that above piece of SM registration code to override the base UnknownUserPolicy. WIN.
So, by putting all of these various tools, patterns, and frameworks together, we were able to build a solution that allows for a lot of customizability, does NOT require us to change our base product (would have loved to have had this at the bank), and I think truly encapsulates the changes that customizations for a customer can bring into one easily deployable unit.
There are a LOT of things going on in this post, we have programming to interfaces, we have making small classes that do one thing well, we are using IoC, we are using a web framework that makes redirecting deep in the bowels of the app easy and testable, we have composition, dynamic code loading and a lot of ‘abstractions’ (Yup, learned about those at a young age ). Using all of these has allowed us to add a lot of seams to our application, and it’s this kind of power that keeps our application nimble and easily customizable for any customer.