Various topics from software architecture part 2: the Repository pattern

Introduction

In the previous post we looked at the RequestResponse messaging pattern. This pattern is often used in Service Oriented Architecture projects to simplify the communication between the service and its client.

In this post we’ll look at something very different. The Repository pattern is frequently employed in layered architectures where the domains in the Domain layer expose data access related operations through abstract interfaces. The actual repository layer, e.g. EntityFramework or MongoDb then implement those interfaces.

The primary goal of the Repository pattern is to decouple the domain objects from the physical repository layer. The domain objects and the domain layer are not supposed to know about the actual data access operations such as opening a database connection and querying a database. This is a common approach in Domain Driven Design (DDD).

We’ll go through the basic concepts of the pattern in this post. If you’d like to see a more complex design then you can go through the series on DDD. Also, there are multiple ways to implement the pattern but the main objective is to keep the technology-specific data access operations out of the domain layer.

A simple domain

Read more of this post

Advertisements

A model .NET web service based on Domain Driven Design Part 6: the concrete Repository continued

Introduction

In the previous post we laid the foundation for the concrete domain-specific repositories. We implemented the IUnitOfWork and IUnitOfWorkRepository interfaces and created an abstract Repository class. It’s time to see how we can implement the Customer repository. We’ll continue where we left off in the previous post.

The concrete Customer repository

Add a new folder called Repositories in the Repository.Memory data access layer. Add a new class called CustomerRepository with the following stub:

public class CustomerRepository : Repository<Customer, int, DatabaseCustomer>, ICustomerRepository
{
         public CustomerRepository(IUnitOfWork unitOfWork, IObjectContextFactory objectContextFactory) :  base(unitOfWork, objectContextFactory)
	{}

	public override Customer FindBy(int id)
	{
		throw new NotImplementedException();
	}

	public override DatabaseCustomer ConvertToDatabaseType(Customer domainType)
	{
		throw new NotImplementedException();
	}

	public IEnumerable<Customer> FindAll()
	{
		throw new NotImplementedException();
	}
}

You’ll need to add a reference to the Domain layer for this to compile.

We derive from the abstract Repository class and declare that the object is represented by the Customer class in the domain layer and by the DatabaseCustomer in the data storage layer and has an id type int. We inherit the FindBy, the ConvertToDatabaseType and the FindAll methods. FindAll() is coming indirectly from the IReadOnlyRepository interface which is implemented by IRepository which in turn is implemented by ICustomerRepository. And where are the three methods of IRepository? Remember, that the Update, Insert and Delete methods have already been implemented in the Repository class so we don’t need to worry about them. Any time we create a new domain-specific repository, those methods have been taken care of.

Let’s implement the methods one by one. In the FindBy(int id) method we’ll need to consult the DatabaseCustomers collection first and get the DatabaseCustomer object with the incoming id. Then we’ll populate the Customer domain object from its database representation. The DatabaseCustomers collection can be retrieved using the IObjectContextFactory dependency of the base class. However, its backing field is private, so let’s add the following read-only property to Repository.cs:

public IObjectContextFactory ObjectContextFactory
{
	get
	{
		return _objectContextFactory;
	}
}

The FindBy(int id) method can be implemented as follows:

public override Customer FindBy(int id)
{
	DatabaseCustomer databaseCustomer = (from dc in ObjectContextFactory.Create().DatabaseCustomers
										 where dc.Id == id
										 select dc).FirstOrDefault();
	Customer customer = new Customer()
	{
		Id = databaseCustomer.Id
		,Name = databaseCustomer.CustomerName
		,CustomerAddress = new Address()
		{
			AddressLine1 = databaseCustomer.Address
			,AddressLine2 = string.Empty
			,City = databaseCustomer.City
			,PostalCode = "N/A"
		}
	};
	return customer;
}

