Last month, we released the .NET Framework 2.0. However, a significant number of organizations are not going to immediately migrate to the new platform and tools. While it's not that difficult to see the benefits of using the new platform, most organizations are very careful and deliberate about changing their platform. That leaves these organizations in a situation where they can clearly know what is coming, yet they do not yet have access to these capabilities. There is an external evolutionary event that impacts their development strategies, and the wise thing to do is to incorporate this visibility into their current approach to developing software
So, what do you do when you live in a .NET 1.1 world, but you still have to plan for the coming .NET 2.0 world?
I am working with a very large customer who is facing this exact scenario. This customer is completely overhauling their identity management strategy. In particular, they have decided to implement an external identity management strategy. They have to strategize around things like federation and ADFS, without actually having a production implementation of this technology. Part of this strategy is to standardize where they are going to store the identities of external individuals and organizations (for, without federation, they still hold responsibility for managing the identity stores and claims of users accessing their systems).
In the past, the approach to managing these identities depended on the team. Some teams were storing information in a SQL Server database. Others were utilizing an external Active Directory forest. Each individual project team had to determine the strategy for their project, and then they were responsible for the implementation. This obviously has consequences. There are manageability issues: each application has its own provisioning procedure and tools. There are security issues: writing authentication code and defining an authentication store is hard. There are user experience issues: each user could have a completely different set of credentials for each application. There are productivity issues: each team has to incorporate time into their schedule to develop the authentication store and code. The list goes on and on. Rather than perpetuate this project-driven approach, it made sense to tackle the problem holistically. This customer decided to standardize on using an LDAP directory store for their external identities: ADAM (Active Directory in Application Mode).
Given a new infrastructure standard, the attention then goes to the application developers. Does every developer now need to be trained in using the System.DirectoryServices namespace? While there was some expertise in this before from the applications using the external Active Directory forest, this knowledge was neither complete nor widespread. Also, while we have reduced the project schedules already by the amount of time necessary to decide on and create an authentication store, wouldn't it be great if they could reduce the amount of time they invest in creating the authentication code? The "code reuse" light was flashing rather brightly here.
The approach I took here was to gradually build levels of abstraction. The first step was to translate from System.DirectoryServices into actionable items. So, the first level of abstraction spoke only in terms of concepts such as Authenticate or ChangePassword. This frees them from the semantics of calling into a NativeObject (because for whatever reason there is no Authenticate method in either ADSI or System.DirectoryServices.) This fees them from the semantics of invoking into ADSI. It made it easier. It reduced the amount of code needed in every application immediately by at least 2/3, and occasionally far more.
Yet, developers still would need to write quite a bit of code into each application - code that would doubtlessly be duplicated. So, I created a higher level of abstraction against the most common scenario: authentication. Every application needs to authenticate, so we can achieve some significant benefits from abstracting this away.
ASP.NET provides an excellent validation framework, and it made sense to me to leverage this. Rather than calling an Authenticate method in code that you write, you could drop a validator object onto your ASP.NET page which will call this Authenticate method for you. Now, you just need to determine if Page.IsValid - if not, then you failed authentication, and the validator will display your error message for you. Another useful abstraction.
Of course, validation still leaves some opportunities for improvement. Each developer still has to lay out their own page, include the validator, and then write the code to react to validation either failing or succeeding. Most login pages are going to look the same. Most of the time, the developers wanted the response to successful authentication to be the same - the standard FormsAuthentiation redirection. Why should everyone have to write this? They don't have to. We can build a third layer of abstraction on top of the validation framework: a custom web server control that contains a place to enter a user name and password, a submit button, and an authentication validator. Set up a configuration section to put the location of the ADAM store in web.config, and now you can drop the control onto your login page, set a few configuration variables, and suddenly you are implementing authentication that looks consistent across the enterprise, with a consistent authentication store, using 0 lines of code. Pretty neat, except...
It looks like I am about to build a Login control - something I get for free in the .NET Framework 2.0! Of course, I also don't really want to wait for the organization to deploy the 2.0 Framework, because they can realize benefit from such a control immediately! What can I do to future-proof anything I make today?
I intended to mimic all of the relevant APIs from the ASP.NET 2.0 Login control. Why? Because I knew that the organization would be moving to the 2.0 Framework at some point. It's OK for my control to become irrelevant, but I certainly don't want all of their applications to become irrelevant! If the API and behaviors are the same (and you really do need to spend a lot of time understanding the APIs to ensure that they are the same), the upgrade experience should simply be at the point of instantiation. Rather than instantiating a control of the class that I developed, they can instantiate a control of the class that the 2.0 Framework provides. Much less painful migration!
So, in this scenario, I was able to generate immediate business value, while still taking into account the evolution of the surrounding platform infrastructure. I simply had to rely on mimicry, a technique biological life has invented time and time again to increase the likelihood of survival.