If prices are changing and you need to capture them, the "Product" concept isn't enough. You need to model the concept of price, and perhaps price change events too. So a customer doesn't buy a product, but a product together with a "ProductPrice".
If prices are changing and you need to capture them, the "Product" concept isn't enough. You need to model the concept of price, and perhaps price change events too. So a customer doesn't buy a product, but a product together with a "ProductPrice".
Let's put it this way. Do you think it is reasonable to store the taxes as a reference, or the shipping cost? We would never consider it appropriate to say, "Go look up the shipping cost for a 5lb package from here to here." Oh, and by the way, this is using a live reference to a customer object, for which the shipping address might very well have changed.
Of course we would not do this. So why would it appropriate for a product price? An invoice is the capture of all this information at the moment the purchase is made. That is its purpose. If we did not need this moment-in-time capture, then the concept of an invoice would not even exist.
I also think that that is its Achilles heel - by giving names to groups, only those privy to the internal representation of the atom can combine their functionality with its data. So you need to either merge your functionality with the atom's data (aka define an instance method) thereby coupling "things" with "actions", or you need to leak the atom's internals to outsiders (aka write a method somewhere else that "knows too much" about the atom's internal representation).
The author already discussed attaching the price to the product into a ProductPrice. His argument continues that price is only one thing, there may be more.
FTA: "If our analytics department needs to know the product category for their reporting, or if our accounting department needs to know cost-of-goods-sold for revenue calculations, we must attach these as well, as they may change over time. As these requirements grow, you end up sending more and more of the object graph attached to the order, just to maintain consistency for everything that happens after-the-fact."
He's essentially extracted all this information out into its own class caleed "State" which can be referenced by a single ID and passes that along with his product, a ProductState if you will.
The information doesn't need to be sent over the wire. There are many ways to solve it, but at the end of the day all that needs to be sent over the wire is the product and the time of purchase. Everything else can be deduced on the backend, and how hard that is to do will depend on how well you've modeled the concepts your interested in.
Indeed. A new model is needed, and that new model would have all the relevant attributes. I can think of two ways to do this (using ActiveRecord-ish naming conventions).
One way would be to have an InvoiceRow to represent the presentational details of each row in an Invoice, like name and price, perhaps along with a reference to the actual model it came from, like product_id. The Product model could change, but the Invoice would still have all the InvoiceRows it needs to display and reason about that invoice without fear of mutation.
Another way would be to have a robust versioning system. So a Product might have only an id, and there could be many ProductVersions that hold all the real info (name, price, etc.) and reference the same Product via product_id. ProductVersions would of course be immutable. Then the Order model in the example would return product_version_id, and could therefore look up the actual name and price at the time the order was made.
For the narrow example from this article, I think the first example fits better and is much simpler.
You're taking his example too literally and solving for it alone. He's talking about adding a generic feature onto your entire REST API to deal with these issues in a consistent way.
The problem is a poor domain model and should be fixed there and not by adding an overly complex cache or state object storage solution that seems to only apply in a REST model. If the domain model is fixed then REST, soap, RDBMS or anything else becomes trivial.