Domain Driven Design & Test Driven Design With Entity Framework, Part 1 Building Domain Object Model

by mosessaur| 05 February 2009| 13 Comments

Introduction:

In my previous post I talked about definitions of TDD as specified in different sources. Here in this post I'll walk-through applied Domain Driven Design & Test Driven Design on Entity Framework.

I assume that you already know Entity Framework and how to create your entity data modes using Visual Studio.Net 2008 with SP1.

Domain Driven Design & Entity Framework:

To simplify things, I am using Northwind database. Basically I'll expose 3 tables in this database, Categories, Products & Suppliers. Those will be my domain objects as Category,Product and Supplier classes.

Each Category object might contain one or more items of Product. Same thing for Supplier object as it might provide one or more items of Product. That means Product object must have a Supplier and a Category. I defined those criteria for my Domain Objects.

I started to create my Domain Object using Entity Framework. My Entity Data Model looks like the below diagram:

Northwind Edm Diagram

I'm not done yet. I thought that just using my generated code is not enough. And I want to abstract my Domain Object as much as possible incase I want to use different tool other than Entity Framework such as LINQ to SQL.

So I created 3 contracts (interfaces), that actually represents my Domain Objects: ICategory, IProduct and ISupplier. These contracts has parent interface called IEntity that has only one property called Id. Assuming that all my Domain Objects should have a unique key property of type integer and called Id.

I made my generated models to implement those interfaces (thanks to partial classes). In fact those interfaces contains exactly same property names and types as specified in the above models. Except that I renamed the navigation properties in the generated models to include the word Internal such as ProductsInternal navigation property in Category concrete data model class.

Below are the definition of each interface:

public interface IEntity
{
    int Id { get; }
}

public interface ICategory : IEntity
{
    string CategoryName { get; set; }
    string Description { get; set; }
    byte[] Picture { get; set; }
    IQueryable<IProduct> Products { get; }
}

public interface IProduct : IEntity
{
    string ProductName { get; set; }
    string QuantityPerUnit { get; set; }
    decimal? UnitPrice { get; set; }
    short? UnitsInStock { get; set; }
    short? UnitsOnOrder { get; set; }
    short? ReorderLevel { get; set; }
    bool Discontinued { get; set; }
    ICategory Category { get; }
    ISupplier Supplier { get; }
}

public interface ISupplier : IEntity
{
    string CompanyName { get; set;}
    string ContactName { get; set; }
    string ContactTitle { get; set; }
    string Address { get; set; }
    string Country { get; set; }
    string City { get; set; }
    string Region { get; set; }
    string PostalCode { get; set; }
    string Phone { get; set; }
    string Fax { get; set; }
    string HomePage { get; set; }
    IQueryable<IProduct> Products { get; }
}

I would like to clarify few points about the above code. As you notice on lines 11, 23, 24 and 40. These lines of code define navigation properties. For example ICategory domain object interface has a navigation property IQueryable<IProduct> Products that retrieves all products related to a specific category. I made this to be independent of the under laying generated code by Entity Framework. This is actually abstraction and removing dependency between your Domain Object Definitions and its implementations. The definitions should define what these models should contain and do. The implementation should specify the how. Your concrete models might be coupled with certain technology, as in my case. My concrete models are Entity Framework Models.

Entity Framework as Data Access Layer:

Entity Framework provide Data Services classes that I will use as my data access layer assets. For example the generated ObjectContext class, this is actually acts as my proxy to the underlying datasource and called entity container by Entity Framework Schema. But Entity Framework doesn't support -in version 1- persistence ignorance! Which will make working with my Domain Object Model interfaces hard and may be impossible. Because for example the generate ObjectContext class doesn't have anything that would return a result set of ICategory. And guess what you'll not be able to cast ObjectQuery<Category> to IQueryable<ICategory>.

Take this as an example the generate NorthwindDataContext has a property called CategorySet. This property returns ObjectQuery<Category>. You might think of calling this property like this:
context.CategorySet.Cast<ICategory>().AsQueryable();
But this is simply will not work and will throw an exception as Cast extension method will only work if you are casting to correct generate Entity Models primitive types.

Another issue calling CategorySet property or calling CreateQuery<T> method of the base ObjectContext class would result in creating a query that will require hitting the database when starting to iterate through the results. This issue violates one of the guide lines of performing TDD which is to be independent of the working environment by simulating environmental dependencies such as databases, file systems, networks, queues etc...

To over come these issues simply I created another interface that include basic data access functionality. I learned this idea from Kigg the open source project that is running live on http://dotnetshoutout.com/. This project apply the same idea on Linq To SQL context object class.