So we populate the Customer object based on the properties of the DatabaseCustomer object. We don’t store the postal code or address line 2 in the DB so they are not populated. In a real life scenario we’d probably need to rectify this by extending the DatabaseCustomer table, but for now we don’t care. This example shows again the advantage of having complete freedom over the database and domain representations of a domain. You have the freedom of populating the domain object from its underlying database representation. You can even consult other database objects if needed as the domain object properties may be dispersed across 2-3 or even more database tables. The domain object won’t be concerned with such details. The domain and the database are completely detached so you don’t have to worry about changing either the DB or the domain representation.

Let’s factor out the population process to a separate method as it will be needed later:

private Customer ConvertToDomain(DatabaseCustomer databaseCustomer)
{
	Customer customer = new Customer()
	{
		Id = databaseCustomer.Id
		,Name = databaseCustomer.CustomerName
		,CustomerAddress = new Address()
		{
			AddressLine1 = databaseCustomer.Address
			,AddressLine2 = string.Empty
			,City = databaseCustomer.City
			,PostalCode = "N/A"
		}
	};
	return customer;
}

The update version of FindBy(int id) looks as follows:

public override Customer FindBy(int id)
{
	DatabaseCustomer databaseCustomer = (from dc in ObjectContextFactory.Create().DatabaseCustomers
										 where dc.Id == id
										 select dc).FirstOrDefault();
	if (databaseCustomer != null)
	{
		return ConvertToDomain(databaseCustomer);
	}
	return null;
}

The ConvertToDatabaseType(Customer domainType) method will be used when inserting and modifying domain objects:

public override DatabaseCustomer ConvertToDatabaseType(Customer domainType)
{
	return new DatabaseCustomer()
	{
		Address = domainType.CustomerAddress.AddressLine1
		,City = domainType.CustomerAddress.City
		,Country = "N/A"
		,CustomerName = domainType.Name
		,Id = domainType.Id
		,Telephone = "N/A"
	};
}

Nothing fancy here I suppose.

Finally FindAll simply retrieves all customers from the database and converts each to a domain type:

public IEnumerable<Customer> FindAll()
{
	List<Customer> allCustomers = new List<Customer>();
	List<DatabaseCustomer> allDatabaseCustomers = (from dc in ObjectContextFactory.Create().DatabaseCustomers
						   select dc).ToList();
	foreach (DatabaseCustomer dc in allDatabaseCustomers)
	{
		allCustomers.Add(ConvertToDomain(dc));
	}
	return allCustomers;
}

That’s it, there’s at present nothing more to add the CustomerRepository class. If you add any specialised queries in the ICustomerRepository interface they will need to be implemented here.

I’ll finish this post with a couple of remarks on EntityFramework.

Entity Framework

There is a way to implement the Unit of Work pattern in conjunction with the EntityFramework in a different, more simplified way. The database type is completely omitted from the structure leading to the following Repository abstract class declaration:

public abstract class Repository<DomainType, IdType> : IUnitOfWorkRepository where DomainType : IAggregateRoot

So there’s no trace of how the domain is represented in the database. There is a way in the EF designer to map the columns of a table to the properties of an object. You can specify the namespace of the objects created by EF – the .edmx file – like this:

1. Remove the code generation tool from the edmx file properties:

Remove EF code generation tool

2. Change the namespace of the project by right-clicking anywhere on the EF diagram and selecting Properties:

Change namespace in entity framework

Change that value to the namespace of your domain objects.

3. If the ID of a domain must be auto-generated by the database then you’ll need to set the StoreGeneratedPattern to Identity on that field:

EF identity generation

You can fine-tune the mapping by opening the .edmx file in a text editor. It is standard XML so you can edit it as long as you know what you are doing.

This way we don’t need to declare the domain type and the database type, we can work with only one type because it will be common to EF and the domain model. It is reasonable to follow down this path as it simplifies certain things, e.g. you don’t need the conversions between DB and domain types. However, there are some things to keep in mind I believe.

