3

I recently came across an internal project where they were using DTOs throughout the solution, and when I sat down to implement a new MVC controller with a JSON REST endpoint I decided to use an anonymous object convention instead of declaring a discrete DTO class solely for the purpose of this API endpoint.

public JsonResult GetSomeSuch()
{
    // .. do work

    return Json(new {
        PropA = domainObj1.PropA,
        PropB = domainObj2.PropB
    });
}

.. as opposed to the team convention, ..

public JsonResult GetSomeSuch()
{
    // do work ..

    return Json(new SomeSuchDTO2 {
        PropA = domainObj1.PropA,
        PropB = domainObj2.PropB
    }
}

// in some other file off in some other project and folder
public class SomeSuchDTO2 {
    public int PropA { get; set; }
    public string PropB { get; set; }
}

Setting aside that I should have used a DTO here purely because it was already a team convention, team conventions trump best practices until team conventions change by team leadership, of which I was not yet a part. I'm not concerned about the social / team dynamic and consistency aspects so much as the technical value of details in specifically that point at which an action in C# becomes serialized to Javascript, as I have since moved on from that company.

The team convention was to return only DTOs and the DTOs were managed in a completely different project (among about 50 projects in the solution), I felt that in this case perpetuating that pattern was excessive and difficult to maintain. Further, my belief is that DRY must correlate with YAGNI; only refactor when you're repeating the same thing twice, as such you would only refactor to use a DTO if that same return object structure reflecting the same context is in fact needed elsewhere, not just possibly but actually, but since this is being serialized to Javascript where it will be consumed anyway there is really no point anyway.

I suppose a case can be made for DTOs as specification documentation, but when it becomes more of a maintenance chore to declare them and to look them up in their special folder in a project I would argue that there are more appropriate places and means to document the interface; indeed the anonymous object declaration seems to be documentation enough. I should also add that in our case everything was internal, this was a standalone project, so it's not like this was an externally exposed API.

I was wondering if there are other angles to this that I didn't consider as to why I should use DTOs, something reasonably convincing?

2
  • 1
    One reason to have all of your DTOs in an external assembly allows you to reuse them in another project so it can speak with your first project in a strongly typed manner. TypedDtoResponse response = client.Get(new TypedDtoRequest()); This is a common practice when using servicestack for example.
    – Mike
    Dec 6, 2013 at 18:42
  • 1
    Mike, this is why I mentioned: "Further, my belief is that DRY must correlate with YAGNI; only refactor when you're repeating the same thing twice, as such you would only refactor to use a DTO if that same return object structure reflecting the same context is in fact needed elsewhere, not just possibly but actually, but since this is being serialized to Javascript where it will be consumed anyway there is really no point anyway." Any counter-arguments to this?
    – stimpy77
    Dec 6, 2013 at 18:48

4 Answers 4

4

A strong type system is supposed to help you

Using a strongly typed language is meant to prevent errors during runtime by catching them at compile time. Using an anonymous object (or dictionary for example) defeats this purpose and allows bugs to be compiled into the program that will only surface during runtime.

As an example take a look at the code below:

public object GetUsers() {
    var users = MyRepo.GetAll<User>();
    return new {
        count: users.Count(),
        items: users
    }
}

public object GetNewUsers() {
    var users = MyRepo.GetNewest<User>();
    return new {
        Count: users.Count(),
        items: users
    }
}

The first response uses a lowercase count parameter where the second response contains an uppercase Count parameter. In a class where these methods may be separated by a few blocks of code it is very easy to miss this typo. Even worse this will happily compile and run. If we were to use a typed DTO instead this situation would not be possible (in fact it wouldn't even compile).

Or how about this example:

public object GetSystemInstallDate(){
    var date = AppHost.GetInstallDate();
    return new { 
        date
    };
}

What happens if we refactor to change the GetInstallDate method to return a DateTimeOffset instead of a DateTime object? The code will happily compile but the output to the client will have changed. This is a weak example but there's many other instances were things like this can happen. Numerical types, refactoring enums, etc...

Refactoring

If your anonymous response is used in more than one location you won't be able to refactor using the fancy tools like resharper. You will have to manually refactor which is never a fun task. If it is used in a single location the refactoring is easier but still more work than defining the class in the first place.

DRY

I would argue that by using anonymous types you are not protecting yourself against violating DRY; rather, you are making yourself more likely to violate it. An anonymous type can only be used in one place; however, if what that type represents is ever required elsewhere the entire type will have to be recreated.

By using a strongly typed object you only have to define the type in a single place and you then use that type where it is needed. This prevents you from having to repeat writing out all the property names, formatting values, etc...

public object GetTop10Movies() {
    return new { lastmodified = DateTime.Now, items }
}

public object GetNewest10Movies() {
    // We now have to repeat the previous anonymous object
}

YAGNI

This is a guideline and not a rule. If it was a rule it would boil down to "ignore the future" which is always a recipe for disaster. At the same time trying to cover all possible future scenarios is a horrible idea and will lead to overly abstract and complicated codebase. The trick it to strike the right balance between thinking ahead and solving the problems at hand.

Another way of thinking about YAGNI in this situation is: Do you need compile time type safety?

I would argue that the work involved in creating a class to represent your response is only slightly more than the work involved in typing out an anonymous object. The complexity difference is essentially none (in fact with the C# world an anonymous object would most likely be considered a more complex route to take).

For a rather minimal time investment you get quite a few benefits that are real and usable to the developer. They may not be needed in the sense that without them the application won't function, but if you didn't need a strongly typed language I would question the use of C# in the first place.

Maintenance

The team convention was to return only DTOs and the DTOs were managed in a completely different project (among about 50 projects in the solution), I felt that in this case perpetuating that pattern was excessive and difficult to maintain.

I'm not sure where the difficulty in maintaining this pattern would come from except for the idea of having 50 projects all bundled into a single solution. You will have one of two situations:

  1. A new response type is needed.

Add a new file to the DTO project representing your type and then use it in your application (a reference should already exist between the projects).

  1. An existing response type is needed.

Start typing the namespace of the DTO assembly and select the type that you need.

intellisense screenshot

I suppose a case can be made for DTOs as specification documentation, but when it becomes more of a maintenance chore to declare them and to look them up in their special folder in a project I would argue that there are more appropriate places and means to document the interface; indeed the anonymous object declaration seems to be documentation enough.

There's no better place to document your code than to have your code document itself. The name of your response DTO should describe what it is. Your anonymous declaration says nothing about what it represents, it only tells you what it contains. Having to look up external documentation (ala a website or help file) is even more cumbersome.

A box that consists of two bolts, two washers, and a nut doesn't describe what it is for at all. A box with the sticker "monitor wall mount" lets you know exactly what it is simply by looking at the packaging.

new { id, model }
// What is this? A vehicle? An economic model? Representation of DNA?

new Vehicle { id = id, model = model }
// Oh! A vehicle!
1
  • I return to the fact that this returns JSON for Javascript to consume, and it's internal, thus type enforcement is pointless. Were I to replace the JSON serialization with SOAP I wouldn't have even bothered to ask as I would have argued in favor of DTOs. Also the API response was very specific, and one could only reasonably name it "{controllername}{actionname}Response", so reuse is out the door. Not that you argued for reuse. But in this case there was no globally understood "Vehicle"; there was only a specially filtered model specially built for one widget on one view
    – stimpy77
    Dec 7, 2013 at 22:39
3

Personally, I've transitioned from anonymous JsonResults to DTOs. I loved the anonymous objects - they're quick, easy, don't really hurt anything (transferring Json from server to UI, I could care less about type checking ... this is just a serialization vehicle). In an agile environment ... it's fine by me.

Over time, though, I noticed that when returning data, especially to a UI, it more often that not needed a little standardized formatting/conversion/processing. I started seeing duplicate code to do date/time formatting, unit-of-measure conversions, etc. With DTO's, I can enforce some standardization around these things via core interfaces and some extension methods. So we are talking about reuse, but not of the DTO itself.

Sure - there are cases where it doesn't make sense. If you are sending down a tuple of some integers, who cares. However, I think it's easier for a team to just stick to the rule "create DTOs for Json actions" and it'll provide benefit in the long run. The cost is cheap.

2

I like to use anonymous type in this case because:

  1. This is the final step of data transformation on server. A type system only increases the burden to programmers without adding any value to the operation. Regarding the strong typed client mentioned by Mike, I actually see it cause more problems in real world - the coupling between the service project and all the client projects tends to increase over the time.

  2. Anonymous types don't pollute the namespace. Don't underestimate the advantage of this. In a project where developers come and go and business change their mind frequently, a developer tends to choose a misleading name for the new DTO he/she has to create if the "good" one was taken, which renders both name misleading to the later maintainers.

2

They say that coming up with names is hard, but I believe it's only hard because too many idealist programmers insist you should always extract declaration from usage whenever possible. That stupid rule enforces you to name those extracted declarations, and when the sole reason for extracting them is to obey that rule, it's ofter hard to name them. Even if the language would have forced you to use a declaration anyways, that rule requires that you move the declaration somewhere far away from the usage, thus preventing you from using the context of the usage as implicit reference while choosing a name, something that could have made the naming task much easier.

How was that related to your question? When pondering whether to extract declaration from usage or not, I find that a great rule of thumb is to consider what name you'll give the thing you declare. If the name comes naturally, it means it has some meaning in the domain, and you should make it a DTO. If you have to struggle to find a name, and all that you can come up with is something directly related to the usage(like <methodname>_result) or a direct description of the fields inside1(like IntAndStringTuple), you shouldn't make it a DTO unless something in the project is built in a way that forces you to use DTOs.

In your case, the method is named GetSomeSuch and the DTO is named SomeSuchDTO2. These are ofcourse not the real names used in the project, but I'll use them for the example. If SomeSuchDTO2 is named after GetSomeSuch - that is, if without GetSomeSuch there is no direct way to understand what SomeSuchDTO2 means - then you shouldn't make a DTO. But if GetSomeSuch is named after SomeSuchDTO2 - that is, if SomeSuch is a thing - then you should create a DTO.

1When I say "direct description of the fields", I mean description of the fields that compose the object rather than description of the object as a whole. For example: PersonName is not a direct description of the fields. even though you can understand what the fields would be from it, it describes the object as a whole. A direct description of the fields for the same class would be something like FirstNameAndLastName.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.