I called the interface IDataContext and defined the basic operations I will need in this interface:

public interface IDataContext
{
 IQueryable<ICategory> Categories { get; }
 IQueryable<IProduct> Products { get; }
 IQueryable<ISupplier> Suppliers { get; }
    
 IQueryable<TInterface> GetDataSource<TInterface, TEntity>(Func<TEntity, bool> filterExpression)
     where TInterface : class, IEntity
     where TEntity : class;

 TEntityInterface GetEntityById<TEntityInterface>(int id) 
     where TEntityInterface : class, IEntity;

 void Insert<TEntityInterface>(TEntityInterface instance) 
     where TEntityInterface : class, IEntity;
    
 void Delete<TEntityInterface>(TEntityInterface instance) 
     where TEntityInterface : class, IEntity;
    
 void SubmitChanges();
}

Again the generated ObjectContext class by Entity Framework should implement this interface. Below is the implementation of IDataContext:

public partial class NorthwindDataContext : IDataContext
{
 public static IEnumerable<TInterface> GetEnumerable<TInterface, TEntity>(IQueryable<TEntity> query, Func<TEntity, bool> filter)
  where TInterface : class, IEntity
  where TEntity : class
 {
  var filteredQuery = (filter != null) ? 
	(from item in query select item).Where(filter) : query;
  foreach (var item in filteredQuery)
  {
   yield return item as TInterface;
  }
 }

 public virtual IQueryable<Category> CategoryTable
 {
  get{return this.CategorySet;}
 }
 public virtual IQueryable<Product> ProductTable
 {
  get{return this.ProductSet;}
 }
 public virtual IQueryable<Supplier> SupplierTable
 {
  get{return this.SupplierSet;}
 }
 
 public IQueryable<ICategory> Categories
 {
  get
  {
   return this.GetDataSource<ICategory, Category>(null);
  }
 }
    
 public IQueryable<IProduct> Products
 {
  get
  {
   return this.GetDataSource<IProduct, Product>(null);
  }
 }
    
 public IQueryable<ISupplier> Suppliers
 {
  get
  {
   return this.GetDataSource<ISupplier, Supplier>(null);
  }
 }

 public IQueryable<TInterface> GetDataSource<TInterface, TEntity>(Func<TEntity, bool> filter)
  where TInterface : class, IEntity
  where TEntity : class
 {
  IQueryable<TEntity> queryable = null;
  if (typeof(TInterface) == typeof(ICategory))
     queryable = this.CategoryTable as IQueryable<TEntity>;                
  if (typeof(TInterface) == typeof(IProduct))
     queryable = this.ProductTable as IQueryable<TEntity>;
  if (typeof(TInterface) == typeof(ISupplier))
     queryable = this.SupplierTable as IQueryable<TEntity>;

  return GetEnumerable<TInterface, TEntity>(queryable, filter).AsQueryable() as IQueryable<TInterface>;
 }
    
 public TEntity GetEntityById<TEntity>(int id) 
  where TEntity : class, IEntity
 {
  if (typeof(TEntity) == typeof(ICategory))
  {
   return this.GetDataSource<ICategory,Category>(c => c.Id == id).SingleOrDefault() as TEntity;
  }
  else if (typeof(TEntity) == typeof(IProduct))
  {
   return this.GetDataSource<IProduct,Product>(c => c.Id == id).SingleOrDefault() as TEntity;
  }
  else if (typeof(TEntity) == typeof(ISupplier))
  {
   return this.GetDataSource<ISupplier, Supplier>(c => c.Id == id).SingleOrDefault() as TEntity;
  }
  else
   return null;
  }

 public void Insert<TEntity>(TEntity instance) 
  where TEntity : class, IEntity
 {
  AddObject<TEntity>(instance);
 }
 public void Delete<TEntity>(TEntity instance)
  where TEntity : class, IEntity
 {
  DeleteObject(instance);
 }
 public virtual void AddObject<TEntity>(TEntity instance) 
  where TEntity : class, IEntity
 {
  Type type = typeof(TEntity);
  if (type == typeof(ICategory))
  {
   this.AddToCategorySet(instance as Category);
  }
  else if (type == typeof(IProduct))
  {
   this.AddToProductSet(instance as Product);
  }
  else if (type == typeof(ISupplier))
  {
   this.AddToSupplierSet(instance as Supplier);
  }
 }
    
 public new virtual void DeleteObject(object instance)
 {
  base.DeleteObject(instance);
 }
    
