A model SOA application in .NET Part 3: the repository

In the previous post we built the thin domain layer of the model application. Now it’s time to build the repository.

The abstract repository

Open the SoaIntroNet application we started building previously. Add a new interface in the SoaIntroNet.Domain.ProductDomain folder:

public interface IProductRepository
{
	Product FindBy(Guid productId);
	void Save(Product product);
}

This is a very simplified repository interface but it suffices for our demo purposes. In case you’re wondering what the purpose of this interface is and what it is doing in the domain layer then make sure to at least skim through the series on Domain-Driven-Design here. Alternatively you can go through the solution structure of the series about the cryptography project starting here. This latter project follows a simplified repository pattern that we’ll employ here.

The concrete repository

The repository will be a simple in-memory repository so that we don’t need to spend our time on installing external storage mechanisms.

Let’s build the infrastructure around the concrete repository. Add a new C# library project called SoaIntroNet.Repository to the solution. Add a new folder called ProductRepository. Add the following stub to the class:

public class InMemoryProductRepository : IProductRepository
{
	public Product FindBy(Guid productId)
	{
		throw new NotImplementedException();
	}

	public void Save(Product product)
	{
		throw new NotImplementedException();
	}		
}

We’ll use the thread-safe singleton design pattern to build an instance of this object. Add the following code to the class:

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

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

If you don’t understand what this means then make sure to check out the post on the singleton pattern. It ensures that we always return the same instance of the concrete repository.

Before we get rid of the NotImplementException bits we’ll take care of the rest of the initialisation elements. Add the following interface to the folder:

public interface IProductRepositoryFactory
{
	InMemoryProductRepository Create();
}

The following class implements the above interface:

public class LazySingletonProductRepositoryFactory : IProductRepositoryFactory
{
	public InMemoryProductRepository Create()
	{
		return InMemoryProductRepository.Instance;
	}
}

We don’t want to start with an empty “database” so add the following fields, properties, a constructor and an initialisation method to the class:

private int standardReservationTimeoutMinutes = 1;
public List<Product> DatabaseProducts { get; set; }
public List<ProductPurchase> DatabaseProductPurchases { get; set; }
public List<ProductReservation> DatabaseProductReservations { get; set; }

public InMemoryProductRepository()
{
	InitialiseDatabase();
}

