Helpers and managers
Imagine you are working on a system built in a transaction script architecture and have the following talk with the lead dev/architect/some other manager:
You: This tax calculation for each order gets really messy.We have four different taxes to apply, some are for the whole order and some for the specific order items. Now we have to multiply the net value by each tax rate in the specific order, handle rounding properly and then calculate totals for the order. This multiplying is all over our order service!
Lead: Do you have any ideas?
You: Value objects. I want to implement a Tax class. Each created tax object will take your input value and handle the job of calculating tax according to the logic for the tax type. I am pretty sure we can chain these taxes together, so that we can setup tax pipelines for each order item, depending on its category. This is much better than having the tax logic all over the services. What do you think?
Lead: Wait, all domain logic must be in services. You can’t change the architecture!
I highlighted in green the part which he/she really hears. Why did this happen? Because you were specific enough using the value object pattern name, that it rang a bell in his head. The Tax class is a really strong concept, which in his opinion goes against the system’s architecture (whether it is good or bad is another story). Now imagine a different conversation:
Lead: Do you have any ideas?
You: I want to create a TaxHelper class which will be responsible for tax calculations.
Lead: Helper class? I think that’s OK. Go on.
Ever wanted to hide your crazy ideas in the production code? Use helpers😉
No, really, my point here is that these helper classes do not show any specific concept. They are everywhere, I bet you have some OrderManager, TransactionHelper, FeeCalculator in your code. Usually such class has lots of static methods, sometimes even state. The problem is that it can encapsulate very important business logic and instead of properly modelling the domain, you leak some concepts to these helpers.
The main problems I see here:
- Violation of SOLID. As Nick Malik proves, they violate each letter of the acronym.
- Swelling: No single responsibility, so such class becomes a bag for unwanted, repetitive code.
- Proliferation: Helper classes usually stay close to the consuming class. Let’s say you have a domain service layer and in these services you use your helper class extensively. Now you want to use one method of the helper in the client. The client and server are properly separated, so you can’t just call the method. Instead of thinking in terms of application structure (e.g. creating a new service and calling it from client), you would probably create helper in the client project, copy the code and call the method, because, hey, it’s just a helper.
- Tight coupling: Imagine you want to take out some part of your logic to another application. Even if your modules are beautifully separated, simple POCO classes, one helper can ruin all your work. You would probably take it along with your module.
- Not so helping after all: What tools do you have in your StringUtils class? Do you remember all methods? Are they easily discoverable? Probably like me, you thought this was a good idea to put commonly used string manipulations in one class. The problem is nobody ever uses it and reinvents the wheel by coding the same string parsing routines again and again.
Almost always helper classes are really, really bad decisions. The only counter-examples I can recall are HtmlHelper and UrlHelper from ASP.NET MVC. Here the situation is clear: these classes are starting points, do not use anything else, here is a way to extend it. Following this pattern can give new developers a very helpful way to learn your tools. Otherwise helpers smell.