 public void SubmitChanges()
 {
  base.SaveChanges(true);
 }
}

An important thing to note, now after implementing IDataContext, from my code I should use NorthwindDataContext as IDataContext. Which means my consumer application which could be a Repository class or anything else should not know anything about NorthwindDataContext and be totally independent of it.

Using NorthwindDataContext as my data access layer through IDataContext is something that can be specified using Dependency Injection.

Start Updated Content
I would like to explain 2 methods above which are IEnumerable<TInterface> GetEnumerable<TInterface, TEntity>(IQueryable<TEntity> query, Func<TEntity, bool> filter) and IQueryable<TInterface> GetDataSource<TInterface, TEntity>(Func<TEntity, bool> filter)...

GetEnumerable method is helper method, I used it to overcome casting limitations in LINQ to Entities. This method takes IQueryable<TEntity> as a parameter. TEntity is supposed to be the concrete model class while IQueryable<TEntity> will be actually ObjectQuery<TEntity>. This method is used by XxxDataSource properties. It will help me much in testing my Data Access Layer as I will show in the next part soon.

About GetDataSource method, this one also to overcome LINQ to Entities limitations. In fact in implementation GetDataSource makes call to GetEnumerable and pass a filter expression to it. Someone would say why Filter as you could use property Categories and build a LINQ statement over it and that is it, something like context.Categories.Where(c=>c.Id == 1); Well, I highly recommend to review Davy Landman comment on this post which shows that the code snippet context.Categories.Where(c=>c.Id == 1) would perform filter in memory and not on database side. Maybe I should call this method GetFilteredDataSource.
End Updated Content

I didn't complete the implementation of my domain model objects, basically the navigation properties. There are different  ideas to implement them so I will keep that to be done later to differentiate between different approaches I have in mind.

What about TDD first?!

You might ask why I didn't start with TDD as supposed?! Well, I had an idea that my domain object should be ready before starting writing testing code! Someone would say write test and then refactor! I say 2 faces one coin. I made my design, and I will write its Test every time I finish part of it. Now that I finished this part, I would start writing my unit test code. In next part I will talk about Unit Testing with VS.Net tools and Moq. Applying TDD to my design to ensure that my code works fine with my expectations. And later I will do another unit testing for integration to ensure that both TDD and Integration Testing where both built on the correct assumptions.

Moq will help me to achieve 3 guide lines:

  • Separates or simulates environmental dependencies such as databases, file systems, networks, queues, and so on. Tests that exercise these will not run fast, and a failure does not give meaningful feedback about what the problem actually is.
  • Runs and passes in isolation. If the tests require special environmental setup or fail unexpectedly, then they are not good unit tests. Change them for simplicity and reliability. Tests should run and pass on any machine.
  • Often uses stubs and mock objects. If the code being tested typically calls out to a database or file system, these dependencies must be simulated, or mocked. These dependencies will ordinarily be abstracted away by using interfaces

Moq will help me to set expectations for my returning results of certain methods. For example GetEnumerable method I mentioned above should return IEnumerable<TInterface>. I can simulate its operation using Moq and set an expectation to its call where expected and also define the return results. To simulate database results I will use in memory collections which I will specify as return results whenever possible with the assist of Moq. We will look at what Moq can provide us in my next post.

You can download my initiative project and Stay Tuned for the upcoming part.

Comments

Davy Landman
Davy Landman Netherlands on 2/2/2009 11:29 AM Like the direction of the article, I have been designing/figuring out about the same thing.

Would you not get into performance problems because by using this wrapping all the queries will be done in the memory instead of on the SQL server side?

Your repositories would still have to use the NorthwindDataContext because LinqToEntities only likes queries with Entity Model primitive types and .NET primitive types fields.
trackback
DotNetKicks.com on 2/2/2009 12:44 PM Trackback from DotNetKicks.com

Domain Driven Design
mosessaur
mosessaur Egypt on 2/2/2009 1:46 PM @Davy Landman Thank you Davy for the comment. I think you mean the GetEnumerable method! Well using SQL profiler I noticed that invoking this method is not triggering the database. Even when you do GetEnumerable().AsQueryable().
But once you start to iterate, it will hi the database. Just as expected.
You can try it and check it out. If you found anything interesting kindly share it so that I can adjust my code.
Davy Landman
Davy Landman Netherlands on 2/2/2009 2:22 PM @mosessaur: I've looked at the code again, and I looked over the yield in the GetEnumerable!

So, indeed it will remain delayed executed. (first part of my comment is indeed invalid)

