views:

136

answers:

4

Hi,

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 NHibernate 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 FundClasses 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?

A: 

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.

Jimmy Bogard
Hi Jimmy, I'm not sure to how this solves the problem. How does the second deletion service know to check if a `FundClass` is invested? Does this not end up with if (x is IFundClass) then _fundClassChecker.check(x); ?One possible solution that I thought of this morning is to inject in a `IDeletionValidationFactory` into the Delete method. Then the fund class can call `GetFundClassDeletionValidator` and use this check that it is OK for deletion.This feels like a slightly abstracted version of double dispatch to me. What do you think of this?Thanks for your attention.
Stu
A: 

"Obviously I don't want to inject the InvestmentCalculationService".

Its the word 'obviously' that I have a problem with. 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.

mcintyre321
A: 

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.

Paul Linton