Another time I start from :
“Program to an interface and not to an implementation”
I want have the same approach, I’m using for DAOs, Models, Presenters, Validation, and so on, for my domain.
The domain:
public interface IEntity<TIdentity>: IEquatable<IEntity<TIdentity>>
{
TIdentity Id { get; }
}
public interface IAnimal : IEntity<int>
{
string Description { get; set; }
}
public interface IReptile : IAnimal
{
float BodyTemperature { get; set; }
}
public interface IHuman : IAnimal
{
string Name { get; set; }
string NickName { get; set; }
DateTime Birthdate { get; set; }
}
public interface IFamily<T> : IEntity<int> where T : IAnimal
{
T Father { get; set; }
T Mother { get; set; }
ISet<T> Childs { get; set; }
}
Because I’m going to work with interfaces I will need a sort of factory to have transient-instances of my entities. For this example I’m going to use something simple and more “general purpose”; a class resolver:
public interface IClassResolver : IDisposable
{
T Resolve<T>() where T : class;
T Resolve<T>(string service) where T : class;
}
The responsibility of the IClassResolver implementor is return an instance for a given Type where the Type is an interface (well… in general is an interface). The concrete implementation of a IClassResolver will be injected using some IoC framework but for this post I will use a simple static exposer:
public class DI
{
private static IClassResolver resolver;
private DI() {}
public static IClassResolver Resolver
{
get
{
if (resolver == null)
{
throw new InvalidOperationException("Resolver was not initialized. Use StackResolver.");
}
return resolver;
}
}
public static void StackResolver(IClassResolver dependencyResolver)
{
resolver = dependencyResolver;
}
}
As you can see nothing so complicated.
Now I have all needed to write a test for my domain:
[Test]
public void DomainAbstraction()
{
using (ISession s = sessions.OpenSession())
using (ITransaction tx = s.BeginTransaction())
{
var rf = DI.Resolver.Resolve<IReptile>();
rf.Description = "Crocodile";
var rm = DI.Resolver.Resolve<IReptile>();
rm.Description = "Crocodile";
var rc1 = DI.Resolver.Resolve<IReptile>();
rc1.Description = "Crocodile";
var rc2 = DI.Resolver.Resolve<IReptile>();
rc2.Description = "Crocodile";
var rfamily = DI.Resolver.Resolve<IFamily<IReptile>>();
rfamily.Father = rf;
rfamily.Mother = rm;
rfamily.Childs = new HashedSet<IReptile> { rc1, rc2 };
s.Save(rfamily);
tx.Commit();
}
using (ISession s = sessions.OpenSession())
using (ITransaction tx = s.BeginTransaction())
{
var hf = DI.Resolver.Resolve<IHuman>();
hf.Description = "Flinstone";
hf.Name = "Fred";
var hm = DI.Resolver.Resolve<IHuman>();
hm.Description = "Flinstone";
hm.Name = "Wilma";
var hc1 = DI.Resolver.Resolve<IHuman>();
hc1.Description = "Flinstone";
hc1.Name = "Pebbles";
var hfamily = DI.Resolver.Resolve<IFamily<IHuman>>();
hfamily.Father = hf;
hfamily.Mother = hm;
hfamily.Childs = new HashedSet<IHuman> { hc1 };
s.Save(hfamily);
tx.Commit();
}
using (ISession s = sessions.OpenSession())
using (ITransaction tx = s.BeginTransaction())
{
var hf = s.CreateQuery("from HumanFamily").List<IFamily<IHuman>>();
Assert.That(hf.Count, Is.EqualTo(1));
Assert.That(hf[0].Father.Name, Is.EqualTo("Fred"));
Assert.That(hf[0].Mother.Name, Is.EqualTo("Wilma"));
Assert.That(hf[0].Childs.Count, Is.EqualTo(1));
var rf = s.CreateQuery("from ReptilesFamily").List<IFamily<IReptile>>();
Assert.That(rf.Count, Is.EqualTo(1));
Assert.That(rf[0].Childs.Count, Is.EqualTo(2));
tx.Commit();
}
using (ISession s = sessions.OpenSession())
using (ITransaction tx = s.BeginTransaction())
{
s.Delete("from HumanFamily");
s.Delete("from ReptilesFamily");
tx.Commit();
}
}
Note: s.Save(hfamily) <<=== there isn’t a string for the entity-name; now NH are supporting it.
As you can see the users of my domain (the test in this case), are working only using interfaces; there isn’t a reference to a concrete implementation of my domain. The concrete implementation of the domain is trivial and you can see it downloading the code. The main thing you will notice, in the implementation, is the absence of the virtual modifier.
To wire the interface, with its concrete implementation, I want use NHibernate. The mapping is similar to the previous:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="EntityNameInAction.Abstraction.Entities.Impl"
namespace="EntityNameInAction.Abstraction.Entities.Impl.Naturalness"
default-access="backfield">
<class name="MyAnimal" abstract="true"
proxy="EntityNameInAction.Abstraction.Entities.Naturalness.IAnimal, EntityNameInAction.Abstraction.Entities"
entity-name="Animal">
<id name="id" access="field">
<generator class="hilo"/>
</id>
<discriminator column="kind"/>
<property name="Description"/>
</class>
<subclass name="MyHuman"
proxy="EntityNameInAction.Abstraction.Entities.Naturalness.IHuman, EntityNameInAction.Abstraction.Entities"
extends="Animal"
entity-name="Human">
<property name="Name"/>
<property name="NickName"/>
<property name="Birthdate" type="Date"/>
</subclass>
<subclass name="MyReptile"
proxy="EntityNameInAction.Abstraction.Entities.Naturalness.IReptile, EntityNameInAction.Abstraction.Entities"
extends="Animal"
entity-name="Reptile">
<property name="BodyTemperature"/>
</subclass>
<class name="MyFamily`1[[EntityNameInAction.Abstraction.Entities.Naturalness.IReptile, EntityNameInAction.Abstraction.Entities]]"
proxy="EntityNameInAction.Abstraction.Entities.Naturalness.IFamily`1[[EntityNameInAction.Abstraction.Entities.Naturalness.IReptile, EntityNameInAction.Abstraction.Entities]], EntityNameInAction.Abstraction.Entities"
table="Families" discriminator-value="Reptile" where="familyKind = 'Reptile'"
entity-name="ReptilesFamily">
<id name="id" access="field">
<generator class="hilo"/>
</id>
<discriminator column="familyKind"/>
<many-to-one name="Father" cascade="all" entity-name="Reptile"/>
<many-to-one name="Mother" cascade="all" entity-name="Reptile"/>
<set name="Childs" generic="true" cascade="all">
<key column="familyId" />
<one-to-many entity-name="Reptile"/>
</set>
</class>
<class name="MyFamily`1[[EntityNameInAction.Abstraction.Entities.Naturalness.IHuman, EntityNameInAction.Abstraction.Entities]]"
proxy="EntityNameInAction.Abstraction.Entities.Naturalness.IFamily`1[[EntityNameInAction.Abstraction.Entities.Naturalness.IHuman, EntityNameInAction.Abstraction.Entities]], EntityNameInAction.Abstraction.Entities"
table="Families" discriminator-value="Human" where="familyKind = 'Human'"
entity-name="HumanFamily">
<id name="id" access="field">
<generator class="hilo"/>
</id>
<discriminator column="familyKind"/>
<many-to-one name="Father" cascade="all" entity-name="Human"/>
<many-to-one name="Mother" cascade="all" entity-name="Human"/>
<set name="Childs" generic="true" cascade="all">
<key column="familyId" />
<one-to-many entity-name="Human"/>
</set>
</class>
</hibernate-mapping>
In the implementation of IClassResolver I’m going to use the NHibernate’s mapping to wire the interface of the domain (ex: IHuman) to its concrete class (ex: MyHuman) trough the entity-name. Is it not clear ? ok perhaps the code will be more clear
public class NhEntityClassResolver : IClassResolver
{
private readonly Dictionary<Type, string> serviceToEntityName = new Dictionary<Type, string>();
public NhEntityClassResolver(ISessionFactoryImplementor factory)
{
if(factory == null)
{
throw new ArgumentNullException("factory");
}
Factory = factory;
InitializeTypedPersisters();
}
private void InitializeTypedPersisters()
{
foreach (var entityName in Factory.GetAllClassMetadata().Keys)
{
serviceToEntityName
.Add(Factory.GetEntityPersister(entityName)
.GetConcreteProxyClass(EntityMode.Poco), entityName);
}
}
public ISessionFactoryImplementor Factory { get; private set; }
#region Implementation of IDisposable
public void Dispose()
{
}
#endregion
#region Implementation of IClassResolver
public T Resolve<T>() where T : class
{
string entityName;
if(serviceToEntityName.TryGetValue(typeof(T), out entityName))
{
return Resolve<T>(entityName);
}
return null;
}
public T Resolve<T>(string service) where T: class
{
return Factory.GetEntityPersister(service).Instantiate(null, EntityMode.Poco) as T;
}
#endregion
}
The ISessionFactoryImplementor is one of the interfaces implemented by the NH SessionFactory. The method Resolve<T>(string) are using the parameter service as the entity-name. The hard-work is done by the method InitializeTypedPersisters; what I’m doing there is map each interface with its entity-name… nothing more.
“Program to an interface and not to an implementation” is really wonderful.
Do you really have some doubt about how NHibernate implements “Persistence ignorance” ?
Code available here.