Convex Corners

2008/11/26

Prototyping : New wine, old skins

Filed under: Agile, TDD, Uncategorized — Tags: , , , , — neil martin @ 7:29 am

One of the aspects of TDD that causes me concern is mocking out data layers if you have worked with me you will know im a massive fan of repositories and finders , updaters and basically any technique that allows me to abstract data access and all of these do allow me to easily Mock (Moq { stub==mock} )the data layer with out worrying about it if i so choose. So why am i getting my knickers in a twist ?

Well About 2 years ago i friend showed me it was ok to break the rules and the rule in question was this

Unit test everything as a unit

I was introduced the concept that Acceptance tests rule , so basically put you write a top level test that is as close as possible to the acceptance criteria and you hammer down as long as the test is driving you to write code you keep going when you run out of “Test Momentum” you write another test , When you have a complex problem to solve you write another test , When you need to do something your not 100% sure of or confident in you write another test, when you run out of test momentum and your stretching you write a test and finally when it feels uncomfortable or you feel unsupported you write another test.

Now i pushed against this flavour of TDD for a while but after about a week of doing it i realised that you don’t lose quality in your code in fact what happens is your test to code adhesion increases because the technique reduces and in most cases eliminates the need or the wish to mock everything as your path hammer all the way down to the bottom of the stack and you are testing the Functional stripe not the object.

I will warn you this takes some getting used to and at first it gave me a very cold feeling but now for fast moving projects im a convert and nearly all my projects move at break neck speed. the only point worth noting here is that you do need to live and die by the philosophy of find a bug write a test you need to remember with this type of TDD you only cover the happy paths and in a course grained manner so you need good QA.

Ok so highly cohesive course grained testing aside where does that leave us well in my experience one of the things that slows most projects down is worrying data access and databases, i have seen teams having heart felt debates about how mush of the schema do we hammer out now ? what ORM mappers do we use , do we use linq or do we use ADO and stored procs and so it goes on. Generally i personally find it best just to get on with coding the functional stripes (stories) as soon as possible get traction get the rubber on the road , make an impact in the functional footprint. Its important to remember that BA’s and QA’s are class one citizens in your team and for them to do there work they need functional cards satisfied as soon as possible you have a duty to them to get paint on the visible canvass as soon as is possible.

So one of the ways i find helps is to use a OO database beneath the covers for the early stages of development my OODB of choose is DB4Objects its foot print for releasable projects i feel is limited to specialist applications and embedded work however as a prototyping tool it is superb in all regards, and if you isolate your data access functionality beneath good patterns such as repositories and finders then changing this out for a production database such as SQL server , oracle or Mysql is not such a painful exercise.

So to some code.

DB4objects can be found at : http://www.db4o.com/

So db4o is in my view one of the simplest and most elegant data storage engines i have ever seen so here is my base repository and config and container:

Base Object (Container)

namespace net.codequbedevelopments.blush.objects
{
    public class Db4Container : IDb4Container
    {
        private readonly IDb4Config databaseConfig;

        public Db4Container(IDb4Config DatabaseConfig)
        {
            databaseConfig = DatabaseConfig;
        }

        public IObjectContainer database { get; set; }

        public void Open()
        {
            database = Db4oFactory.OpenFile(databaseConfig.FileLocation);

        }

        public void Close()
        {
            database.Close();
        }

    }
}

Config

public interface IDb4Config
  {
      string FileLocation { get; }
  }

Base Repository

namespace net.codequbedevelopments.blush.repositories
{
    public class Db4Repository<T, C> : Db4Container, IRepository<T, C> where C : IList, new()
    {
        public Db4Repository(IDb4Config DatabaseConfig) : base(DatabaseConfig)
        {
        }

        public void Store(T Item)
        {
            try
            {
                Open();
                database.Store(Item);
            }
            finally
            {
                Close();
            }
        }

        public C GetAll()
        {
            C returnValue = default(C);
            try
            {
                Open();
                IList<T> cards = database.Query<T>(typeof (T));
                returnValue = ConvertToCollection(cards);
            }
            finally
            {
                Close();
            }

            return returnValue;
        }

        private C ConvertToCollection(IEnumerable<T> Objects)
        {
            var Collection = new C();
            foreach (T t in Objects)
            {
                Collection.Add(t);
            }
            return Collection;
        }

    }
}

Ok so nothing to shocking there we have methods for dealing with storing an object and a collection we just inherit from this repository to give use type safety and then push it under our functional interface like so:

Repository

