Does visitor pattern lead to violation of SRP?
I think those are generally an orthogonal concepts. But it is definitely a violation of arguably more important concept -- encapsulation.
The very first line in wikipedia says:
the visitor design pattern is a way of separating an algorithm from an object structure on which it operates.
And this is a vivid trait of procedural programming, contradicting basic OOP principles. So in my opinion balance calculating logic should reside in 401k class. However it doesn't mean that you can't use any other classes to help you. You can, but without exposing
401k's internal behavior.
But there is a problem of
401k becoming a God object, that Doc Brown mentioned. I would take a good look whether you need 401k object representing the same class in all those use cases where its current methods are called. First of all, consider your bounded contexts. I doubt that the same class must represent a use case of adding a contribution and generating report (moreover, I doubt that you ever need an object when all you need is data representation). Chances are that they belong to different BCs. Than, you could split your model on write and read side. Adding a contribution is a write model, your domain, while a balance calculating probably could be your read model.
Here is how your report repository could look like:
public function report()
// query your database
'year' => 2017,
'month' => 11,
'amount' => 10000,
ContributionsList can be exposed only if you're using an ORM (which is not mandatory, by the way), and this can be done implicitly by the framework itself, using reflection, for example. The point is not to expose it to an arbitrary clients. So if
ContributionsList's data is used within a certain set of objects (
401k object in your case), it's not such a big deal. After all, your data has to be exposed somewhere. Just don't make it a exposable by default, with getters.