'DDD: How to use mediator pattern to push aggregates
I don't quite figure out what's behind this [Implementing domain-driven design Vaughn, Vernon] practice:
Use a Mediator to Publish Aggregate Internal State
To work around the problem of tight coupling between the model and its clients.
I don't quite figure out what's trying to avoid behind this practice:
- Don't publish aggregates on application layer?
Solution 1:[1]
I don't quite figure out what's trying to avoid behind this practice:
- Don't publish aggregates on application layer?
Back in the early days of web development, most people implemented basic applications like this:
public class UserController
{
private readonly MyDbContext context;
public IActionResult GetAll()
{
return Ok(context.Users.ToList());
}
}
This is obviously bad because you may expose sensitive information that is needed for business layer logic, such as passwords. People have learnt that they needed to separate a "presentation model" (DTO) from their database model.
With the growing of DDD, people started to write domain models with repositories to manipulate them. So they started implementing applications like this (YMMV):
public class UserController
{
private readonly UserRepository userRepository;
public IActionResult GetAll()
{
return Ok(userRepository.GetAll().ProjectTo<UserDto>().ToList());
}
}
This is also wrong, because:
- The mapping of your DTOs depends on the domain model. Any change in the domain layer will impact the presentation layer mapping. But the domain layer's purpose is to validate business rules when the application's state is changing. You don't need business validation when you query your state.
- The presentation model is the result of two mappings (persistence to domain + domain to presentation). No mapping library is able to translate models across more than one transformation. This means you need to load into memory your whole domain model in order to do the mapping towards the presentation model. This is a very costly operation, especially when you want to sort, filter or page results.
- The domain model must contain all data needed for queries. But the purpose of the domain layer is to validate business rules. It should only contain data that is needed for that validation. Why include objects 4 or 5 navigation properties away in your domain model, whose purpose is to validate business rules when the state is changing, only because one large non-state-changing query needs that information ?
In conclusion, this approach is a waste of resources. How to implement an efficient application then ? You have two options:
You can simply inject your ORM in the controller and use that to query the database. However this is not very good because you will clutter your controllers (in the application/presentation layer) with database specific code.
You can create a mediator object, whose interface will be in the application layer, and the implementation will be in the infrastructure layer. It works just like a repository whose interface is in the domain layer and the implementation is in the infrastructure layer. Instead of returning domain object, it will directly return DTO mapped from the persistence store. Now your controllers can be implemented by calling domain services for state-changing operations, and mediators for queries :
public class UserController
{
private readonly UserService userService;
private readonly UserMediator userMediator;
public IActionResult GetAll()
{
return Ok(userMediator.GetAll());
}
public IActionResult Update(int id, UserDTO data)
{
try
{
return Ok(userService.Update(id, data));
}
catch (BusinessException ex) when (ex.Error == UserErrors.NotFound)
{
return NotFound();
}
}
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | ArwynFr |
