DDD & TDD. part I

Disclaimer

Article is a simple presentation of DDD and TDD in order to show how to deal with it and give an opportunity to start development according describing techniques. Those who already practice TDD and DDD,  please don’t be strict to words.

Magic abbreviations

DDD – Domain Driven Design, in few words it is a solution code manner. And your target is an extract main application logic, the core, in a separate independent module. Domain must be independent from specific technologies, it won’t relay on underlying data, graphical presentation and so on.

TDD – Test Driven Design, developing application by tests. “Test become before code”.

Both of these practices (maybe better to call it philosophy) come together in my projects and, to be honest, for me it’s hard to imagine their usage separately. But yes, you can use them absolutely separate and no one oblige you to apply them in every project together.

There is a dozen of books, articles and other stuff in any format and size. Somewhere it is simpler, somewhere with more complex vision. I wouldn’t like  to make a  historical excursion how DDD and TDD have been invented, because it’s easy to find out in web, to  google Eric Evance books or visit Martin Fowler site. I’d like to share my vision of subject in simple words. Tell how it helps, works, evolves and supports. I hope I can achieve the goal.

Okay, let’s take a closer tour by these techniques.

Domain Driven Design on example

Assume that we have a little task. The task is to count students attendance on lessons and track their achievements during semesters. I’ll try to define task in a project holder manner in a very common words.

There is a school with classes and students. We try to populate classes in range from 15 to 25 students per class. Learning plan formed for 6 days working week except junior school (5 day work week). There are 50 teachers for 16 learning subjects at all. Student’s result can be measured by 5 grade system.

This is it. Do you agree that this is a very high level description? But according this information we can already build backbone of a domain, by answering on following questions.

  • What is the most important?
  • How information is organized?

Let’s try to find out answers.

DDD  offers you to get noun as a foundation of domain’s classes and verbs as a domains services. Domain classes must be able to work with themselves only. They shouldn’t have any complex methods that allow changing class instance. They should delegate this kind of work to domain services.

So then:

There is a school with classes and students. We try to populate classes in range from 15 to 25 students per class. Learning plan formed for 6 days working week except junior school (5 day work week). There are 50 teachers for 16 learning subjects at all. Student’s result can be measured by 5 grade system.

I make bold the key none and phrase, that looks for me the most important and which will form the core of application, the domain. So we have classes:  School, Class, Student, Teacher, Subject, Mark.

There can be some doubt about necessary of the Mark class, that is consists of from the only one field on a first sight. But I think it will be more complex and I try to reveal it during the post.

Now it’s interesting how all these classes are connected between each other.

From the text we can see that the main aggregate is a school.

  • School aggregate classes
  • Class aggregate students
  • School aggregate teachers
  • School aggregate subject

If we perform the statements in diagram, we’ll get scheme below:

The next step is to find out relation between students and subjects. From my perspective, there must be artificial link not covered in the task. The mentioned relationship will be “many to one”, because of students learn different subjects. Logical, isn’t it? As a result student’s marks can be define by set of student+subject. I mean, we can’t make that Student aggregate marks, because in this implementation it’s impossible to determine the link mark – subject.  Using the same argumentation we can’t make that Subject aggregate marks. Taking in account all these reasons, scheme transforms to:

On current time point the scheme looks adequate for me. Please email me if you have any questions, don’t hesitate.

Hmm… Scheme is accurate until no fields are displayed. Try to fill classes with necessary properties (fields). I think that some ideas about scheme improvement should appear in your mind during filling classes ;). Progress inspired by human’s laziness. And this is the reason why so lazy to write similar code for classes Student and Teacher. We can extract common base class Person, where will be placed main information about human.

 public class Person {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string MiddleName { get; set; }
    }

public class Student : Person { … }

public class Teacher : Person { … }

Thus, the final result is:

Strict and clear!

At this moment in code we have declared properties and fields, but there are no any references between classes. I think now is the best moment to start conversation about TDD. I can somehow imagine how will look method’s signatures that are going to connect our classes. Being experienced enough you can imagine it also.  But not with 100% confidence.

Test Driven Development

Officially TDD programming methodic tells that program writing brakes on little cycles. Within each cycle you should write a small test on one simple action. At beginning test must be red (i.e. not meet assert conditions), then we make it works by developing the simplest possible way – test must be green. After all perform refactoring to improve code and run test once again to be sure that nothing was broken. This cycle called “Red-Green-Refactor”. Ideally you should spent less than 15 minutes on each test. Tests must be extremely fast, all project’s tests should passed in one minute; otherwise you won’t run them at all. For a big project passing time can be about 5 minutes.

There can raise reasonable question: “How do I write test on something that not exist?” Keep patience, I’m going to show and describe everything on the example.

Create new “Test project”. Right click on a Solution, add new project (Add > New project), in appeared dialog window select “Test”, set project’s name and press “OK”. As a result of these actions we have a new project with 3 auto-generated files. Delete them with no mercy.

