Using DB4Objects as a prototyping tool. Part I


I have been looking at DB4Objects http://db4objects.com as a persistence layer that could help with prototyping work. If you are not familiar with db4objects ( DB4O ), it is an object oriented database.  What does that mean?  If I boil it down in laymen’s terms : it is a database without tables or columns.

 Sample of DB4Objects

Here is a sample of storing a simple domain object:




   1: User  user = new User {Username = “testuser”, Password = “password”};
   2: user.Roles = new List<Role> {new Role {RoleType = UserRepositoryTester.RoleType.User}};
   3: using( IObjectContainer objectContainer = PersistanceContainerFactory.Create())
   4: {
   5:     objectContainer.Store(user);
   6: }

The magic sauce is in line 5. I did not have to do anything to setup how to save a User object in the database.  It just serializes the object to the database. No need to worry about table names, column names or data types. This all comes for free with this engine, allowing a developer to focus on the domain model and the value-add code, rather than data persistence.


Why use it for prototypes?


My thought here is this.  The sooner that I can deliver software, the sooner I can start getting to the actual requirements of what it needs to do. My experience is that once the designers and/or product owners see software in working form, they get inspired to really work on making the most of the application. This could be from a User eXperience perspective or sometimes it is just a realization that they did not know what they needed until they saw it in executable form. It really does not matter why they want to make a change. If the end product of a change is better software, than that is a good thing.  To help facilitate getting to software that can be demonstrated, I think that using a persistence engine like DB4O could really help with the time to delivery.  Once the changes/churn has slowed down, than you can take the time to implement the persistence layer. I think writing business logic rather than persistence code that will be changed later, is the best use of developers time.


There are some Caveats




  • First and foremost using infrastructure technology like DB4O has to be used in a manor that promotes Separation Of Concerns from a dependency and design point of view.
  • You need to use the least common denominators as far as functionality, between the prototyping framework and the production persistence framework.
     
  • </UL></DIV>

    A working sample

    The following sample code is available at http://erichexter.googlecode.com/svn/trunk/DB4O-sample/
     
    So to keep with test first development, lets look at the tests:


       1: [TestFixture]
       2: public class Given_that_a_user_exist_in_repository:Db4oBaseTester
       3: {
       4:     private IUserRepository repository;
       5:     private User user;
       6:  
       7:     protected override void  OnStartup()
       8:     {
       9:         repository = new UserRepository(objectContainer);
      10:         user = new User {Username = “testuser”, Password = “password”};
      11:         user.Roles = new List<Role> {new Role {RoleType = UserRepositoryTester.RoleType.User}};
      12:         objectContainer.Store(user);
      13:     }
      14:     [Test]
      15:     public void When_a_linq_query_is_executed_the_user_should_be_returned()
      16:     {
      17:         IEnumerable<User> users = from User u in repository.Query()
      18:                                   where u.Username.Equals(“testuser”) && u.Roles.Count == 1
      19:                                   select u;
      20:         IList<User> userList = new List<User>(users);
      21:  
      22:         Assert.That(userList.Count, Is.EqualTo(1));
      23:     }
      24:     [Test]
      25:     public void When_a_user_is_deleted_the_user_should_be_removed_from_the_repository()
      26:     {
      27:         repository.Delete(user);
      28:         
      29:         IObjectSet retrievedUser = objectContainer.QueryByExample(user);
      30:         Assert.That(retrievedUser.Count, Is.EqualTo(0));
      31:     }
      32:     [Test]
      33:     public void When_a_username_is_supplied_the_repository_should_return_the_user()
      34:     {
      35:         User retrivedUser = repository.Get(user.Username);
      36:  
      37:         Assert.That(user,Is.EqualTo(retrivedUser));
      38:     }
      39:     [Test]
      40:     public void When_the_user_object_is_changed_the_repository_should_save_the_changes()
      41:     {
      42:         user.Username += “1”;
      43:         repository.Save(user);
      44:  
      45:         User retrievedUser = (User) objectContainer.QueryByExample(new User() {Username = user.Username})[0];
      46:  
      47:         Assert.That(user, Is.EqualTo(retrievedUser));
      48:     }
      49:     protected override void OnTearDown()
      50:     {
      51:         objectContainer.Delete(user);
      52:     }
      53: }
    Here I have implemented some basic functionality of a persistence engine.  My goal was to provide an interface which 
    could be used to deliver the bare minimum persistence layer so that I can concentrate on user defined features.  
    Here is the implementation using DB4Objects.  The IObjectContainer is a DB4o interface which is returned when you open up a new db4o database.
     


       1: public class UserRepository : IUserRepository, IDisposable
       2: {
       3:     private readonly IObjectContainer objectContainer;
       4:  
       5:     public UserRepository(IObjectContainer ObjectContainer)
       6:     {
       7:         objectContainer = ObjectContainer;
       8:     }
       9:  
      10:     public void Dispose()
      11:     {
      12:         objectContainer.Close();
      13:         objectContainer.Dispose();
      14:     }
      15:  
      16:     public void Save(User user)
      17:     {       
      18:         objectContainer.Store(user);        
      19:     }
      20:  
      21:     public User Get(string ID)
      22:     {
      23:         return GetByUsername(ID);
      24:     }
      25:  
      26:     public IEnumerable<User> Query()
      27:     {
      28:         return objectContainer.Cast<User>();
      29:     }
      30:  
      31:     public void Delete(User user)
      32:     {
      33:         objectContainer.Delete(user);
      34:     }
      35:  
      36:     public User GetByUsername(string username)
      37:     {
      38:         IObjectSet set = objectContainer.QueryByExample(new User() {Username = username});
      39:         if (set.Count >= 1)
      40:             return set[0] as User;
      41:         else
      42:             return null;
      43:     }
      44: }
    </DIV>

     


    Its all in the LINQ


    The biggest challenge to using one persistence framework for prototyping and another for production code boils down to the ability to query the objects being persisted.  That makes me think that without .Net 3.5 and the LINQ features of the languages than too much time would be spent developing a persistence independent querying /criteria domain model to get any real advantages as far as developer productivity.  I believe that the real power comes in by using LINQ to query your objects in your service layer when it uses the repositories. This separation really forces the persistence layer to deal specifically with persistence  work and infrastructure optimizations.  The bigger benefit (than separation of infrastructure concerns)  is that the querying criteria can be moved into a service layer that deals specifically with logic around selecting the appropriate objects or projections. I think LINQ addresses the caveat of the Least Common Denominator for querying syntax. This is a huge win in developer productivity (once you get your head around linq)


    Where do we go next?


    The next step is to implement a UserRepository which would be acceptable for production. I am not suggesting that DB4O cannot be used in production, but in my world of ecommerce, our operations team is willing to support SQL Server, end of conversation. So I need to focus on that requirement for production ready code. We will look at developing a persistence layer for production in Part II of this series.

    </p>