C# Web API - Is there a better alternative to Data Transfer Object?

Viewed 393

I've been following the tutorial on how to build my own Web API using ASP.NET. I have finished the last section on Prevent over-posting. From what I understand, TodoItem class exposes all the fields of a particular model, including secret fields. The TodoItemDTO class is basically a duplicate of TodoItem, but it prevents secret fields from being exposed to the client for obvious security reasons. However, this technique goes against DRY principles. Is there a better alternative, or is this the only method?

4 Answers

However, this technique goes against DRY principles.

The purpose of DTOs is to decouple your domain logic layer from your presentation logic layer. It's completely irrelevant to attempt to apply DRY across multiple layers, because the separation of concerns effected by the layering is a more important factor.

Good software engineering is about balancing application of the numerous principles and patterns to achieve an optimal end result, not applying one of them dogmatically.

DTO is meant to transfer data between layers, while reducing coupling between said layers.

An alternative to DTOs is by not using layers.

An example of unlayered architecture is called Vertical Slice or sometimes refered as Clean Architecture.

First of all DRY, like any other pattern is not a silver bullet, and as any one of them should not be followed blindly.

Secondary, the DRY principle states:

Every piece of knowledge must have a single, unambiguous, authoritative representation within a system

In this particular case you can consider your TodoItem and TodoItemDTO as separete pieces of knowledge due to "secret fields" which should not be exposed to the client for obvious security reasons. You can try to move "shared" pieces to some base class or interface but I would say that usually it is not worth the effort.

Also note that code duplication is one of code smells for DRY violation, it still doesn’t necessarily means that there is one.

As an alternative to DTOs, you can use anonymous types to send only specific items to your client:

public IActionResult YourControllerMethod()
{
    var result = db.YourTable
        .Select(x=>new { ... });
    return Ok(result);
}

If you want wo use your entity classes for POST/PUT requests, you can use TryUpdateModelAsync() in your controller methods to prevent over-posting. Example from docs.microsoft.com:

var emptyCourse = new Course();

if (await TryUpdateModelAsync<Course>(
     emptyCourse,
     "course",   // Prefix for form value.
     s => s.CourseID, s => s.DepartmentID, s => s.Title, s => s.Credits))
{
    _context.Courses.Add(emptyCourse);
    await _context.SaveChangesAsync();
    return RedirectToPage("./Index");
}

You can decorate your properties with [BindNever] and [JsonIgnore]. [BindNever] will be ignored if you use FromBody.

I wouldn't say that using entity classes for data transfer is a bad idea in general. There are advantages (reusing of annotations for validation and EF Core, saving of mappers) but also risks that have to be considered.

Related