private void InitialiseDatabase()
{
	DatabaseProducts = new List<Product>();
	Product firstProduct = new Product()
	{
		Allocation = 200,
		Description = "Product A",
		ID = Guid.Parse("13a35876-ccf1-468a-88b1-0acc04422243"),
		Name = "A"
	};
	Product secondProduct = new Product()
	{
		Allocation = 500,
		Description = "Product B",
		ID = Guid.Parse("f5efdfe0-7933-4efc-a290-03d20014703e"),
		Name = "B"
	};
        DatabaseProducts.Add(firstProduct);
	DatabaseProducts.Add(secondProduct);

	DatabaseProductPurchases = new List<ProductPurchase>();
	DatabaseProductPurchases.Add(new ProductPurchase(firstProduct, 10) { Id = Guid.Parse("0ede40e0-5a52-48b1-8578-de1891c5a7f0") });
	DatabaseProductPurchases.Add(new ProductPurchase(firstProduct, 20) { Id = Guid.Parse("5868144e-e04d-4c1f-81d7-fc671bfc52dd") });
	DatabaseProductPurchases.Add(new ProductPurchase(secondProduct, 12) { Id = Guid.Parse("8e6195ac-d448-4e28-9064-b3b1b792895e") });
	DatabaseProductPurchases.Add(new ProductPurchase(secondProduct, 32) { Id = Guid.Parse("f66844e5-594b-44b8-a0ef-2a2064ec2f43") });
	DatabaseProductPurchases.Add(new ProductPurchase(secondProduct, 1) { Id = Guid.Parse("0e73c8b3-f7fa-455d-ba7f-7d3f1bc2e469") });
	DatabaseProductPurchases.Add(new ProductPurchase(secondProduct, 4) { Id = Guid.Parse("e28a3cb5-1d3e-40a1-be7e-e0fa12b0c763") });

	DatabaseProductReservations = new List<ProductReservation>();
	DatabaseProductReservations.Add(new ProductReservation(firstProduct, standardReservationTimeoutMinutes, 10) { HasBeenConfirmed = true, Id = Guid.Parse("a2c2a6db-763c-4492-9974-62ab192201fe") });
	DatabaseProductReservations.Add(new ProductReservation(firstProduct, standardReservationTimeoutMinutes, 5) { HasBeenConfirmed = false, Id = Guid.Parse("37f2e5ac-bbe0-48b0-a3cd-9c0b47842fa1") });
	DatabaseProductReservations.Add(new ProductReservation(firstProduct, standardReservationTimeoutMinutes, 13) { HasBeenConfirmed = true, Id = Guid.Parse("b9393ea4-6257-4dea-a8cb-b78a0c040255") });
	DatabaseProductReservations.Add(new ProductReservation(firstProduct, standardReservationTimeoutMinutes, 3) { HasBeenConfirmed = false, Id = Guid.Parse("a70ef898-5da9-4ac1-953c-a6420d37b295") });
	DatabaseProductReservations.Add(new ProductReservation(secondProduct, standardReservationTimeoutMinutes, 17) { Id = Guid.Parse("85eaebfa-4be4-407b-87cc-9a9ea46d547b") });
	DatabaseProductReservations.Add(new ProductReservation(secondProduct, standardReservationTimeoutMinutes, 3) { Id = Guid.Parse("39d4278e-5643-4c43-841c-214c1c3892b0") });
	DatabaseProductReservations.Add(new ProductReservation(secondProduct, standardReservationTimeoutMinutes, 9) { Id = Guid.Parse("86fff675-e5e3-4e0e-bcce-36332c4de165") });

	firstProduct.PurchasedProducts = (from p in DatabaseProductPurchases where p.Product.ID == firstProduct.ID select p).ToList();
	firstProduct.ReservedProducts = (from p in DatabaseProductReservations where p.Product.ID == firstProduct.ID select p).ToList();

	secondProduct.PurchasedProducts = (from p in DatabaseProductPurchases where p.Product.ID == secondProduct.ID select p).ToList();
	secondProduct.ReservedProducts = (from p in DatabaseProductReservations where p.Product.ID == secondProduct.ID select p).ToList();
}

We have 2 products with a couple of product purchases and reservations in our data store.

The lazy singleton implementation will make sure that the initialisation code only runs once so we’ll have access to the initial set of data every time we need a concrete repository.

The implementation of FindBy is very simple:

public Product FindBy(Guid productId)
{
	return (from p in DatabaseProducts where p.ID == productId select p).FirstOrDefault();
}

The implementation of Save is not much harder either:

public void Save(Product product)
{
	ClearPurchasedAndReservedProducts(product);
	InsertPurchasedProducts(product);
	InsertReservedProducts(product);
}

…which calls the following three private methods:

private void ClearPurchasedAndReservedProducts(Product product)
{
	DatabaseProductPurchases.RemoveAll(p => p.Id == product.ID);
	DatabaseProductReservations.RemoveAll(p => p.Id == product.ID);
}

private void InsertReservedProducts(Product product)
{
	DatabaseProductReservations.AddRange(product.ReservedProducts);
}

private void InsertPurchasedProducts(Product product)
{
	DatabaseProductPurchases.AddRange(product.PurchasedProducts);
}

In the Save method we want to concentrate on updating the product reservations and purchases and ignore the changes in the Product domain itself. Updating product reservations and purchases line by line is a cumbersome task. It’s easier to remove all existing purchases and reservations first and insert the old ones along with the new ones. The existing product purchases and reservations are always populated correctly in the InitialiseDatabase() method. Therefore a call to FindBy will product a product with the existing purchases and reservations.

So we now have the Domain and the Repository layer ready – we’ll build the service layer in the next solution.

View the list of posts on Architecture and Patterns here.

Advertisement

About Andras Nemes
I'm a .NET/Java developer living and working in Stockholm, Sweden.

One Response to A model SOA application in .NET Part 3: the repository

  1. Pingback: Architecture and patterns | Michael's Excerpts

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Elliot Balynn's Blog

A directory of wonderful thoughts

Software Engineering

Web development

Disparate Opinions

Various tidbits

chsakell's Blog

WEB APPLICATION DEVELOPMENT TUTORIALS WITH OPEN-SOURCE PROJECTS

Once Upon a Camayoc

Bite-size insight on Cyber Security for the not too technical.

%d bloggers like this: