Demystifier Dicky

A blogs dedicated for simple easy followed tutorial.

This is My Style of Domain Model and Repository Pattern, What’s Your? (Part I)

 
 

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:

First proposed UML 

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 (ProductSpec
ification)) 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 (Sale
sLineItem)) return false;   
   
return Equals((Sale
sLineItem) 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).

Final Class Diagram

Share this post: | | | |
Published Feb 28 2009, 02:00 PM by Dicky Arinal
Attachment: Pos.zip

Comments

 

fngaserin said:

A great article.

Just a few things, in my humble opinion that I want to comment. Repeating the code above:

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; }  

   }

}

On the above implementation, I see a bit of issue, depending on whether when the unit price in the product specification is update, the unit price in the line item should be updated too? If it should, then it's safer to actually implement the UnitPrice property to return productSpecification.UnitPrice instead.

Second thing, regarding to the implementation of CashPayment on Sale. If you want to provide extensibility, we can always break it down to two properties in Sale: Amount and PaymentType. However, if you do want that Payment able to do something else, instead of using concrete class, you should use an abstract class or interface instead.

March 4, 2009 3:40 AM
 

fngaserin said:

One more thing to add up...

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);

The code above still doesn't ensure other people won't call

sale.LineItems.Add(pepsiLineItem);  

A better approach will be creating a LineItemCollection class which will only provide AddLineItem, RemoveLineItem, etc to ensure that no one will access the IList directly.

Just my $0.02

March 4, 2009 3:49 AM
 

Dicky Arinal said:

@fngaserin

No, SalesLineItem's price should not change when ProductSpec changed, I've a typo here.

Yes, to provide extensibility, we can implement payment as abstract class, and yes, better approach is to make our implementation of ICollection on SalesLineItem, but I wouldn't do that in this simple tutorial. I want to make the domain as simple as possible.

Thanks for the comment.

Btw, semoga tidak bingung lagi dalam milih bahasa dalam nulis blog.. ok?

March 7, 2009 12:12 AM
 

ronaldwidha said:

Domain modelnya sangat bagus, tapi sepertinya rekan Dicky belum membahas lebih lanjut ttg repository patternnya sendiri.

Yang saya pengen berbagi pikiran, di repository class anda, bagaimna cara rekan Dicky untuk meng-hydrate

relational properties seperti:

public class Sale

{   ...

   public IList<SalesLineItem> LineItems { get; set; }

}

Dengan NHibernate, ActiveRecords maupun EF, pengisian objek SalesLineItem sudah dihandle oleh framework. Kebanyakan mengandalkan Fowler's Lazy Load untuk cuma me-load on demand.

Bagaimana menurut anda?

March 18, 2009 8:26 PM