-1

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:

API Layer:

The authenticated user sends the request

PATCH /documents/{documentId}

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.

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.

Repository layer:

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?

2
  • 3
    "This would break up the reusability." Personally one of the big things that made me a better programmer was realising that you should always focus on code use before even considering code reuse. Reuse is cool and all, but in many cases it makes you focus on the wrong thing or introduces coupling where there really shouldnt be any. Focus on code use first, worry about code reuse once you see that you are actually having a problem. If you can get some code reuse as a bonus along the way, good for you!
    – wasatz
    Apr 7, 2021 at 5:15
  • I updated my question and added a very small and simple usecase :)
    – Question3r
    Apr 9, 2021 at 19:54

2 Answers 2

2

I would not avoid it, I would put some distinction between the business and database logic. Simply,

Save(Thingy);

Save can come from IRepostory, IDatabase etc.

The save handles mapping and persisting the "thingy" to the repository. Pretty easy to switch from one database to another without changing the business layer.

The business layer doesn't care how it is saved.

Some layering is required, to have separation of concerns, otherwise your business and database logic will be intertwined, which beyond a trivial application will lead testing and maintainability issues.

1
  • I updated my question and added a very small and simple usecase :)
    – Question3r
    Apr 9, 2021 at 19:55
0

Frankly, I would not expose any kind of "database-layer" or "CRUD" logic. Requests coming from the outside world should reference only "outside-world things," and should never let anyone anywhere directly manipulate your database.

1
  • This is potentially good advice, but to be useful you will have to elaborate a bit more, perhaps with some examples. Apr 10, 2021 at 1:19

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.