View Blog

The Great Library - Part 3 - Domain Layer, Domain Driven Design, and Test Driven Development

Aug25

Written by:
8/25/2011 9:12 PM RssIcon


We left off Part 2 having outlined a skeleton architecture.  Today, we will start on the heart of the system, the Business Layer.  We’ll also briefly cover the concepts of Domain Driven Design (DDD) and Test Driven Development (TDD).  Finally, we’ll expand on the solution and write some code that incorporates these two points.  As usual, we will build upon the Great Library project.

Domain Driven Design

Domain Driven Design is a way of developing software that focuses on the domain and domain logic. The design starts with the model; not with the UI, services, or database.  This is a major paradigm shift from the typical data driven enterprise system.  Let’s compare the two:

 

Domain Expert: When an Access Centre shuts down, we need to move all the Goods to the nominated Regional Access Warehouse.

Data Driven Developer: Oh, that’s easy.  We can just update the FK in the Goods table to the ID of the Regional Access Centre.

Domain Driven Design Developer: Ok.  We’ve earlier implemented a way for Access Centres to transfer Goods between them.  So, we can simply say that when an Access Centre shuts down we transfer all the Goods to the Regional Access Centre.


In Data Driven response, the tendency is simply to look at the end result. In this case, the persistence layer is simply switching which Access Centres have what Goods.  However, there is a disconnection between the Domain Experts and the Developers.   The solution described is purely technical, something the Expert probably will not understand. 

In the Domain Driven Design response, the language used is that of the Domain Expert.  This is called Ubiquitous Language, and is a core concept of DDD.  From a technical view, we can see that there are some functions that handle the transfer in code (as opposed to a single UPDATE statement).  This allows a powerful mechanism to handle all the logic in the domain.

Neither design strategy is dominant.  The initial time investment in DDD tends to be more expensive, but follows a more linear development curve.  Many tools are available for Data Driven development, but tend to box in the developer.  We will be continuing with DDD throughout the Great Library.

Test Driven Development

Test Driven Development is the practice of writing tests first then writing your code.  The benefits are numerous, including:
  • Actually having tests
  • It makes you think about the task at hand
  • Concrete proof that what is coded works
  • Added confidence in adding, modifying, or refactoring code
  • The steps in practicing TDD help in developing loosely coupled and SOLID code
The steps in TDD are simple:
  1. RED – Write a test that satisfies a goal.  The test does not have to compile, and ideally shouldn’t work if it does compile.
  2. GREEN – Write code so that the test becomes satisfied, even if the code isn’t perfect.
  3. REFACTOR – Clean up the code, ensuring all tests still pass. 

Using DDD and TDD together

Ok, now that we have a basic understanding of DDD and TDD let’s blend them all together and make something practical.  We’ll start by creating code to do the following:
  • Allow a Good to be added to the inventory of an Access Centre

Let’s start off and add a Test Project to our solution.  To do this:

  1. Highlight the Tests solution folder, Right-Click -> Add new Project.
  2. Under Test Projects -> Test Documents select Test Project. 
  3. Name it GreatLibrary.DomainEntities.Tests, as this will be our test bed for Domain Entities.
  4. Rename the one generated class file, UnitTest1.cs, to AccessCentreTest.cs.

When Unit Tests are run, each class marked by the [TestClass] attribute will run a test on any method with the [TestMethod] attribute.   Expressions are tested using the Assert class, and all Asserts must pass for a method to pass its test.  Also, and obviously, each method must Assert at least once to pass.

We’ll add the following TestMethod: 

[TestMethod]
public void CanAddGoodToInventory()
{
    Good good = new Good();
    AccessCentre accessCentre = new AccessCentre();
 
    accessCentre.AddToInventory(good);
 
    bool expected = true;
    bool actual = accessCentre.Inventory.Contains(good);
 
    Assert.AreEqual(expected, actual, "The Good could not be found in Inventory");
}

Right now, we can’t compile as Goods and Access Centres don’t even exist. This follows with the RED step in TDD. So, we’ll move onto GREEN by adding class library to our solution.

  1. Highlight the "03. Domain Layer" solution folder, Right-Click -> Add new Project. Note: Astute readers will see that I renamed this from the last lesson (it was 03. Business Layer… my bad!).
  2. Under Visual C# -> select Class Library. Name it GreatLibrary.DomainEntities.
  3. Rename the one generated class file, Class1.cs, to Good.cs. Also, change the name of the class to Good.
  4. Add a new class called AccessCentre.cs.
  5. Add a reference to this new project in GreatLibrary.DomainEntities.Tests.
Our test hinges on four things: The existense of a Good class; the existence of an AccessCentre class; a method called AddToInventory for Access Centre; and a property called Inventory in AccessCentre. Right now, we only have the final two remaining pieces to be coded. We can satisfy that with the following code:
public class AccessCentre
{
  
    #region Properties
  
    public IList Inventory { get; private set; }
  
    #endregion
  
    #region Methods
  
    public void AddToInventory(Good good)
    {
         
    }
    #endregion
}

So!  Let’s run our test!  You can click on Test->Run->All Tests in Solution, or you can simply press CTRL R, A.  And voila!  An exception!  We didn’t initialize the List.  Let’s change that and try again.

public AccessCentre()
{
    Inventory = new List();
}



We didn’t get an exception, but our test failed.  The Error Message says that it couldn’t find the Good in the Inventory.  Seeing as the AddToInventoryMethod didn’t actually do anything we can handle be surprised.  Let’s fix it by doing something in the AddToInventory method:

public void AddToInventory(Good good)
{
    Inventory.Add(good);
}


We now get a nice green checkmark.  

Quick Notes

Why did I use a private setter in the Access Centre class?  This is a design practice for DDD as classes interact through verbs, not by nouns.  The AddToInventory method accomplishes the set.  Saying that it has a private setter allows us to modify the property in the issuing class, but it remains hidden from the rest of the world.

Summary

Today we learned about Domain Driven Design and Test Driven Development.  In DDD, we learned that we develop the model first, developers speak the same ubiquitous language as domain experts, and we learned entities communicate via verbs and not nouns.  In TDD, we learned about the Red/Green/Refactor cycle, and some of its benefits.  Finally, we put together all these aspects by adding a class library and it's corresponding Unit Test project to the Great Library solution.

Next Post

We are currently at ChangeSet 8021, and so far, we’ve barely done any coding.  Importantly, however, we’ve learnt some great concepts.  In the next post, we are going to expand on our knowledge of Domain Driven Design and really hammer down some code.



Your name:
Gravatar Preview
Your email:
(Optional) Email used only to show Gravatar.
Your website:
Title:
Comment:
Security Code
CAPTCHA image
Enter the code shown above in the box below
Add Comment  Cancel