Repository pattern is a common solution for encapsulating data access. See Fowler’s Pattern of Enterprise Application Architecture for further details. Because pattern is technology agnostic, many writing you’ll seen on the web are the way too abstract, lack of technological details. Below, I want to show you how to achieve repository pattern by using C#. Off course, this is not the best solution, if you have some glitch about my implementation, please leave a comment.
Let’s start with the simple sample. We want to implement a simple Point of Sales (POS) with these specifications:
1. System can save completed Sale.
2. For simplicity, system can only accept cash payment.
3. System can save Products data.
4. Sale contains one or more sales line item.
Here is proposed model:
If you have read Larman’s Applying UML and Pattern (great book, I recommend to read it if you hasn’t), chance are great that you’re familiar with those model. Here, I want to signify the differences. There is no Store class (or Ledger class) that stores completed Sales, nor there is a ProductCatalog that stores ProductSpecification s. In my solution, completed Sales are stored in SaleRepository (or SaleDao), not Ledger class (as in Larman’s) and as you may have guessed it well, there will be ProductRepository (or ProductDao) that stores ProductSpecification s, not ProductCatalog as in Larman’s. Oh well... quite a difference eh? Just slight name difference, and as Shakespeare would say, “What is an a name?” OK, that not just name difference. On Larman’s, ProductCatalog and Store is part of the domain, something that you’ll see on real store on the real world. On the other hand, ProductDao or SaleDao is not part of the domain, so if you ask the storekeeper which part in this store is something called ‘product Dao’, you’ll sent by the storekeeper to nearby asylum. I’ll discuss this deeply later (discuss the repository, not the asylumJ).
Here is the corresponding code for ProductSpecification:
public class ProductSpecification
{
public string Id { get; set; }
public string Name { get; set; }
public decimal UnitPrice { get; set; }
public string Description { get; set; }
}
Purist would say the code block above is some kind of anemic domain model (ADM), model that contains only data and no behavior at all. I, no purist nor ADM fan boy, as soon as I see appropriate behavior on ProductSpecification, I will put some methods there. Because this is just first iteration, there is no appropriate behavior sighted, just leave the model anemic.
Here is corresponding code for SalesLineItem:
public class SalesLineItem
{
public int Quantity { get; set; }
public ProductSpecification ProductSpecification { get; set; }
}
Leave it anemic again? No, because appropriate behavior sighted! SalesLineItem are good candidate to know what the subtotal is, so property called Subtotal should be there.
public decimal Subtotal
{
get { return this.ProductSpecification.UnitPrice * this.Quantity; }
}
Is the subtotal formula just shown above do as it should do? Note the price. The price is product’s current price, not the price when the item sold, so when ProductSpecification’s price changed, the subtotal should change too. The SalesLineItem must save the price when the item sold, so there must be UnitPrice attribute in SalesLineItem. Let’s add it:
public class SalesLineItem
{
public int Quantity { get; set; }
public decimal UnitPrice { get; set; }
public ProductSpecification ProductSpecification { get; set; }
public decimal Subtotal
{
get { return this.UnitPrice * this.Quantity; }
}
}
To make SalesLineItem even easier to use, let’s make UnitPrice automatically filled with product’s price when SalesLineItem.ProductSpecification changed:
public class SalesLineItem
{
private ProductSpecification productSpecification;
public int Quantity { get; set; }
public decimal UnitPrice { get; set; }
public ProductSpecification ProductSpecification
{
get { return productSpecification; }
set
{
this.productSpecification = value;
this.UnitPrice = productSpecification.UnitPrice;
}
}
public decimal Subtotal
{
get { return this.UnitPrice * this.Quantity; }
}
}
That’s enough for this simple scenario.
Here Payment class code:
public class CashPayment
{
public decimal Amount { get; set; }
}
Some of the reader may found this class rather confusing, why on earth does this class even exists? Why not just make this class as single attribute on Sale class? The reason is two words... what if? What if in the future this system goes larger and payment is not just cash, but credit card as well? When the entity is simple, make it simple, as simple as single attribute. When the entity is complex, make a single class, completed with attributes and complicated behaviors if any. Other reason why this class exists, I want to show you the concept called aggregate, something that will be discussed later on this article.
Last but not least, the Sale class code:
public class Sale
{
public string Id { get; set; }
public DateTime Date { get; set; }
public CashPayment Payment { get; set; }
public IList<SalesLineItem> LineItems { get; set; }
}
Note the LineItems collection type. The type is not List<SalesLineItem> but IList<SalesLineItem>, the interface that List<T> implements. This is one of the fundamental agile designs, called Dependency Inversion Principle (DIP). The type must be of type IList<T> rather than List<T> not because of it violates the DIP (I don’t even care if that thing is called DIP), but believe me, I can foreseen the very chaos if you violates it. Just one sample, if you use NHibernate as data access, NHibernate will use its own implementation of IList<T>, not Microsoft System.Collection.Generic.List<T> that we used to use on day basis. So, if you force yourself to use List<T> instead of IList<T>, your domain model is not compatible with NHibernate (which is not good). This is not about NHibernate—of course you’re free to say Entity Framework is far more superior to NHibernate— but this is about the modularity of your solution.
Now, what behavior does this class should have? Total is great candidate, so let’s add it:
public decimal Total
{
get { return this.LineItems.Sum(sli => sli.Subtotal); }
}
Consider Sale.Date. When Sale is instantiated, Sale.Date should represent current time and date. This logic is contained in Sale’s constructor:
public Sale()
{
this.Date = DateTime.Now;
this.LineItems = new List<SalesLineItem>();
}
Test Drive our Solution and Little Refactoring
Now, the time has come for a little testing. If you’re fan of TDD, testing are the majority of your work, you’ve done testing even before you create above classes. Because this is not about TDD session (I always perform TDD on domain model), we create classes first before testing it.
Suppose list of products listed in product catalog is:
| Identity |
Name |
Unit Price |
| P01 |
Momogi |
500 |
| P02 |
Pepsi |
5000 |
| P03 |
Mizone |
4500 |
We want to buy 2 Momogi, and 1 Pepsi, the code representation is:
public static void Main(string[] args)
{
var momogiSpec = new ProductSpecification { Id = "P01", Name = "Momogi", UnitPrice = 500 };
var pepsiSpec = new ProductSpecification {Id = "P02", Name = "Pepsi", UnitPrice = 5000};
var mizoneSpec = new ProductSpecification { Id = "P03", Name = "Mizone", UnitPrice = 4500 };
var momogiLineItem = new SalesLineItem {ProductSpec = momogiSpec, Quantity = 2};
var pepsiLineItem = new SalesLineItem {ProductSpec = pepsiSpec, Quantity = 1};
var sale = new Sale();
sale.LineItems.Add(momogiLineItem);
sale.LineItems.Add(pepsiLineItem);
Console.WriteLine("Dicky POS\n---------");
foreach (var lineItem in sale.LineItems)
Console.WriteLine("{0,-10}({1,8:c}) x {2,3} : {3,8:c}",
lineItem.ProductSpec.Name, lineItem.ProductSpec.UnitPrice,
lineItem.Quantity, lineItem.Subtotal);
Console.WriteLine("------------------------------------- +\nTotal is\t\t : {0,8:c}", sale.Total);
}
The output is below:
Dicky POS
---------
Momogi ( Rp500) x 2 : Rp1.000
Pepsi ( Rp5.000) x 1 : Rp5.000
------------------------------------- +
Total is : Rp6.000
The output is correct as expected. Next, what if we add three SalesLineItem, instead of two? To clarify this, see code below
var momogiLineItem1 = new SalesLineItem {ProductSpec = momogiSpec, Quantity = 1};
var pepsiLineItem = new SalesLineItem {ProductSpec = pepsiSpec, Quantity = 1};
var momogiLineItem2 = new SalesLineItem { ProductSpec = momogiSpec, Quantity = 1 };
var sale = new Sale();
sale.LineItems.Add(momogiLineItem1);
sale.LineItems.Add(pepsiLineItem);
sale.LineItems.Add(momogiLineItem2);
And the output is:
Dicky POS
---------
Momogi ( Rp500) x 1 : Rp1.000
Pepsi ( Rp5.000) x 1 : Rp5.000
Momogi ( Rp500) x 1 : Rp1.000
------------------------------------- +
Total is : Rp6.000
The total is correct, the item list is correct, but sale line item is not. Desired output is 2 Momogi and 1 Pepsi, not 1 Momogi, 1 Pepsi, and then 1 Momogi again. Yup, time for refactoring, but before that, let’s override each class’ Equals method. To define equality, we must refer to business rule, what conditions are two objects are considered equal? Below is the rule:
| Class |
Equality Properties |
| Sale |
Id |
| ProductSpecification |
Id |
| SalesLineItem |
ProductSpecification |
Before we implement equality, you must remember one important thing, if you override Equals method, you must also override GetHashCode method. Hash code is integer representation of your object, should be unique between different objects, and same number between same objects. Hash code is used extensively in collection that requires hashing, like IDictionary. When overriding Equals, why not implement IEquatable<T> too?
Equality for ProductSpecification:
public bool Equals(ProductSpecification obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
return Equals(obj.Id, Id);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != typeof (ProductSpecification)) return false;
return Equals((ProductSpec) obj);
}
public override int GetHashCode()
{
return (Id != null ? Id.GetHashCode() : 0);
}
Equality for Sale:
public bool Equals(Sale obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
return Equals(obj.Id, Id);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != typeof(Sale)) return false;
return Equals((Sale)obj);
}
public override int GetHashCode()
{
return (Id != null ? Id.GetHashCode() : 0);
}
Equality for SalesLineItem:
public bool Equals(SalesLineItem obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
return Equals(obj.productSpec, productSpec);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != typeof (SalesLineItem)) return false;
return Equals((SalesLineItem) obj);
}
public override int GetHashCode()
{
return (productSpec != null ? productSpec.GetHashCode() : 0);
}
If you use Resharper, you can generate equality (the code shown above) by pressing alt-insert then click the appropriate menu. That’s enough for equality, let’s back to our Sale class. We want to add same SalesLineItem to Sale will not add new SalesLineItem object, but we want Quantity property from existing SalesLineItem object in Sale.LineItems to be updated based on added SalesLineItem.Quantity. To clarify this, let’s add AddLineItem method to Sale class:
public void AddLineItem(SalesLineItem lineItem)
{
if (this.LineItems.Contains(lineItem)) // Contains use Equals method we've just overrided
this.LineItems.Where(li => li.Equals(lineItem)).First().Quantity += lineItem.Quantity;
else
this.LineItems.Add(lineItem);
}
Now, have we wanted to add new SalesLineItem to our Sale class, we should use this method instead of AddLineItem from collection (like we used before). So, just test if the code work as expected:
var momogiLineItem1 = new SalesLineItem {ProductSpec = momogiSpec, Quantity = 1};
var pepsiLineItem = new SalesLineItem {ProductSpec = pepsiSpec, Quantity = 1};
var momogiLineItem2 = new SalesLineItem { ProductSpec = momogiSpec, Quantity = 1 };
var sale = new Sale();
//sale.LineItems.Add(momogiLineItem1); sale.LineItems.Add is depreceated, use sale.AddLineItem instead
//sale.LineItems.Add(pepsiLineItem);
//sale.LineItems.Add(momogiLineItem2);
sale.AddLineItem(momogiLineItem1);
sale.AddLineItem(pepsiLineItem);
sale.AddLineItem(momogiLineItem2);
When we execute the program, the output screen is:
Dicky POS
---------
Momogi ( Rp500) x 2 : Rp1.000
Pepsi ( Rp5.000) x 1 : Rp5.000
------------------------------------- +
Total is : Rp6.000
Yes, the output is the same as we desired.
What we’ve done
So far, we’ve created the domain model which diagram shown below. The domain model is not anemic because we added some behavior to it when necessary. Notice CashPayment now can handle return, implemented in CashBack properties. The implementation is left as exercise for you, admirable readerJ. Thing we have not yet done is, where to store the domain model? That’s the use of repository, which we discuss on next part of this article. Yup, until next article then and don't forget to download the solution (see attachment link below).
