Back in January, Jeremy Miller posted a nice article on HtmlTags: Shrink your Views with FubuMVC Html Conventions. We were immediately in love with the idea and have spent several months adapting the conventions to work with our ASP.Net MVC applications. I was having a conversation with Ryan recently, reflecting on how far we’ve come and how we had no vision of that when we first read that article. I want to share some of that, so I will be working on a series of blog posts to show “What we are doing with HtmlTags.” But first: why?
A lot of buzz has been generated around reusability and views, particularly html. Html being a highly compositional language, but lacking any ability for reuse, has led to copy paste hell. Several different philosophies are employed in View Engines (VE) to try to address this issue and recently MVC2 was released with it’s templated helpers. The problem with reusing html directly is that lacks the benefits of separating the concern of what we need from the html (building it) and generating it, classically, the difference between “What” I want versus “How” I get it. The power of the Linq abstraction shows the benefits of separating “What” versus “How.” HtmlTags is another model based approach that separates the “What” and “How.” It goes even further by allowing the “How” to be configured via conventions. Here is a list of the benefits this separation has brought to our team:
The next set of posts will cover examples of how we are using HtmlTags and how it’s paying dividends.
-Wes
After years of dabbling with different version control systems and techniques, I wanted to share some of what I like and dislike in a few blog posts. To start this out, I want to talk about how I use VCS in a team environment. These come in a series of tips or best practices that I try to follow.
Note: This list is subject to change in the future.
Note: I edited this to make it more friendly, I was too opinionated when I first wrote this.
There is one area I haven’t found a solution I like yet: versioning 3rd party libraries and/or code. I really dislike keeping any assemblies in the repository, but seems to be a common practice for external libraries. Please feel free to share your ideas about this below.
-Wes
Pick a small project with a small(ish) team. This can be a legacy application or a green-field application. Strive to find a team of early adopters that will be eager to try something new. Get the team on board!
Research the tool(s) that you want to use. Some tools provide all of the features you would need while some only provide a slice of the pie. DBVCS requires the ability to manage a set of change scripts that update a database from one version to the next. Ideally a tool can track database versions and automatically apply updates. The change script generation process can be manual, but having diff tools available to automatically generate it can really reduce the overhead to adoption. Finally, an automated tool to generate a script file per database object is an added bonus as your version control system can quickly identify what was changed in a commit (add/del/modify), just like with code changes.
Don’t settle on just one tool, identify several. Then work with the team to evaluate the tools. Have the team do some tests of the following scenarios with each tool:
Let the team give feedback and decide together, what tool they would like to try out.
My recommendation at this point would be to include TSqlMigrations and RoundHouse as SQL based migration platforms. In general I would recommend staying away from the fluent platforms as they often lack baseline capabilities and add overhead to learn a new API when SQL is already a very well known DSL. Code migrations often get messy with procedures/views/functions as these have to be created with SQL and aren’t cross platform anyways. IMO stick to SQL based migrations.
If your project is a legacy application, you will need to reconcile the current state of production with your development databases. Find changes in production and bring them down to development, even if they are old and need to be removed. Once complete, produce a baseline of either dev or prod as they are now in sync. Commit this to your VCS of choice.
Add whatever schema changes tracking mechanism your tool requires to your development database. This often requires adding a table to track the schema version of that database. Your tool should support doing this for you. You can add this table to production when you do your next release.
Script out any changes currently in dev. Remove production artifacts that you brought down during reconciliation. Add change scripts for any outstanding changes in dev since the last production release. Commit these to your repository.
Simply put, you wouldn’t dream of sharing a code checkout, why would you share a development database? If you have a shared dev database, back it up, distribute the backups and take the shared version offline (including the dev db server once all projects are using DB VCS). Doing DB VCS with a shared database is bound to cause problems as people won’t be able to easily script out their own changes from those that others are working on.
Copy prod to your beta/testing environment. Add the schema changes table (or mechanism) and do a test run of your changes. If successful you can schedule this to be run on production.
After your first release, evaluate the pain points of the process. Try to find tools or modifications to existing tools to help fix them. Don’t leave stones unturned, iteratively evolve your tools and practices to make the process as seamless as possible. This is why I suggest open source alternatives. Nothing is set in stone, a good example was adding transactional support to TSqlMigrations. We ran into situations where an update would break a database, so I added a feature to do transactional updates and rollback on errors!
Another good example is generating change scripts. We have been manually making these for months now. I found an open source project called Open DB Diff and integrated this with TSqlMigrations. These were things we just accepted at the time when we began adopting our tool set. Once we became comfortable with the base functionality, it was time to start automating more of the process. Just like anything else with development, never be afraid to try to find tools to make your job easier!
Enjoy
-Wes
In the process of creating my own DB VCS tool tsqlmigrations.codeplex.com I ran into several good resources to help guide me along the way in reviewing existing offerings and in concepts that would be needed in a good DB VCS. This is my list of helpful links that others can use to understand some of the concepts and some of the tools in existence. In the next few posts I will try to explain how I used these to create TSqlMigrations.
Blogs entries
Books
Book |
Description |
Refactoring Databases: Evolutionary Database Design |
Martin Fowler signature series on refactoring databases. Book site: http://databaserefactoring.com/ |
Recipes for Continuous Database Integration: Evolutionary Database Development (Digital Short Cut) |
A good question/answer layout of common problems and solutions with database version control. |
After working with FluentNHibernate and seeing examples of registries in StructureMap, I started craving the same thing for my registrations with Windsor. Our registrations often look like the following:
public static void Register(IWindsorContainer container) { container.Register(Component.For<IFoo>().ImplementedBy<Foo>()); container.AddComponent<IFoo, Foo>(); ... }
There are a few things I don’t like about this approach:
Note: this code is available on github. The registries are tested with nunit, so you can drop in whatever version of windsor and verify it works.
A registry needs a uniform entry point. Enter IWindsorInstaller, a rather undocumented feature that deserves more attention.
public interface IWindsorInstaller { void Install(IWindsorContainer container, IConfigurationStore store); } // Container entry point container.Install(IWindsorInstaller installer);
The container has an install method that takes an instance of IWindsorInstaller. See this post for more details about IWindsorInstaller.
public class SampleRegistry : RegistryBase { public SampleRegistry() { For<IFoo>().ImplementedBy<Foo>().LifeStyle.Singleton(); For<IFoo>().ImplementedBy<Foo>(); } }To pull this off, the RegistryBase keeps a collection of registrations and adapts to the Component.For entry point to capture the registration before returning it. These registrations are stored in a Steps collection, more on why this isn't called Registrations later.
public class RegistryBase : IWindsorInstaller { ... public ComponentRegistration<S> For<S>() { var registration = Component.For<S>(); Steps.Add(registration); return registration; } public ComponentRegistration<S> For<S, F>() { var registration = Component.For<S, F>(); Steps.Add(registration); return registration; } public ComponentRegistration<S> For<S, F1, F2>() { var registration = Component.For<S, F1, F2>(); Steps.Add(registration); return registration; } public ComponentRegistration<S> For<S, F1, F2, F3>() { var registration = Component.For<S, F1, F2, F3>(); Steps.Add(registration); return registration; } public ComponentRegistration<S> For<S, F1, F2, F3, F4>() { var registration = Component.For<S, F1, F2, F3, F4>(); Steps.Add(registration); return registration; } public ComponentRegistration For(params Type[] types) { var registration = Component.For(types); Steps.Add(registration); return registration; } ... }
RegistryBase implements the IWindsorInstaller.Install method to add registrations to the container.
public virtual void Install(IWindsorContainer container, IConfigurationStore store) { Steps.ForEach(s => container.Register(s)); }
The application bootstrapper adds registries. This example assumes all registries are loaded into the container, though they probably would never have dependencies (chicken/egg paradox). I just like to abuse my container :)
private static void LoadRegistries(IWindsorContainer container) { var registries = container.ResolveAll<IWindsorInstaller>(); registries.ForEach(r => container.Install(r)); }
The registry also adapts to a few other entry points and captures their registrations.
In the event there is an entry point missing from the registry, it has a Custom method that takes an Action
Here is a sample of different useages of the registry, of course the entire fluent registration API is at your finger tips.
public class SampleRegistry : RegistryBase { public SampleRegistry() { // Register a singleton For<IFoo>().ImplementedBy<Foo>().LifeStyle.Singleton(); // Extension methods to call property. // Register a single item For<IFoo>().ImplementedBy<Foo>(); For(typeof (IFoo)).ImplementedBy<Foo>(); AddComponent<IFoo, Foo>(); // Custom actions if you want to access the original container API, with deferred installation via lambda expressions Custom(c => c.AddComponent<IFoo, Foo>()); Custom(c => c.Register(Component.For<IFoo>().ImplementedBy<Foo>())); // Scan for types FromAssemblyContaining<SampleRegistry>().BasedOn<IFoo>(); FromAssemblyContaining(typeof (SampleRegistry)).BasedOn<IFoo>(); FromAssemblyNamed("GotFour.Windsor.Tests").BasedOn<IFoo>(); FromAssembly(typeof (SampleRegistry).Assembly).BasedOn<IFoo>(); // Forwarding types For<IFoo, Foo>().ImplementedBy<Foo>(); For<IFoo, Foo, FooBar>().ImplementedBy<FooBar>(); For<IFoo, Foo, FooBar, FooBar2>().ImplementedBy<FooBar2>(); For<IFoo, Foo, FooBar, FooBar2, FooBar3>().ImplementedBy<FooBar3>(); // Adding facilities AddFacility<StartableFacility>(); } }
Notes: I have tested capturing registrations for all of the above scenarios but I suppose there might be some deep dark portion of the registration API that might not work. This would happen if something creates a brand new registration, independent of the original captured one. I have yet to run into this, the design of the api is pretty rock solid as a builder that collects state. I left out AllTypes.Of.From since Of doesn't return a registration, it is simply a call to AllTypes.FromAssemblyXyz().BasedOn() reversed and really isn't very helpful.
I added an extended set of registration points with ExtendedRegistryBase. This adds another layer of new fluent registrations for common scenarios, often involving convention based registration :) If you have additions, please add them in the comments and I will get them added.
public class SampleExtendedRegistry : ExtendedRegistryBase { public SampleExtendedRegistry() { // Same as scanning above in SampleRegistry but much cleaner! ScanMyAssemblyFor<IFoo>(); // Scan for all services of the pattern Service : IService ScanMyAssembly(Conventions.FirstInterfaceIsIName); // Scan for all services of the pattern Whatever : IService (register with first interface) ScanMyAssembly(Conventions.FirstInterface); // Next we could use some attributes to discover services, to register imports / exports :) } }
This registry class helps avoid the static calls to registries from my applications. Now I can scan for registries of a known type and install them into the container. The registries are much more readable with the ceremony gone. I know there was talk of adding something like this to the next version of Windsor/MicroKernel. I hope this is the direction that effort is headed towards. In the mean time enjoy this as a fix to the cravings for a cleaner registry. I typically add one of these per project and let it control registration within that layer.
-Wes