Now you have to add reference to tested project. Build solution. Select the Test project, right click on it and select “Add reference…”. Click on the “Project” tab in the appeared window, select “Domain.dll”.  So, now we can refer to domain’s classes from the Test project. In whole, everything is ready to test writing.

The first test will be about ability to add a new Class to a School class. Add a new class to the Test project, rename new class to “SchoolTests”. The start position is:

namespace Tests {
     public class SchoolTests {}
}

In order to make it “Test” you have to add TestClass attribute and mark new method with attribute TestMethod. Like this:

using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Tests {
[TestClass]
public class SchoolTests {
    [TestMethod]
    public void Test() {

      }
  }
}

Don’t forget about using in further test classes. Now we can write a test’s body where will be described mentioned situation. I suggest renaming test method to something more understandable, which can describe a test’s main idea and purpose.

[TestMethod]
public void ShouldAddClass() {
    var school = new School();
    var testClass = new Class();
    school.AddClass(testClass);

    Assert.AreEqual(1, school.Classes.Count);
    Assert.AreEqual(testClass, school.Classes[0]);
}

If you use ReSharper, you can see that AddClass highlighted red – method not recognized. Yes, the AddClass method not exists and the first thing is to make solution able to compile. Create necessary method as a blank.

Assert state is the main player it the test. These statements tell us is test passed or not, were necessary work performed or some expected event triggered. In our case we test, that collection has one element and this element exact the same that we put in collection.

Blank method is:

public class School {
public void AddClass(Class clas) {

  }
}

Build solution, check that there are no errors and run test.

After few moments you should see the Test Result tab and result of tests.

Test failed. You should find out the reason of “red” test. Was it failed by “right” reason, in the way how we expect it falls, or happened something unexpected. In current situation the “right” reason will be 0 elements in collection instead of 1.

Double click on test row and read the reason (Error Message):

Test method Tests.SchoolTests.ShouldAddClass threw exception:  System.NullReferenceException: Object reference not set to an instance of an object..

This message explicitly says that something was not initialized during attempt to get value. Great! It’s not exactly what we expected, but also reason to be glad. Let’s write another one test that checks variable initialization in a School class.

[TestMethod]
public void SchoolInitialized() {
var school = new School();

Assert.AreEqual(0, school.Classes.Count);
Assert.AreEqual(0, school.Teachers.Count);
Assert.AreEqual(0, school.Subjects.Count);
}

Run test and you’ll see error message: “Assert.NotNull failed. Actual:<(null)>” – this is good error message. Go to the School class and init properties.

public class School {
    private readonly List<Class> classes = new List<Class>();
    private readonly List<Teacher> teachers = new List<Teacher>();
    private readonly List<Subject> subjects = new List<Subject>();

public ReadOnlyCollection<Class> Classes {
    get { return classes.AsReadOnly(); }
}

public ReadOnlyCollection<Teacher> Teachers {
    get { return teachers.AsReadOnly(); }
}

public ReadOnlyCollection<Subject> Subjects {
    get { return subjects.AsReadOnly(); }
}

public void AddClass(Class @class) {}
}

Run test “SchoolInitialized” once again and now it’s green! Launch the first test and now we should see that collection’s length doesn’t match.  Exactly what we expect! Now we can write method implementation for the AddClass method:

public void AddClass(Class @class) {
    classes.Add(@class);
}

Run test and see that it became green also!

We’ve performed full cycle of test writing. Write a test, make it compile, write code if necessary to get expected error. After write product code to make test green. Refactor code (extract similar parts, optimize code, etc.), run all tests again to be sure that nothing broke.

Where is a profit?

The main goal is to be sure that critical code parts works exactly as you think of them. No, it doesn’t mean that everything works right =) Tests shows that everything works as planned.

Quite often during test writing I discover new threats and possibilities, additional verification. For example, in our case about the School class I got idea about checking double adding same Class in School. Or that we should tell somehow to the Class that now it belongs to a School. All this cases can be covered by tests.

Tests help me to write only a really necessary code to make application work. I’ve read somewhere that only 20% of code do real job, other 80% almost never used. But we can spent a lot of time and power that can be applied in other areas.

Tests don’t leave me a chance to write uncomfortable in use a code. I’m too much lazy to write complex functions, methods, converters and so on. I want to write a code as fast and easy as possible (in reasonable manner) and as a result I’d like to use fewer methods. Via tests I create API, that easy to use in a product code.

If I have a test, I don’t feel fear to change an exists code and refactor it. I can improve and rewrite code, but I know that tests help to localize and fix errors. Of course it works only if you write test with understanding how it works.

In next part I’m going to tell you how to work with domain’s services and how to test them.

Source code

Hard’n’heavy!

Tagged , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>