namespace net.codequbedevelopments.blush.repositories
{
    public class Db4CardRepository : Db4Repository<ICard, CardStore>, ICardRepository
    {
        private readonly ICardFinder finder;
        private readonly ICardUpdater updater;

        public Db4CardRepository(ICardFinder finder, ICardUpdater updater, IDb4Config DatabaseConfig)
            : base(DatabaseConfig)
        {
            this.finder = finder;
            this.updater = updater;
        }

        public void Save(ICard card)
        {
            Store(MakeUnique(card));
        }

        public ICardStore Cards()
        {
            return GetAll();
        }

        public ICardUpdater Update
        {
            get { return updater; }
        }

        public ICard CreateCard()
        {
            var AdvancedFind = (IAdvancedDb4CardFinder) Find;
            ICard card = null;
            try
            {
                Open();
                ICard referenceCard = AdvancedFind.ByHighestUniqueID(this);
                if (referenceCard != null)
                {
                    int uniqueID = referenceCard.UniqueID;
                    card = new Card
                               {
                                   UniqueID = (uniqueID + 1),
                                   Description = "<p> </P> ",
                                   Name = " ",
                                   State = CardStates.Perperation
                               };
                }
                else
                {
                    card = new Card {UniqueID = 1};
                }
            }
            finally
            {
                Close();
            }
            return card;
        }

        public ICardFinder Find
        {
            get { return finder; }
        }

        private ICard MakeUnique(ICard card)
        {
            if (finder.ByUniqueID(card.UniqueID) != null)
            {
                card.UniqueID ++;
                MakeUnique(card);
            }
            return card;
        }
    }
}

Interface

public interface ICardRepository
    {
        ICardFinder Find { get; }
        void Save(ICard card);
        ICardStore Cards();
        ICardUpdater Update { get; }
        ICard CreateCard();
    }

Ok so all that is left is to wire up our flavour of IOC to inject the right repository against the interface in this case im using Unity.

private static void RegisterRepositories()
  {
      container.RegisterType<ICardRepository, Db4CardRepository>(new ContainerControlledLifetimeManager());
      container.RegisterType<IUserRepository, Db4UserRepository>(new ContainerControlledLifetimeManager());
  }

So thats as complex as it gets ill expand on this with some simple finders and updaters in another post but the only thing left to say is happy coding.

XXMEXX

Post Note : please find a zip here : http://codequbedevelopments.net/blush/samples/

Source Zip being updated 05 march 09

10 Comments »

  1. Hi!

    This is cool Nail.
    I look forward to your db4o follow up post!
    Would it be possible to offer the source code in a zip file (and maybe also an assembly)?
    Check this out:
    http://developer.db4o.com/blogs/community/archive/2008/11/27/abstracting-data-access-db4o-example.aspx

    Best!

    German Viscuso
    db4objects, Inc.

    Comment by German — 2008/11/28 @ 8:29 pm

  2. i have source code here

    Comment by Becky — 2008/11/28 @ 9:22 pm

  3. For details about refactoring with db4o see:
    http://developer.db4o.com/Resources/view.aspx/Reference/Implementation_Strategies/Refactoring_and_Schema_Evolution

    The link to the source code seems to be broken:
    http://codequbedevelopments.net/blush/releases/release.htm

    Can you fix it?

    Best!

    Comment by German — 2008/12/31 @ 7:17 pm

  4. source code republished to : http://codequbedevelopments.net/blush/releases/release.htm

    i took it down by accident sorry about that

    Comment by neilmartinagile — 2009/01/01 @ 12:59 am

  5. that’s cool to use DB4O with not duplicated code. but i actually i can’t trace the simple . could you please attach the code in Zip file.
    thank you Neil.
    Best regards
    Karim

    Comment by Karim — 2009/03/29 @ 12:49 pm

  6. Applogies
    Im havng a world of pain with my hosting provider , i have uploaded a slimmed down zip to here ( http://www.codequbedevelopments.net/blush/samples/ ) if this dosent explain it in enough detail le tme know and ill tailor another example.

    kind regards
    neil

    Comment by neilmartinagile — 2009/03/29 @ 1:02 pm

  7. Than you Mr.Neil so much.
    have a nice day.
    Best regards
    Karim

    Comment by Karim — 2009/03/29 @ 8:17 pm

  8. The style of writing is quite familiar . Did you write guest posts for other bloggers?

    Comment by Ex Boyfriend — 2009/04/10 @ 12:32 am

  9. hmm.. cognitively ))

    Comment by boyclelab — 2009/04/16 @ 2:34 am


RSS feed for comments on this post. TrackBack URI

Leave a comment

Blog at WordPress.com.