We inherently couple the domain object to its database counterpart. It may work with simple domain objects, but not with more complex objects where the database representation can be spread out in different tables. We may even face a different scenario: say we made a design mistake in the database structuring phase and built a table that represents more than one domain object. We may not have the possibility of rebuilding that table as it contains millions of rows and is deployed in a production environment. How do we then map 2 or more domain objects to the same database table in the EF designer? It won’t work, at least I don’t know any way how this can be solved. Also, mapping stored procedures to domain objects or domain object rules can be problematic. What’s more, you’ll need to mark those properties of your domains virtual where you want to allow lazy loading by the ORM framework like this:

public class Book
{
    public int Id { get; set; }
    public virtual Title Title { get; set; }
}

I think this breaks the persistence ignorance (PI) feature of proper POCO classes. We’re modifying the domain to give way for a persistence technology.

However, you may be OK with these limitations. Your philosophy may well differ from my rigid PI and POCO approach. It is certainly desirable that the database tables and domains are as close as possible, but you don’t always have this luxury with large legacy databases. If you start off with a completely empty database with no tables then EF and the code-first approach – where the data tables will be created for you based on your self-written object context implementation – can simplify the repository development process. However, keep in mind that database tables are a lot more rigid and resistant to change than your loosely coupled and layered code. Once the database has been put into production and you want to change the structure of a domain object you may run into difficulties as the corresponding database table may not be modified that easily.

In the next post we’ll discuss the application services layer.

View the list of posts on Architecture and Patterns here.

A model .NET web service based on Domain Driven Design Part 5: the concrete Repository

Introduction

In the previous post we laid the foundation for our repository layer. It’s now time to see how those elements can be implemented. The implemented data access layer will be a simple in-memory data storage solution. I could have selected something more technical, like EntityFramework but giving a detailed account on a data storage mechanism is not the target of these series. It would probably only sidetrack us too much. The things we take up in this post should suffice for you to implement an EF-based concrete repository.

This is quite a large topic and it’s easy to get lost so this post will be devoted to laying the foundation of domain-specific repositories. In the next post we’ll implement the Customer domain repository.

The Customer repository

We need to expose the operations that the consumer of the code is allowed to perform on the Customer object. Recall from the previous post that we have 2 interfaces: one for read-only entities and another for the ones where we allow all CRUD operations. We want to be able to insert, select, modify and delete Customer objects. The domain-specific repository interfaces must be declared in the Domain layer.

This point is important to keep in mind: it is only the data access abstraction we define in the Domain layer. It is up to the implementing data access mechanism to hide the implementation details. We do this in order to keep the Domain objects completely free of persistence logic so that they remain persistence-ignorant. You can persist the domains in the repository layer the way you want, it doesn’t matter, as long as those details do not bubble up to the other layers in any way, shape or form. Once you start referencing e.g. the DB object context in the web layer you have committed to use EntityFramework and a switch to another technology will be all the more difficult.

Insert the following interface in the Domain/Customer folder:

public interface ICustomerRepository : IRepository<Customer, int>
{
}

Right now it’s an empty interface as we don’t want to declare any new data access capability over and above the methods already defined in the IRepository interface. Recall that we included the FindAll() method in the IReadOnlyRepository interface which allows us to retrieve all entities from the data store. This may be dangerous to perform on an entity where we have millions of rows in the database. Therefore you may want to remove that method from the interface. However, if we only have a few customers then it may be OK and you could have the following customer repository:

public interface ICustomerRepository : IRepository<Customer, int>
{
	IEnumerable<Customer> FindAll();
}

The Repository layer

Insert a new C# class library called DDDSkeletonNET.Portal.Repository.Memory. Add a reference to the Infrastructure layer as we’ll need access to the abstractions defined there. We’ll first need to implement the Unit of Work. Add the following stub implementation to the Repository layer:

public class InMemoryUnitOfWork : IUnitOfWork
{
	public void RegisterUpdate(IAggregateRoot aggregateRoot, IUnitOfWorkRepository repository)
	{}

	public void RegisterInsertion(IAggregateRoot aggregateRoot, IUnitOfWorkRepository repository)
	{}

