The magic of the infrastructure
I did a code review recently and pretty much the most frequent suggestion was something along the line of: “This needs to be pushed to the infrastructure”. I was asked to be clearer about this, so I decided to write a blog post about it.
In general, whenever you see a repeating code pattern, you don’t need to start extracting it. What you need to do is to check whatever this code pattern serves a purpose. If it doesn’t serve a purpose, only then it is time to see if we can abstract that to remove duplication. I phrase things in this manner because all too often we see a tendency to immediately want to abstract things out. The truth is that in many cases, trying to abstract things is going to cause things to be less clear down the line. That is why I wanted to call it out first, even when I want to explain how to do the exact thing that I caution you about it. Resource cleanup in performance sensitive code is a good example of one scenario where you don’t want to put things to the infrastructure, you want everything to be just there. There are other reasons, too.
After all the disclaimers, let’s talk about a concrete example in which we should do something about it.
Error handling is a great case for moving to infrastructure. This code is running inside an MVC Controller, and we can move our error handling from inside each action to the infrastructure, you can read about it here. I’m not sure if this is the most up to date reference for error handling, but that isn’t the point. The exact mechanism that you do it doesn’t matter. The whole idea is that you don’t want to see it. You push it to the infrastructure and then it is handled.
In the same manner, if you need to do logging or auditing, push them down the stack if they are in the form of: “User X accessed Y”. On the other hand, if you need something like: “Manager X authorized N vacations days for Y”, that is a business audit which should be recorded in the business logic, not in the infrastructure. I wrote about this a lot in the past.
Comments
I agree with you - but with one caveat: I really dislike moving all potential failures from the Controller to a generalized Error Handler. It is totally fine and expected if you see a pattern, e.g. return a 404 when something was not found or a 500 if nobody else catched the error. But if you have "only once" Exceptions, its usually better to catch them in the action and handle it there - it is easier to find and your global handler does not become totally cluttered. I noticed this problem espiecally in Spring Boot, where every error has to go through the global error handler and its annoying as hell.
Christian ,
Agreed. The global error handler should be for the things that repeat. If you have to know that
FooException
is thrown from/foo/bar
in case so and so and the handling is somewhere over there... That isn't a good idea. Pushing stuff there is only for the common case.unnecessary use of async is a form of self-sabotage when trying to build an infrastructure . In a straightforward, synchronous case you could easily centralize and abstract-out all sorts of authentication, transaction management, auditing, logging etc but when the code runs async in unpredictable patterns then you end up redoing the same stuff everywhere.
Rafal,
I'm not sure that I'm following you here. Given that we have
await / async
, pretty much all you defined should just follow naturallyi'm referring to the fact that the async code will be executed on another thread, or a bunch of threads, and trying to follow that with infrastructure is problematic when i want to control transactions, have logs follow the request, provide user's session data everywhere etc. At least problematic for me. But maybe thread static is old and obsolete and there's new cool way of doing things.
Rafal,
Why? You have the async context that you can use,
AsyncLocal
, and everything else just follow with it. In fact, if you useawait / async
in a pervasive manner, the code end up being almost identical for such cross cutting concerns.Comment preview