DTOs are for JSON serialization (and any serialization) also, not only for display

by andrei 23. September 2008 18:00

Data transfer objects are very useful for preparing domain information for a presentation layer, and this usually means the UI. But they are at least as useful when it comes to transferring information between different environments, especially when serialization is involved.

 

Here is an example: I needed to serialize a hierarchy of value objects in JSON, so I can put them in a hidden field and use them in an algorithm on the client-side.

These are the value objects:

 

  • CombinationOfProfiles:
public interface ICombinationOfProfiles
{
  IList<ProfileInCombinationWithOtherProfiles> ProfilesInCombinationWithOtherProfiles { get; }
  void AddProfileInCombinationWithOtherProfiles(ProfileInCombinationWithOtherProfiles combination);
  bool IsEmpty { get; }
}

public class CombinationOfProfiles : ICombinationOfProfiles
{
  private IList<ProfileInCombinationWithOtherProfiles> _profilesInCombinationWithOtherProfiles = new List<ProfileInCombinationWithOtherProfiles>();

  public IList<ProfileInCombinationWithOtherProfiles> ProfilesInCombinationWithOtherProfiles
  {
    get { return new ReadOnlyCollection<ProfileInCombinationWithOtherProfiles>(_profilesInCombinationWithOtherProfiles); }
    set { _profilesInCombinationWithOtherProfiles = value; }
  }

  public void AddProfileInCombinationWithOtherProfiles(ProfileInCombinationWithOtherProfiles combination)
  {
    _profilesInCombinationWithOtherProfiles.Add(combination);
  }

  public bool IsEmpty
  {
    get { return _profilesInCombinationWithOtherProfiles.Count == 0; }
  }
}
  • ProfileInCombinationWithOtherProfiles:
public class ProfileInCombinationWithOtherProfiles
{
  private readonly Product _profile;
  private readonly ProductCombinationForProfile _combinationOfProductsForProfileWhichIsValidInCombination;

  public ProfileInCombinationWithOtherProfiles(Product profile, ProductCombinationForProfile combinationOfProductsValidInCombinationForProfile)
  {
    _profile = profile;
    _combinationOfProductsForProfileWhichIsValidInCombination = combinationOfProductsValidInCombinationForProfile;
  }

  public ProductCombinationForProfile ValidCombinationOfProducts
  {
    get { return _combinationOfProductsForProfileWhichIsValidInCombination; }
  }

  public Product Profile
  {
    get { return _profile; }
  }
}

  • ProductCombinationForProfile:
public class ProductCombinationForProfile
{
  private readonly IList<Product> _products = new List<Product>();

  public IList<Product> Products
  {
    get { return new ReadOnlyCollection<Product>(_products); }
  }

  public void AddProductInCombination(Product p)
  {
    _products.Add(p);
  }
}

 

As you can see, they encapsulate a list of products (profiles) and for each product a list (valid combination) of products.

 

I initially tried to serialize the list of CombinationOfProfiles:

hdnProfileAssistanceData.Value =
    JsonConverter<CombinationOfProfiles>.Serialize(dto.PossibleCombinations);

(dto.PossibleCombinations is an IList<CombinationOfProfiles>)

This got me an error, because the hierarchy contains Product entities (which contain subentities and a whole business structure), and these are not serializable.

 

So I have 2 options: either make the Product entity serializable, or find another way to transmit the information I need to the client.

Making the Product entity serializable is a bad idea, because it would mean I am doing changes in my domain classes because of an infrastructure issue. This will impose initial restrictions, and in time it will become a problem. The domain must remain independent and uninterested of the other layers.

I realized I only needed the Ids of the products and nothing else, because on the client-side I just wanted to check al valid combinations based on this hierarchy. And this made things very simple.

 

I created a set of DTOs for JSON serialization:

[Serializable]
public class CombinationOfProfilesJSONDTO
{
    public CombinationOfProfilesJSONDTO()
    {
        
    }

    public CombinationOfProfilesJSONDTO(IEnumerable<ProfileInCombinationWithOtherProfiles> profiles)
    {
        Profiles = new List<ProfileInCombinationWithOtherProfilesJSONDTO>();
        foreach(ProfileInCombinationWithOtherProfiles profile in profiles)
        {
            Profiles.Add(new ProfileInCombinationWithOtherProfilesJSONDTO(profile));
        }
    }

    public IList<ProfileInCombinationWithOtherProfilesJSONDTO> Profiles { get; set; }
}

[Serializable]
public class ProfileInCombinationWithOtherProfilesJSONDTO
{
    public ProfileInCombinationWithOtherProfilesJSONDTO(ProfileInCombinationWithOtherProfiles profile)
    {
        Product = profile.Profile.Id;
        ProductCombinationForProfile = new ProductCombinationForProfileJSONDTO(profile.ValidCombinationOfProducts);
    }

    public int Product { get; set; }
    public ProductCombinationForProfileJSONDTO ProductCombinationForProfile { get; set; }
}

[Serializable]
public class ProductCombinationForProfileJSONDTO
{
    public ProductCombinationForProfileJSONDTO(ProductCombinationForProfile products)
    {
        Products = new List<int>();
        foreach(Product p in products.Products)
            Products.Add(p.Id);
    }

    public IList<int> Products { get; set; }
}

 

and a mapper between a CombinationOfProfiles domain value object and an CombinationOfProfilesJSONDTO:

public class CombinationOfProfilesJSONMapper : IMapper<CombinationOfProfiles, CombinationOfProfilesJSONDTO>
{
    public void CopyFrom(CombinationOfProfiles source, CombinationOfProfilesJSONDTO destination)
    {
        throw new System.NotImplementedException();
    }

    public CombinationOfProfilesJSONDTO MapFrom(CombinationOfProfiles input)
    {
        return new CombinationOfProfilesJSONDTO(input.ProfilesInCombinationWithOtherProfiles);
    }
}

Here is how my call looks now, which generates the entire hierarchy of DTOs automatically:

hdnProfileAssistanceData.Value =
    JsonConverter<CombinationOfProfilesJSONDTO>.Serialize(
        dto.PossibleCombinations.MapAllUsing(new CombinationOfProfilesJSONMapper()));

 

 

Enjoy programming!

 

I am putting together a set of concept lists for the main programming techniques, which should help developers learning or using them to be more efficient. Here are the techniques in progress:

Please feel free to leave any comments or suggestions which could make these lists more useful for everyone.

 

 

Also, if you are interested in learning these techniques you can try the developer training modules.

Again, please feel free to leave any comments or suggestions which could make the training modules more useful for everyone. We are having great results with them at Akcedo (so they really work), but they can always be improved. Also, if you would like to use the modules or are already using them and you want to discuss about the process feel free to comment and ask questions here.

Add comment


(Will show your Gravatar icon)  

  Country flag

biuquote
  • Comment
  • Preview
Loading



Powered by BlogEngine.NET 1.4.5.0