	public void RegisterDeletion(IAggregateRoot aggregateRoot, IUnitOfWorkRepository repository)
	{}

	public void Commit()
	{}
}

Let’s fill this in. As we don’t have any built-in solution to track the changes to our entities we’ll have to store the changes ourselves in in-memory collections. Add the following private fields to the class:

private Dictionary<IAggregateRoot, IUnitOfWorkRepository> _insertedAggregates;
private Dictionary<IAggregateRoot, IUnitOfWorkRepository> _updatedAggregates;
private Dictionary<IAggregateRoot, IUnitOfWorkRepository> _deletedAggregates;

We keep track of the changes in these dictionaries. Recall the function of the unit of repository: it will perform the actual data persistence.

We’ll initialise these objects in the unit of work constructor:

public InMemoryUnitOfWork()
{
	_insertedAggregates = new Dictionary<IAggregateRoot, IUnitOfWorkRepository>();
	_updatedAggregates = new Dictionary<IAggregateRoot, IUnitOfWorkRepository>();
	_deletedAggregates = new Dictionary<IAggregateRoot, IUnitOfWorkRepository>();
}

Next we implement the registration methods which in practice only means that we’re filling up these dictionaries:

public void RegisterUpdate(IAggregateRoot aggregateRoot, IUnitOfWorkRepository repository)
{
	if (!_updatedAggregates.ContainsKey(aggregateRoot))
	{
		_updatedAggregates.Add(aggregateRoot, repository);
	}
}

public void RegisterInsertion(IAggregateRoot aggregateRoot, IUnitOfWorkRepository repository)
{
	if (!_insertedAggregates.ContainsKey(aggregateRoot))
	{
		_insertedAggregates.Add(aggregateRoot, repository);
	}
}

public void RegisterDeletion(IAggregateRoot aggregateRoot, IUnitOfWorkRepository repository)
{
	if (!_deletedAggregates.ContainsKey(aggregateRoot))
	{
		_deletedAggregates.Add(aggregateRoot, repository);
	}
}

We only want to add those changes that haven’t been added before hence the ContainsKey guard clause.

In the commit method we ask the unit of work repository to persist those changes:

public void Commit()
{
	foreach (IAggregateRoot aggregateRoot in _insertedAggregates.Keys)
	{
		_insertedAggregates[aggregateRoot].PersistInsertion(aggregateRoot);
	}

	foreach (IAggregateRoot aggregateRoot in _updatedAggregates.Keys)
	{
		_updatedAggregates[aggregateRoot].PersistUpdate(aggregateRoot);
	}

	foreach (IAggregateRoot aggregateRoot in _deletedAggregates.Keys)
	{
		_deletedAggregates[aggregateRoot].PersistDeletion(aggregateRoot);
	}
}

At present we don’t care about transactions and rollbacks as a fully optimised implementation is not the main goal here. We can however extend the Commit method to accommodate transactions:

public void Commit()
{
    using (TransactionScope scope = new TransactionScope())
    {
         //foreach loops...
         scope.Complete();
    }
}

You’ll need to import the System.Transactions library for this to work.

So what does a unit of work repository implementation look like? We’ll define it as a base abstract class that each concrete repository must derive from. We’ll build up an example step by step. Add the following stub to the Repository layer:

public abstract class Repository<DomainType, IdType, DatabaseType> : IUnitOfWorkRepository where DomainType :  IAggregateRoot
{
	private readonly IUnitOfWork _unitOfWork;

	public Repository(IUnitOfWork unitOfWork)
	{
		if (unitOfWork == null) throw new ArgumentNullException("Unit of work");
		_unitOfWork = unitOfWork;
	}

	public void PersistInsertion(IAggregateRoot aggregateRoot)
	{
		
	}

	public void PersistUpdate(IAggregateRoot aggregateRoot)
	{
			
	}

	public void PersistDeletion(IAggregateRoot aggregateRoot)
	{
		
	}
}

There’s not much functionality in here yet, but it’s an important first step. The Repository abstract class implements IUnitOfWorkRepository so it will be the persistence workhorse of the data storage. It has three type parameters: DomainType, IdType and DatabaseType.

