Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

db4objects Domain and DAL

0.00/5 (No votes)
16 Aug 2011 1  
How to make the domain independent from db4objects.

Introduction

For quite some time I've been reading articles about NoSQL databases and finally got a project which was planned to be using db4objects. This OODBMS has a very useful feature called transparent activation which is lazy loading. It requires certain functionality from the stored objects, but this contradicts a well known idea that the domain should not depend on how its objects are persisted.

Using the code

The code is very simple and the main idea is not about the coding, but about the design.

The first solution I came up with was based on my experience with NHibernate where each domain class looks like this:

public class Book  
{
    public virtual string Name { get; set; }
}

NHibernate will take care of creating the Book class descendant with all the required modifications. db4o doesn't do this for us so we need to implement the IActivatable interface ourselves, and there are options:

  1. Write a code generator
  2. Create a descendant for each class in DAL adding the required changes
  3. Create a common base class in the domain and implement the required changes there

Option 1 is very complex and in general can be considered as an overhead unless we are db4o developers :)

Option 2 seems reasonable, but we will have to copy-paste a lot of stuff over and over again as there is no multiple inheritance in C#.

Here is how a descendant will look like:

public class DALBook : Domain.Book, IActivatable
{
    public virtual string Name
    {
       get
       {
           Activate(ActivationPurpose.Read);
           return base.Name;
       }
       set
       {
           Activate(ActivationPurpose.Write);
           base.Name = value;
       }
    }

/*
    Here is the part that is required for the transparent activation
    which is identical for each DAL class.
*/
    [Transient]
    private IActivator _activator;

    public void Activate(ActivationPurpose purpose) 
    {
        if(_activator != null) 
        {
            _activator.Activate(purpose);
        }
    }
    public void Bind(IActivator activator) 
    {
        if (_activator == activator)
        {
            return;
        }
        if (activator != null && null != _activator)
        {
            throw new System.InvalidOperationException();
        }
        _activator = activator;
    }
}

It seems desirable to make a common superclass which will implement the Activate and Bind methods.

Option 3 means we bind the domain and the DAL - a very inflexible solution and simply a bad idea:

  1. Domain will "know" about its persistence
  2. Unrelated classes will get a common superclass
  3. etc.

Let's change the class to an interface:

public interface Book  
{
    string Name { get; set; }
}

Now in DAL, we can make a proper hierarchy with a base class implementing the logic really common for every persistable object.

internal abstract class Db4oBase: IActivatable
{
    [Transient]
    private IActivator _activator;

    public void Activate(ActivationPurpose purpose) 
    {
        if(_activator != null) 
        {
            _activator.Activate(purpose);
        }
    }
    public void Bind(IActivator activator) 
    {
        if (_activator == activator)
        {
            return;
        }
        if (activator != null && null != _activator)
        {
            throw new System.InvalidOperationException();
        }
        _activator = activator;
    }
}

internal class DALBook : Db4oBase, Domain.IBook
{
    private string name_;
    public virtual string Name
    {
       get
       {
           Activate(ActivationPurpose.Read);
           return name_;
       }
       set
       {
           Activate(ActivationPurpose.Write);
           name_ = value;
       }
    }
}

All DAL classes are marked as internal as DAL clients should know only about the domain interfaces thus a solution to instantiate concrete classes based on the desired interfaces should be implemented. For instance:

public static class DAOFactory 
{
    public static T CreateDomainObject<T>()
    {
        string dalClass = string.Format("DAL.{0}", typeof(T).Name.Substring(1));
        return (T)Type.GetType(dalClass).GetConstructor(Type.EmptyTypes).Invoke(null);
    }
}

Using DAOFactory, we can create objects and treat them based on their interfaces like:

Domain.IBook book = DAL.DAOFactory.CreateDomainObject<Domain.IBook>();

Points of interest

The domain can be a set of interfaces with the DAL being a set of concrete classes with completely encapsulated implementation which, itself, can have whatever class hierarchy required to eliminate copy-paste and avoid unnecessary cohesion.

History

  • 14 AUG 2011 - Initial version.
  • 16 AUG 2011 - Fixed left angle bracket.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here