Now, as far as I know, it is a bad practice to have effective calculations within a constructor.
That depends on what you mean by "cash."
The purpose of a constructor is to "build" an object. To do that, you may have to do many things. One of the things you should do is configure some state within the new object. Is that what you consider "effective"?
The purpose of a class is to store some data ("status") and some code that works with that data. We call that "encapsulation." That is the purpose of a class: to protect the state of the class from external influence and prevent the state from influencing the outside, except through well-defined limits called "properties" and "methods." This encapsulation is what allows us to work with data freely without having to worry about breaking the rest of the system.
Immutable classes are generally preferred when possible.
Immutable classes are preferred when you need the benefits that immutable classes provide.
Instead of consulting the database within the constructor, the caller could provide the data to the constructor through parameters.
- This would eliminate some of the advantages obtained by separating the class from the original method in the first place, since the point was to take this functionality and organize it as a unit.
Intuitively, this approach makes some sense. One class is responsible for collecting data, the other class is responsible for performing its calculations. It makes things easier to prove, makes your logic easier to understand and follows the spirit of SRP.
Instead of inside the constructor, the query could be done in a static factory method, leaving only the assignment of the real fields to the constructor.
The purpose of factory methods is to provide an alternative way to build objects that have more flexibility. For example, you can return an object from a type family that is derived from a base type. I don't know enough about your system to know whether or not you need that kind of flexibility.
Use something like a builder
… since the generator itself would have the wrong type, it would ensure that the caller has to call build (), which would handle all the side effects.
Making a builder is a very complex task. Only do it if you absolutely need the benefits that a builder provides. You would only use a generator if you want to provide guidance to the caller in the form of intelligence, provide construction flexibility to the caller or force a certain order in the construction steps.
I think you might worry too much about the ceremony and about being "right." If you could ignore the rule that says "do not perform calculations in the constructor", would you do it? If your class didn't have to be immutable, what would you do? If you could adopt the approach you like best, regardless of the notions of "best practices", which approach would you choose?
Focus on the techniques that give you the best balance of benefits and costs.