Question

I have a unique situation where I am building a DDD based system that needs to access both Active Directory and a SQL database as persistence. Initially this wasnt a problem because our design was setup where we had a unit of work that looked like this:

public interface IUnitOfWork
{
   void BeginTransaction()
   void Commit()
}

and our repositories looked like this:

public interface IRepository<T>
{
   T GetByID()
   void Save(T entity)
   void Delete(T entity)
}

In this setup our load and save would handle the mapping between both data stores because we wrote it ourselves. The unit of work would handle transactions and would contain the Linq To SQL data context that the repositories would use for persistence. The active directory part was handled by a domain service implemented in infrastructure and consumed by the repositories in each Save() method. Save() was responsible with interacting with the data context to do all the database operations.

Now we are trying to adapt it to entity framework and take advantage of POCO. Ideally we would not need the Save() method because the domain objects are being tracked by the object context and we would just need to add a Save() method on the unit of work to have the object context save the changes, and a way to register new objects with the context. The new proposed design looks more like this:

public interface IUnitOfWork
{
   void BeginTransaction()
   void Save()
   void Commit()
}

public interface IRepository<T>
{
   T GetByID()
   void Add(T entity)
   void Delete(T entity)
}

This solves the data access problem with entity framework, but does not solve the problem with our active directory integration. Before, it was in the Save() method on the repository, but now it has no home. The unit of work knows nothing other than the entity framework data context. Where should this logic go? I argue this design only works if you only have one data store using entity framework. Any ideas how to best approach this issue? Where should I put this logic?

Was it helpful?

Solution

I wanted to come back and followup with what I have learned since I posted this. It seems if you are going to keep true to repository pattern, the data stores it persists to do not matter. If there are two data stores, write to them both in the same repository. What is important is to keep up the facade that repository pattern represents: an in memory collection. I would not do separate repositories because that doesn't feel like a true abstraction to me. You are letting the technology under the hood dictate the design at that point. To quote from the dddstepbystep.com:

What Sits Behind A Repository? Pretty much anything you like. Yep, you heard it right. You could have a database, or you could have many different databases. You could use relational databases, or object databases. You could have an in memory database, or a singleton containing a list of in memory items. You could have a REST layer, or a set of SOA services, or a file system, or an in memory cache… You can have pretty much anything – your only limitation is that the Repository should be able to act like a Collection to your domain. This flexibility is a key difference between Repository and traditional data access techniques.

http://thinkddd.com/assets/2/Domain_Driven_Design_-_Step_by_Step.pdf

OTHER TIPS

First I assume you are using an IoC container. I advocate you make true Repositories for each entity type. This means you will wrap each object context EntitySet in a class that implements something like:

interface IRepository<TEntity> {
  TEntity Get(int id);
  void Add(TEntity entity);
  void Save(TEntity entity);
  void Remove(TEntity entity);
  bool CanPersist<T>(T entity);
}

CanPersist merely returns whether that repository instance supports persisting the passed entity, and is used polymorphically by UnitOfWork.Save described below.

Each IRepository will also have a constructor that allows the IRepository to be constructed in "transactional" mode. So, for EF, we might have:

public partial EFEntityARepository : IRepository<EntityA> {
  public EFEntityARepository(EFContext context, bool transactional) {
    _context = context;
    _transactional = transactional;
  }
  public void Add(EntityA entity) {
    _context.EntityAs.Add(entity);
    if (!_transactional) _context.SaveChanges();
  }
}

UnitOfWork should look like this:

interface UnitOfWork {
  void Add(TEntity entity);
  void Save(TEntity entity);
  void Remove(TEntity entity);
  void Complete();
}

The UnitOfWork implementation will use dependency injection to get instances of all IRepository. In UnitOfWork.Save/Add/Remove, the UoW will pass the argument entity into CanPerist of each IRepository. For any true return values, the UnitOfWork will store that entity in a private collection specific to that IRepository and to the intended operation. In Complete, the UnitOfWork will go through all private entity collections and call the appropriate operation on the appropriate IRepository for each entity.

If you have an entity that needs to be partially persisted by EF and partially persisted by AD, you would have two IRepository classes for that entity type (they would both return true from CanPersist when passed an instance of that entity type).

As for maintaining atomicity between EF and AD, that is a separate non-trivial problem.

IMO I would wrap the calls to both of these repos in a service type of class. Then I would use IoC/DI to inject the repo types into the service class. You would have 2 repos, 1 for the Ent. framework and 1 that supports AD. This way each repo deals with only its underlaying data store and doesn't have to cross over.

What I have done to support multiple units of work types, is to have IUnitOfWork be more of a factory. I create another type called IUnitOfWorkScope which is the actual unit of work and it has only a commit method.

namespace Framework.Persistance.UnitOfWork
{
    public interface IUnitOfWork
    {
        IUnitOfWorkScope Get();

        IUnitOfWorkScope Get(bool shared);
    }

    public interface IUnitOfWorkScope : IDisposable
{
    void Commit();
}
}

This allows me to inject different implementations of the unit of work into a service and be able to use them side by side.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top