3

I have an Order entity with a refund() method that uses an abstract factory refundStrategyFactory to create the appropriate refund strategy at run-time:

public class Order extends Entity{

    public void refund(Employee emp, Quantity qty, IRefundStrategyFactory refundStartegyFactory){
        //use the abstract factory to determine the appropriate strategy at run-time.
    }
}

I'm trying to use dependency injection and have IRefundStrategyFactory injected automatically into the refund() method, but I couldn't find a way to achieve this.

I'm not using the constructor injection because Order is an entity and we shouldn't inject services into entities unless it's going to be used temporarily as in the refund method. This is better explained by Misko Hevery's blog post To “new” or not to “new”…:

It is OK for Newable to know about Injectable. What is not OK is for the Newable to have a field reference to Injectable.

A similar answer by Mark Seemann to a related question also advocates the same idea.

Bonus point, Aren't I exposing the internals of the refund() method by exposing its dependency on IRefundStrategyFactory? Should I sacrifice unit testing in isolation and create the factory inside the method itself?

Community
  • 1
  • 1
Songo
  • 5,618
  • 8
  • 58
  • 96
  • Can you be a bit more specific about what your problem is please? What do you mean by "injected automatically into the refund() method"?. Do you mean injected by a dependency injection framework? – lockstock Jul 31 '14 at 00:03
  • @lockstock that's correct. – Songo Jul 31 '14 at 03:12
  • which dependency injection framework are you using? – lockstock Jul 31 '14 at 03:52
  • @lockstock I'm using spring framework. However, I was hoping to get an answer in a language agnostic way. If there is a solution in C# for example that can be ported to Java then I'll work it out. I think my problem is more of a design issue than a technical issue if you know what I mean. – Songo Jul 31 '14 at 04:07
  • Don't forget to mark your favorite answer as 'the answer'. – Steven Oct 31 '16 at 16:00

3 Answers3

1

Well ideally your domain model should be free infrastructure concerns and IOC is a infrastructure concern, it is recommended that the domain model be simple POJO's. So I would not inject beans into my domain model.

In this case i think you should have a application service which gets injected the factory class and it then just passes the factory class as a parameter to your Order class.

Does the factory class use some information from the Order class to decide which kind of strategy class to instantiate ?

Bonus point, Aren't I exposing the internals of the refund() method by exposing its dependency on IRefundStrategyFactory?

I am not sure if there is such a thing as exposing the internals of a method, This concept fits more naturally with "exposing internals of a class", A method does some action and to do so it might need some information from the outside world, we could debate on how to pass in that information and whether that one method is doing too much or not ? ... But i cannot reason on whether a method "exposes its implementation", maybe you should elaborate on what you meant by that point

Sudarshan
  • 8,574
  • 11
  • 52
  • 74
  • 1
    "Does the factory class use some information from the Order class to decide which kind of strategy class to instantiate" : +1, great remark. If it doesn't, the OP would probably be better off just passing the Strategy around instead of a Factory. – guillaume31 Jul 31 '14 at 08:54
  • Ya that is what i wanted to get to once the OP replies :) – Sudarshan Jul 31 '14 at 08:59
1

I'd solve this by starting a refund process with eventual consistency, where you trigger a Refund action. The listeners to this action and it's events would then do their own tasks, i.e. refund the cost of the goods, restock the items, etc. Alternatively you could just do this atomically in an application service that triggers several domain services, one after another, passing in the Order item:

FinancialService.RefundOrderGoods(myOrder);
StockService.RestockOrderItems(myOrder);
...

I'd avoid adding any services or repositories into your entities.

Adrian Thompson Phillips
  • 6,893
  • 6
  • 38
  • 69
1

Your main problem is that you can't "injected automatically into the refund() method", but IMO that isn't a problem at all, since you can simply pass on the dependency onto the method but still use constructor injection in the service that calls this method:

public class ApplyRefundHandler : ICommandHandler<ApplyRefund>
{
    private readonly IRefundStrategyFactory refundStartegyFactory;
    private readonly IRepository<Order> orderRepository;
    private readonly IRepository<Employee> employeeRepository;

    public ApplyRefundHandler(IRefundStrategyFactory refundStartegyFactory,
        IRepository<Order> orderRepository,
        IRepository<Employee> employeeRepository)
    {
        this.refundStartegyFactory = refundStartegyFactory;
        this.orderRepository = orderRepository;
        this.employeeRepository = employeeRepository;
    }

    public void Handle(ApplyRefund command)
    {
        Order order = this.orderRepository.GetById(command.OrderId);
        Employee employee = this.employeeRepository.GetById(command.EmployeeId);
        order.refund(employee, command.Quantity, this.refundStartegyFactory);
    }
}

Aren't I exposing the internals of the refund() method by exposing its dependency on IRefundStrategyFactory? Should I sacrifice unit testing in isolation and create the factory inside the method itself?

Yes you are, but in the same time you are probably violating the Single Responsibility Principle by adding a lot of business logic in the Order class. If you hide the abstractions, it means you need to inject them into the constructor and you will probably find out quickly that your Order class gets a lot of dependencies. So instead you can view the Order's methods as a Single Responsibility (a class in disguise) that has little to no relationships with the other methods in that class, and in that case it makes sense to pass in its dependencies into that method.

As a side node, please be aware that factory abstractions, like your IRefundStrategyFactory are usually not good abstractions. You should consider removing it, as described in detail here.

Steven
  • 166,672
  • 24
  • 332
  • 435
  • Thanks for the answer. Well the idea is that the `refund()` method started out as a series of `if...else if...else if...` which was totally encapsulated and hidden inside the method. Refactoring it to use the strategy pattern made me use a factory to determine the appropriate strategy at run-time. Since I created a dependency I felt I should inject, so that I can supply a mock factory during testing. Why would that violate the SRP? – Songo Jul 31 '14 at 08:50
  • @Songo: Just wait a few months and look at how big the `Order` class has become and how many use cases are implemented in `Order` class. You can't hardly say that 'all use cases for order' together are one responsibility. – Steven Jul 31 '14 at 09:05
  • @Steven: Is this only solution? Doesn't it provide to more complicated API of the entity? What about injecting factory f. ex. as static property of the Order? – Mikz Oct 31 '16 at 15:22
  • @Mikz: This solution is not the only solution, but IMO *the best* solution. Having a statuc property in order leads to the [Service Locator anti-pattern](http://blog.ploeh.dk/2010/02/03/ServiceLocatorisanAnti-Pattern/), so this is a very bad idea. If your entities API becomes very complicated, chances are that you are you are designing things wrong. You might want to start a new question here on SO where you show a detailed (and concrete) example of where you are troubled. – Steven Oct 31 '16 at 15:55
  • @Steven: thank you for your answer. I've posted my question: http://stackoverflow.com/questions/40346857/injecting-service-into-domain-object-once-again – Mikz Oct 31 '16 at 16:58