maandag 25 februari 2013

DTO's with parameterized constructors

At my current project we use dto's to transfer data from an MVC.Net controller to the domain services, like this:

public class HomeController : Controller
{
   public ActionResult Index()
   {
      var dto = new SomeDto();
      dto.SomeValue = "foo";

      var domainService = new DomainService();
      domainService.DoSomething(dto);
   }
}


One problem with the CustomDto is that is doesn't coerce that the SomeValue property is filled. We can do this, and the compiler says it's ok:


var dtonew CustomDto();
// dto.SomeValue = "foo";

var domainServicenew DomainService();
domainService.DoSomething(dto);

Ofcourse when we run the code we will get an exception if domainService.DoSomething expects the dto.SomeValue to not be null.

The real danger

The real danger of not having a constructor with paramaters is this:

We have the DomainService:



public class DomainService
{
   public void DoSomething(SomeDto dto)
   {
      // pseudo: do something with dto.SomeValue
   }
}

And we have a unit test:


[TestClass]
public class Tests
{
   [TestMethod]
   public void Test1()
   {
      var dto = new SomeDto();
      dto.SomeValue = "foo";

      var domainService = new DomainService();
      domainService.DoSomething(dto);
   }
}


Now we decide that for DoSomething to work, it needs an extra property on SomeDto:, so we add SomeOtherValue:


public class SomeDto
{
   public string SomeValue{get;set;}
   public string SomeOtherValue{get;set;}
}


And we add that to the unit test:



[TestClass]
public class Tests
   {
      [TestMethod]
      public void Test1()
      {
         var dto = new SomeDto();
         dto.SomeValue = "foo";
         dto.SomeOtherValue = "bar";

         var domainService = new DomainService();
         domainService.DoSomething(dto);
   }
}

And we change the implementation of DoSomething:

public class DomainService
{
   public void DoSomething(SomeDto dto)
   {
      // pseudo: do something with dto.SomeValue
      // pseudo: and we do something with dto.SomeOtherValue
   }
}

Is we compile the code, it will succeed. If we run the test it will succeed. But it's not untill we start debugging or deploy the code that we find out that the code is broken, because the Index method of the HomeController doens't supply SomeOtherValue to the dto.

Fortunately there's an easy solution to this problem, and that is:


A constructor with parameters



public class SomeDto
{
   public SomeDto(string someValue, string someOtherValue)
   {
      SomeValue = someValue;
      SomeOtherValue = someOtherValue;
   }

   public string SomeValue{get;set;}
   
   public string SomeOtherValue{get;set;}
}

(You can also as an addition choose to make the setters not public.)

Now we have to change our test like this:




[TestClass]
public class Tests
{
   [TestMethod]
   public void Test1()
   {
      var dto = new SomeDto("foo", "bar");

      var domainService = new DomainService();
      domainService.DoSomething(dto);

   }
}

If we compile the code the compiler returns an error, because the  Index method of the HomeController doesn't use the constructor. So we have to change that code too:


public class HomeController : Controller
{
   public ActionResult Index()
   {
      var dto = new SomeDto("foo", "???");

      var domainService = new DomainService();
      domainService.DoSomething(dto);
   }
}


Conclusion

Using a parameterized constructor protects us against overlooking parts of code that has to be changed when making changes to other parts.

Geen opmerkingen:

Een reactie posten