TypeId probably sounds familiar by now so I won’t dive into more details now. Then we distinguish between the domain type and the database type. The domain type will be the type of the domain class such as the Customer domain we’ve created. The database type will be the database representation of the same domain.

Why might we need a database type? It is not guaranteed that a domain object will have the same structure in the storage as in the domain layer. In the domain layer you have the freedom of adding, modifying and removing properties, rules etc. You may not have the same freedom in a relational database. If a data table is used by stored procedures and user-defined functions then you cannot just remove columns at will without breaking a lot of other DB logic. With the advent of NoSql databases, such as MongoDb or RavenDb, which allow a very loose and flexible data structure this requirement may change but at the time of writing this post relational databases are still the first choice for data storage. Usually as soon as a database is put into production and LIVE data starts to fill up the data tables with potentially hundreds of thousands of rows every day then the data table structure becomes a lot more rigid than your domain classes. Hence it can be a good idea to isolate the “domain” and “database” representations of a domain object. It is of course only the concrete repository layer that should know how a domain object is represented in the data storage.

Before we continue with the Repository class let’s simulate the database representation of the Customer class. This can be likened to the objects that the EF or Linq to SQL automation tools generate for you. Insert a new folder called Database in the Repository.Memory layer. Insert the following class into it:

public class DatabaseCustomer
{
	public int Id { get; set; }
	public string CustomerName { get; set; }
	public string Address { get; set; }
	public string Country { get; set; }
	public string City { get; set; }
	public string Telephone { get; set; }
}

This example shows an advantage with having separate domain and database representations. Recall that the Customer domain has an Address value object. In this example we imagine that there’s no separate Address table, we didn’t think of that when we designed the original database. As the database has been in use for some time it’s probably even too late to create a separate Address table so that we don’t interrupt the LIVE system.

However, we don’t care because we’ve allowed for this possibility by the type parameters. We’re free to construct and convert between domain and database objects in the concrete repository layer.

The next object we’ll need in the database is an imitation of the DB object context in EF and Linq to SQL. I realise that I may be talking too much about these specific ORM technologies but it probably suits the vast majority of data-driven .NET projects out there. Insert the following class into the Database folder:

public class InMemoryDatabaseObjectContext
{
	//simulation of database collections
	public List<DatabaseCustomer> DatabaseCustomers { get; set; }

	public InMemoryDatabaseObjectContext()
	{
		InitialiseDatabaseCustomers();
	}

	public void AddEntity<T>(T databaseEntity)
	{
		if (databaseEntity is DatabaseCustomer)
		{
                        DatabaseCustomer databaseCustomer = databaseEntity as DatabaseCustomer;
			databaseCustomer.Id = DatabaseCustomers.Count + 1;
			DatabaseCustomers.Add(databaseEntity as DatabaseCustomer);
		}
	}

	public void UpdateEntity<T>(T databaseEntity)
	{
		if (databaseEntity is DatabaseCustomer)
		{
			DatabaseCustomer dbCustomer = databaseEntity as DatabaseCustomer;
			DatabaseCustomer dbCustomerToBeUpdated = (from c in DatabaseCustomers where c.Id == dbCustomer.Id select c).FirstOrDefault();
			dbCustomerToBeUpdated.Address = dbCustomer.Address;
			dbCustomerToBeUpdated.City = dbCustomer.City;
			dbCustomerToBeUpdated.Country = dbCustomer.Country;
			dbCustomerToBeUpdated.CustomerName = dbCustomer.CustomerName;
			dbCustomerToBeUpdated.Telephone = dbCustomer.Telephone;
		}
	}

	public void DeleteEntity<T>(T databaseEntity)
	{
		if (databaseEntity is DatabaseCustomer)
		{
			DatabaseCustomer dbCustomer = databaseEntity as DatabaseCustomer;
			DatabaseCustomer dbCustomerToBeDeleted = (from c in DatabaseCustomers where c.Id == dbCustomer.Id select c).FirstOrDefault();
			DatabaseCustomers.Remove(dbCustomerToBeDeleted);
		}
	}

