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:
- Write a code generator
- Create a descendant for each class in DAL adding the required changes
- 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;
}
}
[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:
- Domain will "know" about its persistence
- Unrelated classes will get a common superclass
- 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.