Reputation: 197
I have some problems modeling this scenario with DDD.
I have two entities in the same BC: Oil and Machine. There are several different oil types, its final cost is based on some properties (density, materials, purchase price, etc) and these are updated periodically. So they aren't in the same aggregate. Because they are different entities, Machine only contains a reference of Oil with its id:
class Oil
id
purchase_price
density
material
...
class Machine
id
idOil
cost
addOil (Oil)
...
Machine has a cost too, based on oil's cost and some other machine properties.
Oil is optional, so when addOil method is called, Machine save its id and calculate machine cost with oil's cost.
There isn't a calculateCost method in Machine. Machine has the responsability to update its cost when some of its property change.
The problem comes when I need to recalculate machine cost without Oil class (I only have its id). Which is the best way to model this? I have thought some ones but none of them convince me:
1) Inject Oil class in Machine constructor. I think this isn't possible because Oil is an entity and DDD doen't allow it.
2) Inject Oil repository in Machine constructor. As 1, DDD doesn't allow it.
3) Inject Oil value object in Machine constructor. This means that I have to save a OilVO class with a copy of Oil entity instead of only its id when addOil method is called.
4) Build an aggregate. I don't like because Oil have its own id and there are some other classes like Oil that participates in the Machine cost. Machine would become too much bigger.
5) Delegate the oil cost calculator to a domain service. This means that I have to inject this service interface in Machine constructor each time I create a Machine object:
interface OilCostCalculatorService (OilRepository repo) {
...
public double calculateCost (IdOil id) {
Oil oil = repo.getOil(id);
double cost = .... // calculate cost from oil properties
return cost;
}
}
class Machine {
public Machine (OilCostCalculatorService service, ...) { ... }
}
6) Maybe my classes aren't modeled properly, but after thought a lot it doesn't occurr me best way to do it.
Upvotes: 1
Views: 413
Reputation: 57194
The problem comes when I need to recalculate machine cost without Oil class (I only have its id). Which is the best way to model this?
As machine and oil are isolated from one another (separate aggregates), there is no way for machine calculations to use the "current" price of oil -- without a mutual lock, the current price of oil could be changing concurrently with the changes to the machine.
Therefore, machine is working with a cached copy of the oil price.
So one answer would be to include within the machine aggregate a cached copy of the last known oil price, and use that for estimating the cost.
Another possibility is to pass a domain service to the machine, where the domain service is a function that accepts the id of the machines oil, and returns a cached copy of the cost. This looks a bit like your option #5, except that rather than injecting the service into the constructor, you pass it as an argument where you need it.
In either case, you may want to investigate whether your domain needs an explicit model of time (the price of oil as of Monday). In many domains there's the concept of a quote, made at some time and possibly of limited duration, during which a price would be fixed (the provider of the quote basically accepting a risk that any real price change might not be profitable).
Upvotes: 2