Domain Entity Design – How to Design a Domain Entity That Uses a Dependency to Manage a State Field

dependency-injectiondomain-driven-design

I'm new to DDD and IOC/DI and I'm having some trouble figuring out how to design an entity that needs to use a state pattern to manage its status. As the transitions are somewhat complicated, I'm using a finite state machine (FSM) to handle the states (I'm using Stateless).

To further complicate the situation, the FSM needs to be loosely coupled to an entity. I.e. while I know that the state of the entity must be handled by a FSM, I don't necessarily know exactly what the FSM is (i.e. the entity doesn't know what states/triggers/transitions it might go through). The users use the term workflow to define the FSM (state, triggers, transitions, rules etc). The workflow changes independently of the entity. They might want to add new states, change rules, triggers etc. independent of any changes to the entity.

I'm handling this by dynamically loading an assembly that contains a definition of the FSM (which implements a known interface), then using a service that is injected into the entity, the entity calls the service and gets the FSM it is using. This involves potentially loading assemblies, caching the factory that creates the FSM and then assigning the FSM that is in the appropriate state to the entity. Finally, the FSM can return the set of valid triggers that can be provided to the UI so one of the those triggers can be selected and fired. The actual workflow in completely encapsulated externally to the entity.

So, currently I'm my entity looks something like this:

public class EntityUsingWorkflow
{
  private readonly workflowService;

  public Order(IWorkflowService service, string workflowKey, string status)
  {
    this.workflowService = service;
    this.WorkflowKey = workflowKey;
    this.Status = status;

    // Note that the two lambdas are used to retrieve
    // and set the state on the entity.
    this.Workflow = service.GetStateMachine(workflowKey, () => this.Status, stateValue => this.Status = statValue );
  } 

  public string WorkflowKey { get; set; }
  public IStateMachine Workflow { get; private set; }
  public string Status { get; private set; }

}

I'm concerned with this design. My basic understanding is that domain entities should not have dependencies injected into them, yet here I am injecting one. I also understand that the entity needs to handle its own state, so I'm hesitant to move the workflow completely out of the entity. Other ideas I've had, like an init method, would just move the injection to method injection.

What would be a clean way to design an entity with these requirements? Is it OK to inject a dependency in this situation?

Best Answer

It's perfectly fine - indeed, it's a good idea - to use dependency injection in your domain model.

You should to develop your domain model independently of any particular technology or library. The particulars of such-and-such database or so-and-so library are not the concern of your domain model: if you need to swap databases your business rules will remain the same. So we apply the dependency inversion principle and depend on interfaces which address the concerns of the domain model and adapters which adapt a given technological concern to the domain. (Importantly, the interface is phrased in terms of domain-level operations and lives in the same package as your domain classes.)

A great example of this is the Repository pattern. The Repository adapts the database to an interface which suits the domain model. So you can write customerRepository.FindByEmailAddress(email) and not worry about the exact SQL statements that retrieve the customer for you. And by depending on an interface, your domain model has no idea whether it's a SQL database, a file, or MongoDB or Neo4j, or whatever.


So the Dependency Inversion Principle, and Dependency Injection, is of tremendous use in DDD. What I think is a less good idea is keeping your domain in an IOC container. The problem is that you end up storing domain-level information (like how to attach an order to a customer) away from your domain classes in a configuration file somewhere. IOC hides details that shouldn't really be hidden from your domain model. Also, the details of your particular container tend to leak into your domain classes.

Use IOC at the boundaries of your system, if you must, to wire up command handlers and other API services. Use Dependency Inversion and Dependency Injection in your domain model whenever it makes sense to do so, but keep IOC out of the picture.

Related Topic