	private void InitialiseDatabaseCustomers()
	{
		DatabaseCustomers = new List<DatabaseCustomer>();
		DatabaseCustomers.Add(new DatabaseCustomer(){Address = "Main street", City = "Birmingham", Country = "UK", CustomerName ="GreatCustomer", Id = 1, Telephone = "N/A"});
		DatabaseCustomers.Add(new DatabaseCustomer() { Address = "Strandvägen", City = "Stockholm", Country = "Sweden", CustomerName = "BadCustomer", Id = 2, Telephone = "123345456" });
		DatabaseCustomers.Add(new DatabaseCustomer() { Address = "Kis utca", City = "Budapest", Country = "Hungary", CustomerName = "FavouriteCustomer", Id = 3, Telephone = "987654312" });
	}
}

First we have a collection of DB customer objects. In the constructor we initialise the collection values so that we don’t start with an empty one. Then we add insertion, update and delete methods for database type T to make it generic. These are somewhat analogous to the InsertOnSubmit, SubmitChanges and the DeleteOnSubmit methods from the Linq to SQL object context. The implementation itself is not too clever of course as we need to check the type but it’s OK for now. Perfection is not the goal in this part of the code, the automation tools will prepare a lot more professional code for you from the database. In the AddEntity we assign an ID based on the number of elements in the database. This is a simulation of the ID auto-assign feature of relational databases.

We’ll need access to this InMemoryDatabaseObjectContext in the repository layer. In a real system this object will be used intensively therefore access to it should be regulated. Creating a new object context for every single DB operation is not too clever so we’ll hide the access behind a thread-safe lazy singleton class. If you don’t know what these term mean, check out my post on the singleton design pattern. I won’t repeat the stuff that’s written there.

Insert the following abstract factory interface in the Database folder:

public interface IObjectContextFactory
{
	InMemoryDatabaseObjectContext Create();
}

The interface will be implemented by the following class:

public class LazySingletonObjectContextFactory : IObjectContextFactory
{
	public InMemoryDatabaseObjectContext Create()
	{
		return InMemoryDatabaseObjectContext.Instance;
	}
}

The InMemoryDatabaseObjectContext object does not have any Instance property yet, so add the following code to it:

public static InMemoryDatabaseObjectContext Instance
{
	get
	{
		return Nested.instance;
	}
}

private class Nested
{
	static Nested()
	{
	}
	internal static readonly InMemoryDatabaseObjectContext instance = new InMemoryDatabaseObjectContext();
}

If you don’t understand what this code does then make sure you read through the post on the singleton pattern.

We’ll need a reference to the abstract factory in the Repository object, so modify the private field declarations and the constructor as follows:

private readonly IUnitOfWork _unitOfWork;
private readonly IObjectContextFactory _objectContextFactory;

public Repository(IUnitOfWork unitOfWork, IObjectContextFactory objectContextFactory)
{
	if (unitOfWork == null) throw new ArgumentNullException("Unit of work");
	if (objectContextFactory == null) throw new ArgumentNullException("Object context factory");
	_unitOfWork = unitOfWork;
	_objectContextFactory = objectContextFactory;
}

At this point it’s also clear that we’ll need to be able to convert a domain type to a database type. This is best implemented in the concrete repository classes so it’s enough to add an abstract method in the Repository class:

public abstract DatabaseType ConvertToDatabaseType(DomainType domainType);

We can implement the Persist methods as follows:

public void PersistInsertion(IAggregateRoot aggregateRoot)
{
	DatabaseType databaseType = RetrieveDatabaseTypeFrom(aggregateRoot);
	_objectContextFactory.Create().AddEntity<DatabaseType>(databaseType);
}

public void PersistUpdate(IAggregateRoot aggregateRoot)
{
	DatabaseType databaseType = RetrieveDatabaseTypeFrom(aggregateRoot);
	_objectContextFactory.Create().UpdateEntity<DatabaseType>(databaseType);
}

