0

I'm developing an intranet application and I'm trying to use some concepts from Domain Driven Design (DDD) and Command and Query Responsibility Segregation (CQRS) in .NET Core, with EFCore.

But, to avoid weird exceptions from the API, on every Command and on every Query I'm first checking if the database is online (using DbContext.CanConnect()) and I send a pretty message to the user in case it's not. Instead of throwing some EF exception saying the database could not be found. I'm using a generic ApiResponse<T> that has .Data and .ErrorMessages to always return the same way from the API.

Is it the recommended approach? Or should I catch the exception somewhere else and return the message to the user from this other place? Or should I actually throw the exception from the API?

Elaborating a bit on my ideas: I'm thinking of using a .net Core Middleware to avoid this repetition and check the database prior to even getting to the Command/Query handler. Is it a good solution to the repetition?

EDIT: I realize there seems to be multiple questions in here, but my main concern is:

What's the DDD+CQRS recommended approach for this scenario where I'm repeating the same code (to check if DB is online) on every Command and on every Query?

1
  • 3
    Notice that connection can go offline after you successfully checked it's on. I would suggest to catch specific connectivity exception in middleware and wrap them with nice message for the consumer
    – Fabio
    Commented Sep 11, 2021 at 1:50

1 Answer 1

2

The idea of DDD is to design the system according to the business domain. Databases are part of the solution domain, not the business domain. Hence, the design should be as agnostic to your choice of backing service as possible.

Whoever sends a business message (command or query) shouldn't know how the data is processed. Whether it's persistent or not, if the the storage is a database or a file, remote or local, relational or not—and, of course, the error recovery strategy. That knowledge is a separate responsibility. Typically, we use intermediaries to make this decoupling, with event buses being a popular choice.

As for the backing service itself, it shouldn't tell anyone else if it makes these checks or not. Its contract might specify how it will resolve unavailability problems e.g., drop the messages, X number of retries or storing them locally. The point is that the caller has delegated the responsibility of persistence and thus shouldn't do any result-checking.

Side note: Checking for availability is one of the most common time-of-check to time-of-use mistakes, so be careful. A more robust approach is to acknowledge that failures will happen and figure out what sort of action is most appropriate in that situation.

2
  • 1
    So, if I understood correctly, I should handle the exception inside the command/query handler pipeline. Is that correct?
    – Rick Wolff
    Commented Sep 13, 2021 at 12:48
  • @RickWolff That's correct (see information expert). Please note that doing so reduces the caller's control but should you be looking for flexibility/customizability, more elegant alternatives are available. Commented Sep 13, 2021 at 20:22

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