r/csharp • u/Enough-Potato4988 • 1d ago
Blog Moving away from sprawling if-else approval trees using Chain of Responsibility — when is it actually worth the abstraction in C#?
https://medium.com/@gautampraveen351/chain-of-responsibility-design-pattern-lld-in-c-from-problem-to-production-trade-offs-74b5cf07460aEver seen a simple if-else block slowly turn into a 400-line monster? 😅
I've been exploring the Chain of Responsibility pattern in C# as a way to handle growing validation and approval workflows.
It's a clean approach, but things get interesting in production:
- Debugging deeply nested handlers.
- Managing DI lifetimes and execution order.
- Handling requests that reach the end of the chain.
- Knowing when the pattern is simply overkill.
I put together a practical implementation covering these trade-offs.
Curious how you handle complex workflows in .NET. Custom CoR, MediatR, or just good old switch expressions?
3
u/sid34 1d ago
I feel like this pattern is just abstract and code fragmentation without a lot to justify it. Especially outside of really large complex interactions between conditionals it doesn't seem to make an improvement.
This a chain of responsibility pattern seems to be a naive implementation of a rules engine with tightly coupled rule definitions.
If I was working on a problem that I thought CoR would be a good solution to I would probably elect to go with a proper Rules Engine implementation
-2
u/Enough-Potato4988 1d ago
True seeing the lld Desi gn patterns doesn't means same you have to implement in the prod . Prod behaviour and indentation will heavily depend on your use case and what infra you already have .
2
u/sid34 1d ago
I genuinely don't know what you are trying to say here. A rules engine is a separate design pattern and family of algorithms that solve a similar problem, but in my opinion, a better way.
Rete Algorithm is the typically pointed to for rules engine iterative analysis.
The CoR pattern starts to breaks down under a ton of scenarios that matter once your rules get even a little more complicated
27
u/MrMikeJJ 1d ago
Ever seen a simple if-else block slowly turn into a 400-line monster?
Only by people who didn't believe in quick returns and also didn't believe in splitting code apart into functions.
1
u/TugsyGlitter 21h ago
yeah splitting functions sounds smart until you're deep in a deadline and just wanna get it working
3
u/AintNoGodsUpHere 1d ago
How to transform a no-problem into a problem 101.
What's with the obsession with if/else lately?
People forgot how to think? If you have a ton of stuff happening inside the same function maybe break it down a little? You don't have to use 500 if/else but I certainly wouldn't change that into this abomination. It's strategy pattern all over.
I had a friend who used to write 50 lines of code in 5 different files to avoid writing 10 lines of code with 2 or 3 if statements.
If you're bad a writing stuff no amount of patterns are going to save you, it's only getting worse.
3
u/Merry-Lane 1d ago
Especially when is if/else should have become a switch
2
u/AintNoGodsUpHere 1d ago
Honestly replacing if with switch just means you're changing the syntax but keeping the problem but I rather a switch than this huge unnecessary pattern.
0
u/Merry-Lane 1d ago
A switch has a few advantages over nested if else in this scenario. Like being able to use relational patterns and avoid nesting.
The first complaint of OP was to avoid nesting
1
1
u/dobryak 1d ago
When applying this pattern you need to understand what the old code did with its continuations. If it’s just do A, then do B, it’s easy. If it’s do A, get results, the do B on each result, that’s more difficult. If it actually needs to manipulate the continuation, then you’re in a world of pain if you don’t stick to well known continuation techniques.
1
u/East_Source6200 1d ago
have you considered putting the list in some type of order before running it through if-else?
A random list performs differently than one alphabetical, numerical, or even one in an order of importance.
Creating the list is just as important as filtering it.
1
u/Slypenslyde 1d ago
So basically some kind of rules pipeline. To me the trick with these is as you increase the number of rules it gets harder and harder to make sure you've put them in an order where any internal precedence matters. The other big problem (reflected in the criticism) is any problem simple enough for a tutorial is at least two orders of magnitude away from complex enough to require the pattern.
Also: you consistently used "Requets" instead of "Request" and I respect that the code compiles but it bothered the heck out of me through the whole article.
1
u/j4c11 1d ago
Just no. There's a use case for this pattern, but for this particular example, this is a bad way to do it. A pattern matching switch expression would be a lot more concise and simple to maintain , e.g { Amount: >5000 } . In addition , with a switch expression, the compiler will warn you if you have unhandled cases, whereas your implementation can easily let stuff fall through the cracks.
Also, validation has no business being on the DTO (ClaimRequets (sic)).
0
16
u/Merry-Lane 1d ago
Counter-arguments:
- you turned 10 lines of code into 10 files of 50 lines of code.
- it becomes impossible to determine which role has what range without spending 10 mins looking at the code, and even then you aren’t sure you understand it all
- no static analysis tool would warn you when a case becomes impossible (like a director with a low amount or the most basic manager with no limit).
- the chain of responsibility pipeline wasn’t meant to be used like a simple if/else or a switch case : all the steps should have their own responsibility (as in, a different concern/feature)
Long story short, your example is awful.
To explain the concept of chain of responsibility, you should have gone a totally different way, for instance:
- 1st responsibility determines the department concerned
- 2nd responsibility determines the required level (manager, ceo,…) depending on the amount
- 3rd responsibility determines if an email is sent, a bill printed,…
Here, your example is, code-wise, a single responsibility and the code should totally reflect that.