I've recently read this blog post regarding what a constructor should do and I am also reading Eric Evans' book on Domain Driven Design.
Both the blog post and the book state that a constructor should have a bare minimum amount of logic in it, as to bring the object to a sane default state. But my question is, shouldn't immutable objects like the one below be an exception to this rule?
public class SomeReport
{
public SomeReport(IReadOnlyList<Order> orders)
{
this.Orders = orders;
this.TotalWithoutTax = orders.Sum(o => o.TotalWithoutTax);
this.TotalTax = orders.Sum(o => o.Tax);
this.TotalWithTax = this.TotalWithoutTax + this.TotalTax;
this.OutStandingOrders = orders.Where(o => o.RespectsSomeComplexBusinessRule()).ToList();
this.TotalWithoutTax_OutStandingOrders = this.OutStandingOrders.Sum(o => o.TotalWithoutTax);
this.TotalTax_OutStandingOrders = this.OutStandingOrders.Sum(o => o.Tax);
this.TotalWithTax_OutStandingOrders = this.TotalWithoutTax_OutStandingOrders + this.TotalTax_OutStandingOrders;
}
public IReadOnlyList<Order> Orders { get; }
public decimal TotalWithoutTax { get; }
public decimal TotalTax { get; }
public decimal TotalWithTax { get; }
public IReadOnlyList<Order> OutStandingOrders { get; }
public decimal TotalWithoutTax_OutStandingOrders { get; }
public decimal TotalTax_OutStandingOrders { get; }
public decimal TotalWithTax_OutStandingOrders { get; }
}
Think of this object as being the container that will be sent to a PDF framework as input for a report for some business analyst (and ignore the fact that there may be some duplicated logic inside. The purpose of this class is to illustrate a complex initialization sequence for an immutable object).
As you can tell, the constructor could contain some complex logic like filtering, sorting and grouping in order to bring the input data into the shape required by the report, which in enterprise reports can mean some serious work.
In my opinion, this is very much in line with what OOP states about how data and behavior should work together inside the same construct to reach the desired goal.
If I were to split this class into a 2 different constructs, one that holds the data and one that assembles it (the factory) the way Eric Evans suggests in his book, I would essentially leave the realm of OOP and venture into the procedural/functional paradigms. Furthermore, I would lose (some of) the benefits of immutability, since from the moment the SomeReport POCO/POJO is initialized to the moment the factory is done initializing it, it is in an invalid state, thus violating the very purpose of a constructor - to have a valid instance of an object right after it has been called.
These being said, am I missing something else or when it comes to immutable objects it is a sane approach to do some heavy-lifting inside the constructor, since its initial state will also be its final state?
P.S: The heavy-lifting should be deterministic and if need be, computed properties can be made lazy.