I am trying to write a couple of classes using SOLID principles and having some trouble.The problem is quite simple. I have an application that tracks leads. Leads are created when events are raised by the customer - eg file download, registration, page view, etc. A lead can have multiple opportunities - ie opportunities are for different products.A lead should be created if it doesn't exist.An opportunity should be created for that lead if it does not existThe lead score should be incremented based on some event scoring tableThe way I wanted to do it was to have a LeadEventHandler class that calls three services TrackLead, TrackOpportunity and IncrementLeadScore. This seems ok, but testing the LeadEventHandler required me to use any-instance which is a code smell.Note I know I could test the whole chain in LeadEventHandler, but for learning's sake I wanted to test in isolation. I could have injected the dependencies into LeadEventHandler, but this just didn't seem correct. It would be great if I can let the tests drive my code, but I seem to run into quite a few stumbling blocks.
How can I write these services using SOLID and keep them easy to test?
design;ruby;rspec
Speaking purely solid your classes:TrackLead, TrackOpportunity Are already fine grained and therefore should be compliant to 'S' single responsibility.Your class:LeadEventHandler Your lead event handler is like a controller to all these smaller processes, so for it to take in dependencies to the TrackLead and Trackopportunity classes makes it compliant to DI but not necessarily 'O' as it is not easily open to extension. To make this easier make sure that your Lead events are stored inside a list in LeadEventHandler that will be enumerated each time an event is received. This way event processors can be added at run time or from an external config. Making it 'O' compliant without changing the code of LeadEventHandlerAs far as unit testing is concerned, instances of TrackLead and TrackOpportunity should be mocks/stubs when testing LeadEventHandler because you should not actually be testing their functionality but only that their entry point methods was calledPS: The system you are describing is a very good fit for event based/message bus pattern. Perhaps you can implement something similar