I'm trying to convince my colleagues that this is a good practice
A good practice for good practices sake is not always business efficient. If justified, I'm more than happy to argue against using CQRS, Event Sourcing, DDD, etc. For example, on smaller projects that have one or two use-cases.
While the other answers are excellent, the concerns that convinced me that decoupling is important hasn't been mentioned: Your repository (ORM) may populate data unexpectedly.
For example, consider these two actions accessing an Entity Framework DbContext
:
(I've not tested this code, but it should get the point across)
public JsonResult GetUser(string id) {
return new JsonResult(_ctx.Users.FindOne(u => u.id == id));
}
public JsonResult AddUser(string id) {
var currentUser = _ctx.Users.Include(u => u.Roles).FindOne(currentUserId); // <--
if (!currentUser.Roles.Contains("Administrator")) throw new Exception("User not authorized to add new users");
_ctx.Users.Add(new User(id));
return new JsonResult(_ctx.Users.FindOne(u => u.id == id));
}
While both actions return a JSON object of a User, due to Entity Framework's Object Caching, the second action will include the Roles
property—without explicitly asking for it on the final line.
If your application or persistence/infrastructure layer conditionally Includes
properties, then your Domain object properties will be conditionally instantiated and you may get unexpected results.
Due to this, and the great reasons provided by other answers, I'm convinced to no longer expose my domain objects to my presentation layer.
Finally, I'm not against using Anonymous objects in place of Dto's—especially when type safety is lost with JsonResult
.
For example:
public JsonResult GetUser(string id) {
var user = _ctx.Users.FindOne(u => u.id == id);
return new JsonResult(new {
id = user.id,
name = user.name,
})
}
public JsonResult AddUser(string id) {
var currentUser = _ctx.Users.Include(u => u.Roles).FindOne(currentUserId); // <--
if (!currentUser.Roles.Contains("Administrator")) throw new Exception("User not authorized to add new users");
_ctx.Users.Add(new User(id));
var user = _ctx.Users.FineOne(u => u.id == id);
return new JsonResult(new {
id = user.id,
name = user.name,
})
}