Pages

Wednesday, June 24, 2009

Exploring a WPF Business Application Architecture 3

It's time to add some DDD style repositories to the pet shop sample's domain model. While I don't want to go down the one-IRepository-fits-all route, I do want to reuse code between the specific repositories. In this post I'll show how to do that using interfaces and extension methods in conjunction to create repository traits that can be mixed and matched to implement specific repositories.

One Size Does Not Fit All
As argued in a nice blog post by Richard Dingwall a one size fits all approach along the lines of:










is not going to work out well: Some of the methods in the one-size-fits-all interface might be inappropriate for some domain classes. So what do we do? -We introduce a number of interfaces each representing specific traits of a repository.

Traits
As proposed by Mr. Dingwall we create a number of interface each specifying one specific trait that a repository might have. Like these:
























and probably others. Now that enables us to pick and choose which traits a specific repository should have. For instance a repository for the pet shops category objects would probably need all the traits listed above, hence it implements all those interfaces:








Incidentally implementing each of these traits is made really easy by Castle.ActiveRecord. All the smae I still don't want to implement them in each and every of the concrete repositories where they are used. This is where extension methods comes in.

Mixins
To be able to provide reusable implementations of the traits I implement an extension method for each method on the trait interfaces:

public static class ActiveRecordRepositoryImpl
{
internal static IEnumerable<Entity> GetAllImpl<Entity>(this ICanGetAll<Entity> self)
where Entity : class
{
return ActiveRecordMediator<Entity>.FindAll();
}

internal static Entity GetByIdImpl<Entity, Key>(this ICanGetById<Entity, Key> self, Key id)
where Entity : class
{
return ActiveRecordMediator<Entity>.FindByPrimaryKey(id);
}

internal static void RemoveImpl<Entity>(this ICanRemove<Entity> self, Entity entity)
where Entity : class
{
ActiveRecordMediator<Entity>.Delete(entity);
}

internal static void SaveImpl<Entity>(this ICanSave<Entity> self, Entity entity)
where Entity : class
{
ActiveRecordMediator<Entity>.Save(entity);
}

internal static int GetCountImpl<Entity>(this ICanGetCount<Entity> self)
where Entity : class
{
return ActiveRecordMediator<Entity>.Count();
}
}
Notice how easy Castle.ActiveRecord makes this. That's nice.
Right; then these *Impl extension methods are used in the concrete repositories:

public class CategoryRepository :
  ICanGetAll<Category>,
ICanGetById<Category, int>,
ICanGetCount<Category>,
ICanRemove<Category>,
ICanSave<Category>
{
public IEnumerable<Category> GetAll()
{
return this.GetAllImpl();
}
public Category GetById(int id)
{
return this.GetByIdImpl(id);
}
public int GetCount()
{
return this.GetCountImpl();
}

public void Remove(Category entity)
{
this.RemoveImpl(entity);
}

public void Save(Category entity)
{
this.SaveImpl(entity);
}
}

And that's it.

So there still the duplication of calling *Impl methods in each concrete implementation method, but the methods actually implementing the repository traits are shared. I think that's pretty nice.

2 comments:

  1. good article. i like it but what will be in the SaveImpl method? Can you give an example? Can the implementation of the methods be shared amongst many repositories?

    ReplyDelete
  2. @Nav the SaveImpl as well as the other impl methods are there in the text: The entire SaveImpl method is:

    internal static void SaveImpl(this ICanSave self, Entity entity)
    where Entity : class
    {
    ActiveRecordMediator.Save(entity);
    }

    That's all. And yes, it can be shared among all repositories - that's the point, actually :-)

    ReplyDelete