Reputation: 179
I'm trying to get into Design Patterns and REST API's, and for a project, I use Entity Framework Core with:
Some things that are really foggy for me:
I know these things also depends on the developer, but what's the best/"original" way to go about this.
Upvotes: 3
Views: 4181
Reputation: 771
Data validation, where should this be done?
When you get a request you can create a command and send it to a mediator. Then mediator will take care of applying correct command handler.Command handler will use required Domain model, Repositories to perform the operations.
You can do, input Command Validation. You can check whether the command is formed correctly or not. If you use MediatR you can use the PipelineBehavior. Then inside the domain model you can perform some validation. Probably business logic validations, Invaraiants etc.
Events? Where should this be triggered? Example: When a user is registered I want to send them an email.
You can use the Command Handler to send a Domain Event. Once the User registration done, you can emmit UserRegistered domain event. Then Email service may listening to that and will response to that event.
Should the business layer take DTO or Domain models?
You can use Contracts from clients with requests. Then you can extract the data and form a command. Therefore, command will not know about the contract. It means no one after the command (command handler, Domain models) will not know about the contracts.
PS: I prefer to call contracts instead DTOs in this case.
Where should convertion from DTO to Domain models go?
One scenario would be, you need to send a query response to client. There you can use DTO.
Clint makes query request --> controller creates query command and send to mediator --> query handler perform the operation --> convert the domain model/read model to DTO --> send back to the client.
What should go into the controller?
Controller is just a bridge. You can take the request and create related command and just send it to mediator. Then response back to the client when mediator response back. That's it for the controller. I'd like to keep my controllers flat. It helps to maintain single responsibility.
Upvotes: 0
Reputation: 19610
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
Upvotes: 3
Reputation: 3260
Data Validation:
All validations should be performed as part of the Domain Layer, typically as part of a factory method in the Entity class. You could have a separate factory method or Class if that suits you better. The critical thing to remember is this: Entity/Aggregate objects should ALWAYS be valid. They cannot be in an invalid state and need you to check for their validity with is_valid
methods.
A sample workflow would look like this:
Events:
Events should be triggered as part of the domain layer, immediately after the business logic is executed.
To prevent events from being dispatched prematurely (like say, before the data is persisted), you should ideally raise events in business logic, but dispatch them as part of your Unit of work only after a successful persistence transaction.
DTO Handling:
The business layer should accept the DTO and return Domain models. Validation happens during the conversion of DTO to Domain Model. DTOs (or even plain keyword arguments) are the correct inputs to factory methods.
The conversion from a DTO to a Domain model should happen in Factory methods, if the object being constructed is new. In case you are dealing with an update scenario, the repository would fetch the persisted object, reconstitute it as a domain entity using the entity's constructor and apply the DTO changes within an explicit update
method, which would also run validations.
Controller Responsibility:
The controller should be responsible for:
These concepts apply to any DDD design, including EF Core.
Upvotes: 4