Search code examples
asp.netentity-frameworkrestdesign-patternsdomain-driven-design

DDD with EF Core(DTO, Business, Etc)


I'm trying to get into Design Patterns and REST API's, and for a project, I use Entity Framework Core with:

  • Repository pattern
  • Unit of work

Some things that are really foggy for me:

  • Data validation, where should this be done?
  • Events? Where should this be triggered? Example: When a user is registered I want to send them an email.
  • Should the business layer take DTO or Domain models?
  • Where should convertion from DTO to Domain models go?
  • What should go into the controller?

I know these things also depends on the developer, but what's the best/"original" way to go about this.


Solution

  • I need to apologise in advance that I might not give you enough recipes but here is my take on your question.

    You mentioned design patterns but you also tagged your question with the domain-driven design tag, so I assume you mean to use DDD tactical patterns in your project. I'd like to stress out that DDD is not a set of design patterns nor a design pattern or architecture style, it is the way how systems are designed having the business in mind.

    In DDD, one of the most useful patterns is the Aggregate pattern. An aggregate is a set of entities that form a consistency boundary, which holds enough information to make any business decision on its own, assuming all the business rules for that set of entities are assembled in one place.

    When we look at the application side of things, we often use an application service to retrieve the aggregate state, call one or more methods on the aggregate root and persist the new state back. It is important to realise that all of it happens in one go, one transaction, so the Unit Of Work pattern indirectly gets applied here. I am writing indirectly simply because an aggregate itself is a transaction boundary and one command that your application executes is the unit of work. That aggregate persistence can be done using the repository pattern, it is what we call implementation details.

    Now, I will try to deconstruct your specific points.

    Data validation, where should this be done?

    I tend to see validation as ensuring that the command that your application receives from the outside world is somewhat valid. That involves controlling that required command fields aren't empty and contain the correct value type like you cannot send letters for a numeric field and the email field indeed contains something that looks like a valid email. You can implement multiple layers of validation, keeping those that are most obvious closer to the client, so your users get fast feedback. Those easy checks you can do on the UI side and again on the edge (REST API in your case).

    When the command is passed to the application service, it needs to ensure that it gets a proper domain entity (aggregate) state, so at least you check if the entity exists by trying to get it from your database. You can also use value objects, like Email or Address and construct them from primitive types passed as command properties. Value objects are the perfect place to put some of the business rules into and the validation will happen naturally by trying to construct them.

    The last defence layer is your domain model. From all previous steps, you can be quite sure that you already have valid value objects and a valid domain entity to work with. The aggregate protects its invariants and by definition, it should not be possible to put the aggregate to an invalid state. I usually don't call it validation.

    Events? Where should this be triggered? Example: When a user is registered I want to send them an email.

    If you are talking about domain events, and you don't use event-sourcing, you must ensure that your domain events are published to your delivery medium within the same transaction. You should avoid the possibility when your application publishes a domain event but fails to persist the amended entity state, and vice versa. When such a failure happens, your whole system would be in an inconsistent state.

    Should the business layer take DTO or Domain models?

    I am not fully getting the question, but your business layer is your domain model. The domain model usually consists of entities, value objects and domain services. You call the domain model asking it to do something, there's no DTOs at this stage involved.

    Where should convertion from DTO to Domain models go?

    I would avoid using the DTO term in this context at all since your entity state can be seen as a DTO as well as your API model and your event. If you are talking about your API contract, it is a command and it doesn't get "converted", your API gets the command from the API and calls your domain model.

    What should go into the controller?

    API controllers are just the edge of your application. The API takes care of the transport (HTTP or something else), serialisation, authentication and some of the authorisation concerns, and exception handling. Its main purpose is to ensure that the API request looks legit and pass it to the application service.

    If you're looking for a sample app that uses WebAPI, EF and also implements some tactical DDD patterns, you might want to look at the working sample at Packt repo https://github.com/PacktPublishing/Hands-On-Domain-Driven-Design-with-.NET-Core/tree/master/Chapter09/ef-core