I'm creating a .Net 5 API backend and use EF Core for the database part. The simple CRUD way I know:
- API layer calling a command or query from the application layer using MediatR
- Application layer calling CRUD actions from repository interfaces
As we all know the CRUD actions are struggling with the real world problems. I would like to move away from the repository CRUD pattern and focus on the commands/queries as a single complex unit.
I think I should not mix the business logic with the persistence logic. But how should I structure the different parts then? Should one command/query contain everything it needs? This would break up the reusability. But otherwise I think I will fall back to CRUD based interfaces.
I read about Event Sourcing but for now I'm more interested in a solution not using it :)
I thought about an example usecase and came up with a user and document entity. The document has a
currentState and a
deleted flag. You can't modify the state if that document got deleted (you might do this but this is a pseudo example). This is how I would do it now:
The authenticated user sends the request
to update the document state and the middleware extracts the user from the incoming request.
After that it calls the command (created with MediatR) from the Application layer.
First you might want to call the repository layer if that user really exists. If not, you send back a "user not found" response to the API layer.
After that you call the repository layer to check if that document exists. If not, you send back a "document not found" response to the API layer. Otherwise you check if the document got deleted. If yes, you send back a "conflict" response to the API layer. Otherwise you call the repository layer and update the field.
This layer provides basic CRUD actions to deal with the database or other data sources (external APIs etc.). SQL query statements for example go here.
So this example usecase is a very small and easy one. But as you know the real world problems are way more complex and abstracting every data modification into repositories with CRUD methods end up in a rigid structure. An example would be to call two repository methods and make them run in a single transaction, so you might have to implement transaction functionality for each repository method?