Pergunta

I'm trying to get to grips with Domain Driven Design and MediatR. A reaction to being involved with applications that favour Services for almost everything. I see so much value in DDD and I'm trying to increase my understanding to write better more maintainable software. For software context I'm using a .netcore Razor Pages application (trying to use Clean Architecture layout). For business context I'm dealing with Memorials and ServicePersons. Memorials have RecordedNames (link table with additional properties). Memorial -> RecordedNames <-ServicePerson

The thing I'm trying to work out is where best the validations and business rules ought to go.

There's the 2 types of validation I have in mind:

  1. Request validation - e.g. An email field is [required] and is it a [valid email address].
  2. Business rules - shouldn't add the same person to the same List.

Request Validation

Initial thoughts are: Options:

  1. PageModel properties/ViewModel - Similar to each other
  2. MediatR Pipeline -Pre with Fluent Validation.

One of the nice things about using PageModel properties with attributes validation is it gives the client side unobtrusive out the box too. But it's at the expense of writing mapping code for each operation like this into the PageModel class properties.

Whereas when I consider using MediatR pipeline to do Request Validation, I would need to handle that all separately and more manually.

Business Rules

E.g. We shouldn't add the same person to the same memorial more than once.

  1. Have them in the Domain of Aggregates/entities/ValueObjects - MediatR just acts as a handler between the UI and the Domain object.
  2. Use MediatR Command and have the business logic there. However this feels like it starts to fragment the Domain business rules, but looks like the norm with MediatR. With that in mind I then start to wonder about the value of adding MediatR in, so as much as anything I'm trying to understand MediatR's strengths too.

Thoughts so far

Possible MediatR flow within DDD

Lastly

A colleague is pushing for the Aggregate Domain Objects to be used in the PageModel, which just feels all kinds of wrong.

Foi útil?

Solução

I expect the reason you aren't seeing benefits of MediatR is that the diagram shows a "layered" approach where your domain logic sits on top of a repository, which usually means domain classes all have a repository interface injected into their constructor by an IoC container.

A common disadvantage of a layered approach is that business logic must necessarily depend upon a repository interface (perhaps interfaces to other APIs too) and be responsible for triggering queries and updates to/from that repository. This is a potential design smell because in most cases, the orchestration of data persistence aren't domain rules or business logic. This is something MediatR is designed to avoid.

MediatR is a broker which exists as the focal point in the middle of everything; handling all orchestration and communication between different, disconnected components that have absolutely no interaction with each other whatsoever nor knowledge of each others' interfaces; They have no injected dependencies except for those which are absolutely essential.

For example, most classes may require an injected Logger. A repository may require a DB Connection Factory. But a domain class being concerned only with business logic really shouldn't need a repository since that breaks the separation-of-concerns between business rules and orchestration.

Using MediatR, the Domain Model can remain 'pure' by being concerned with business logic and rules only. Domain classes can operate without any side-effects (except for logging) since they will not be triggering any queries to any repositories, nor triggering any updates into a repository, nor triggering any downstream APIs or other external components, nor indeed any other concerns which aren't solely around business rules and domain logic.

This is ideal for isolating the business rules and ensuring the domain model remains agnostic to wider technical concerns and systems which surround it. In particular, removing repository interfaces from business logic classes reduces the complexity of building automated testing for business rules too by eliminating the need for mocking various interfaces for repositories, APIs, etc.

The responsibility for triggering a query from a repository or API to obtain data which needs to feed into the domain model rests solely within the remit of a MediatR handler. Again, MediatR is the only place where any orchestration happens between different components, so if the business rules require any additional data or state from elsewhere, then that query would be triggered by a separate MediatR handler which sends another event.

Again, MediatR, is the focal point of orchestration - the central idea is that MediatR adds extra levels of indirection which provide almost complete de-coupling between all the major components of the application. The UI, the domain model and the repository are all entirely disconnected and unaware of each others' interfaces or existence.

Licenciado em: CC-BY-SA com atribuição
scroll top