Service behaviour in Entity - how to avoid service injection into entity?
I have an entity structure as follows:
IManager: IDeletable
{
IEnumerable<IFund> Funds {get;}
IFailureNotification Delete();
}
IFund : IDeletable
{
IEnumerable<IFundClass> FundClasses
IFailureNotification Delete();
}
IFundClass: IDeletable, IInvestable
{
IFailureNotification Delete();
}
And I have a service which takes an IDeletable
and calls Delete on it. Depending on the return value it then either commits the transaction or rolls it back. I'm using N开发者_如何学PythonHibernate to persist the classes so can't put RI in the DB and catch the exception (which I wouldn't like anyway).
This is a classic case for polymorphism and the Manager
loops through its Funds
and deletes them before deleting itself, the Fund
in turn delete the FundClass
es before deleting themselves, so the service can just take any entity implementing IDeletable
and know that the deletion will perform the appropriate actions at all levels.
Here's the problem: The fund classes need to find if they're being used in a completely separate context using the IInvestable
interface which they don't know anything about. This requires a service - the IInvestmentCalculationService
.
Obviously I don't want to inject the InvestmentCalculationService
into the fund class entity constructor and I don't want to inject it into the delete method as this is on Funds and Managers as well as many other classes so doesn't make any sense - also means that as soon as we have more requirements we have to change the delete methods on everything.
I'm a bit tempted by the Domain Events model here: http://www.udidahan.com/2009/06/14/domain-events-salvation/ but I'm not confident that it's right as I'm trying to get data back from the triggered event handler - which would work but smells a little bit wrong, and all the examples only show fire and forget situations.
Does anyone have any suggestions?
Thanks to all for their help, I was really impressed by the audience I attracted! I especially liked mcintyre's philosophy and it's genuinely affected my thinking since. In this case though we went for double dispatch. Feels a little more stable.
Cheers
"Obviously I don't want to inject the InvestmentCalculationService
".
Its the word 'obviously' that I don't like. I still haven't felt compelled by the results from googling "injecting services into entities". The top posts on the subject boil down to "it doesn't feel right and you can use domain events/double dispatch to do it anyway so don't do it".
I personally think it's fine to inject services into entities and reckon you should stop worrying about it and do it. Maybe don't inject the whole InvestmentCalculationService, but inject the BitsOfInvestmentCalculationServiceThatINeedToKnowAboutService if you feel the entity doesn't need to have access to the whole thing.
Domain events is no good in your situation unless you add a return value (which basically makes it into a dressed up service locator) and with double dispatch, the thing you are injecting has to come from somewhere higher up the call stack - probably an injected value to the entry point class which in all likeliness doesn't use that dependency.
Just inject the InvestmentCalculationService into the entity and get on with your life.
One thing we've done in situations like these is have the Delete not do the actual deletion, but instead use a collecting parameter for things to delete. The Delete() method would register itself and any other objects, which then get replayed by another service.
How about having an interface
public interface ICanBeDeleted<T>
{
bool CanBeDeleted(T itemToBeDeleted);
}
Before actually deleting ask your container for all implementations of this interface, invoke the CanBeDeleted
function and if any return false then do not delete. Your InvestmentCalculationService
would implement ICanBeDeleted<FundClass>
and register with the container.
精彩评论