Data Transfer Objects Pattern

Data Transfer Objects (DTOs) are a necesarry time-sink when working with SOA.  Proper SOA dictates that you must not pass domain objects across service boundaries.  In many cases, such as when using an ORM, it is extremely difficult to pass your domain objects across the service boundaries even if you choose to disreguard the SOA guidance.  Also, there may be good reasons why you do not want to make your domain objects serializable.

DTOs Defined

So what exactly are DTOs?  They are objects that have no behavior outside of accessors and mutators of their properties.  Often times DTOs line up one-to-one with domain objects as in the following example.

/// <summary>

/// Domain Object

/// </summary>

public class Company
{
private string name;
private string taxId;

public Company(string name, string taxId)
{
this.name = name;
this.taxId = taxId;
}

public string Name { get { return name; } }
public string TaxId { get { return taxId; } }
}

/// <summary>
/// Data Transfer Object
/// </summary>
public class CompanyDTO
{
public string Name { get; set; }
public string TaxId { get; set; }
}

Other times DTOs may need to carry data for a request that has no direct representation as a single object in the domain.  Consider a service that needs to expose data about a company and its owner.  Obviously the clients shouldn't have to request data about the company and then data about the owner in a separate request.  Doing so would incur the costs of two round trips, and the owner data is a part of a single logical unit as exposed by the service.

/// <summary>

/// A company
/// </summary>
public class Company
{
private string name;
private string taxId;

public Company(string name, string taxId)
{
this.name = name;
this.taxId = taxId;
}

public string Name { get { return name; } }
public string TaxId { get { return taxId; } }

public Owner Owner { get; set; }
}

/// <summary>
/// Owner of a company
/// </summary>

public class Owner
{
public Company Company { get; set; }

public string FirstName { get; set; }
public string LastName { get; set; }
}

/// <summary>
/// Data Transfer Object

/// </summary>
public class CompanyDTO
{
public string Name { get; set; }
public string TaxId { get; set; }

public string OwnerName { get; set; }
}

From Domain Object to DTO and Back Again

The assembler pattern, a subset of the mapping pattern, is used to translate DTOs to and from domain objects.



It is possible to create a reusable conventions based assembler.  I will talk more about this in the future.  For small projects, the cost of creating assemblers for the DTOs is trivial.  For larger ones, it is still worth the effort for the sake of encapsulation.

/// <summary>

/// Assembler for Company and CompanyDTO.

/// </summary>
public interface ICompanyAssembler
{
CompanyDTO Convert(Company company);
Company Convert(CompanyDTO DTO);
}

What is so useful about an object that has no behavior?

DTOs may seem like weak objects because of their lack of behavior; but this is what makes them the one object that is safe to pass across a service boundary.  A client that interacts with your service may, and often will, have a different domain from the service itself.  In fact, many different clients with many different domains may interact with your service.  It is difficult and undesirable to share the same domain model between a service and all its potential clients. 

Consider again the Company example.  Let us assume that the service we are writing is a better business bureau catalog of companies.  The domain object, Company, may include such methods as "FileComplaint" or "RevokeLicense." 

This service may be consumed by an auction site that wants to use the companies names and ratings but has no need to file complaints and does not have the ability to revoke a license.  The company domain object for the auction site will include methods such as "AddAuction" or "AssociateWithReview." 

Sharing the domain objects between the auction site and the better business bureau service would pollute both domains; not to mention the fact that they may, and in this example certainly would, have different owners who do not wish to share code bases.

Sharing DTOs, because of their lack of behavior, works fine though.  The DTOs serve as a simple, abstracted definition for the data that the service and its clients must exchange.



DTOs also insulate your service clients from changes to the domain of the service and vice versa.  You are free to change your domain objects as much as you want and leave the DTOs alone.  The assembler can simply be updated as the domain objects are updated.

If the domain objects were shared by the clients, every change to a domain object would directly impact clients, greatly increasing the costs of maintaining the service.

kick it on DotNetKicks.com