But if you wrote :

  IDataContext contex = new NorthwindDataContext();
  var q = contex.ProductDataSource.Where(p => p.Discontinued);
  foreach (var prod in q)
  {
    Console.WriteLine(prod.ProductName);
  }
  Console.ReadLine();

And look at the SQL profiler you'll see that the filtering on the Discontinued is not in the query.

SELECT
1 AS [C1],
[Extent1].[ProductID] AS [ProductID],
[Extent1].[ProductName] AS [ProductName],
[Extent1].[QuantityPerUnit] AS [QuantityPerUnit],
[Extent1].[UnitPrice] AS [UnitPrice],
[Extent1].[UnitsInStock] AS [UnitsInStock],
[Extent1].[UnitsOnOrder] AS [UnitsOnOrder],
[Extent1].[ReorderLevel] AS [ReorderLevel],
[Extent1].[Discontinued] AS [Discontinued],
[Extent1].[CategoryID] AS [CategoryID],
[Extent1].[SupplierID] AS [SupplierID]
FROM [dbo].[Products] AS [Extent1]


So the query will only be translated by EF until GetEnumerable(), after that it's just generic in memory LINQ stuff at work.

It's not actually wrong that the client side should not be able to produce SQL functions, because you'll keep that kind of logic outside of the other layer. My biggest question was, how would you write your repositories?
mosessaur
mosessaur Egypt on 2/2/2009 4:27 PM @Davy Landman Good catch!! I didn't though that this would be the behavior when start using filtering!
Actually for demo purposes I used this technique, but actually I use compiled queries where my filters are specified inside the compiled query that is why I never faced such issue so far.
So for filtering, I think I should look for a way to specify filters and sorting before yielding my results.

About my repository, I will share soon how do I write those classes.
But basically all repository classes have reference IDataContext regardless of the concrete type. and I start to consume its functionality normally.
Paul
Paul United Kingdom on 2/3/2009 7:38 AM Wow, a post with DDD in the title without a comment from Colin Jack yet. I think that is one of the harbingers of the apocalypse..
trackback
DotNetShoutout on 2/3/2009 11:06 AM Trackback from DotNetShoutout

Domain Driven Design & Test Driven Design With Entity Framework, Part 1 Building Domain Object Model
cowgaR
cowgaR United Kingdom on 2/3/2009 9:34 PM just a thought on the last D letter...
Test-driven DEVELOPEMENT
Domain-driven DESING

I think it makes sense, test-driven design doesn't sound good to me although I am ok with it.

I just discovered your blog and it is awesome, you're kind of a guy that likes doing his research on the topic which I like and follow as well...

But I would like to see other OR/M than Entity Framework, it has too many cons to mention and even V2.0 isn't fixing them (recent F.Bauma blog entry) so there isn't really nothing to look forward to. And performance wise, is a turtle...

But that's just my oppinion, there are dozen of others OR/M that other ppl prefer. Nice reading though, it is not hard to apply it to NHibernate for example (and Fluent Nhibernate + LINQ is the future here Wink
mosessaur
mosessaur Egypt on 2/5/2009 9:58 AM @cowgaR Thank you for your comment. Regarding Test Driven Design or Test Driven Development, for me both makes sense in my previouse post I used in the title Test Driven Development\Design because both seems to be valid to me.

Regarding EF, I can imagine how developers who are using EF are facing hard times Smile because I am one of them. The challenege for me is how to live with nothing that can help you survive LOL!! Still I find EF interesting. And my target is to help myself and others who wish to use and learn EF.
At the same time apply decoupling between layers in order to be able to replace EF with other things such as LINQ to SQL or even classic Data Access Layer.
I think I have few more topics regarding the same subject to post about.
Marwan
Marwan Tunisia on 5/30/2009 7:43 AM may I am late, but I want to thank you for this introductory post that clarified many things to me.
really thank you
trackback
VS2010学习 on 6/2/2009 4:44 AM Entity Framework 4 Persistence-Ignorance First Look

Introduction: From the moment I put my hands on Visual Studio.Net 2010 Beta 1 and I’m targeting EF4
bbakkebo
bbakkebo Norway on 8/27/2009 5:02 AM Thanks for this.  I just found your article and hit exactly the same thing you ran into with.  Been bumping around kigg code also.  
Sander Versluys
Sander Versluys Belgium on 8/27/2009 4:02 PM Awesome post (and follow-up series).

I'm learning the Entity Framework now, and was wondering how use with TDD.

Thanks and keep it up! Wink
Comments are closed