public void PersistDeletion(IAggregateRoot aggregateRoot)
{
	DatabaseType databaseType = RetrieveDatabaseTypeFrom(aggregateRoot);
	_objectContextFactory.Create().DeleteEntity<DatabaseType>(databaseType);
}

private DatabaseType RetrieveDatabaseTypeFrom(IAggregateRoot aggregateRoot)
{
	DomainType domainType = (DomainType)aggregateRoot;
	DatabaseType databaseType = ConvertToDatabaseType(domainType);
	return databaseType;
}

We need to convert the incoming IAggregateRoot object to the database type as it is the database type that the DB understands. The DB type is its own representation of the domain so we need to talk to it that way.

So these are the persistence methods but we need to register the changes first. Add the following methods to Repository.cs:

public void Update(DomainType aggregate)
{
	_unitOfWork.RegisterUpdate(aggregate, this);
}

public void Insert(DomainType aggregate)
{
	_unitOfWork.RegisterInsertion(aggregate, this);
}

public void Delete(DomainType aggregate)
{
	_unitOfWork.RegisterDeletion(aggregate, this);
}

These operations are so common and repetitive that we can put them in this abstract base class instead of letting the domain-specific repositories implement them over and over again. All they do is adding the operations to the queue of the unit of work to be performed when Commit() is called. Upon Commit() the unit of work repository, i.e. the abstract Repository object will persist the changes using the in memory object context.

This model is relatively straightforward to change:

  • If you need a plain file-based storage mechanism then you might create a FileObjectContext class where you read to and from a file.
  • For EntityFramework and Linq to SQL you’ll use the built-in object context classes to keep track of and persist the changes
  • File-based NoSql solutions generally also have drivers for .NET – they can be used to implement a NoSql solution

So the possibilities are endless in fact. You can hide the implementation behind abstractions such as the IUnitOfWork interface or the Repository abstract class. It may well be that you need to add methods to the Repository class depending on the data storage technology you use but that’s perfectly acceptable. The concrete repository layer is…, well, concrete. You can dedicate it to a specific technology, just like the one we’re building here is dedicated to an in-memory storage mechanism. You can have several different implementations of the IUnitOfWork and IUnitOfWorkRepository interfaces and test them before you let your application go public. You can even mix and match the implementations:

  • Register the changes in a temporary file and commit them in NoSql
  • Register the changes in memory and commit them to the cache
  • Register the changes in cache and commit them to SQL Azure

Of course these combinations are very exotic but it shows you the flexibility behind all these abstractions. Don’t assume that the Repository class is a solution for ALL types of concrete unit of work. You’ll certainly have to modify it depending on the concrete data storage mechanism you work with. However, note the following points:

  • The rest of the application will not be concerned with the concrete data access implementation
  • The implementation details are well hidden behind this layer without them bubbling up to and permeating the other layers

So as long as the concrete implementations are hidden in the data access layer you’ll be fine.

There’s one last abstract method we’ll add to Repository.cs is the ubiquitous find-by-id method which we’ll delegate to the implementing classes:

public abstract DomainType FindBy(IdType id);

We’ll implement the CustomerRepository class in the next post.

View the list of posts on Architecture and Patterns here.

ultimatemindsettoday

A great WordPress.com site

Elliot Balynn's Blog

A directory of wonderful thoughts

Softwarearchitektur in der Praxis

Wissenswertes zu Webentwicklung, Domain-Driven Design und Microservices

Technology Talks

on Microsoft technologies, Web, Android and others

Software Engineering

Web development

Disparate Opinions

Various tidbits

chsakell's Blog

WEB APPLICATION DEVELOPMENT TUTORIALS WITH OPEN-SOURCE PROJECTS

Guru N Guns's

OneSolution To dOTnET.

Johnny Zraiby

Measuring programming progress by lines of code is like measuring aircraft building progress by weight.

%d bloggers like this: