Design Patterns – How to Implement State Machine Pattern on Aggregate Root

design-patternsdomain-driven-design

I am modeling an aggregate root, which has several actions that perform operations against other entities, as you'd expect. The aggregate, however, has a state, and several of these operations can only be performed when the aggregate is in a particular state.

I created an implementation of the state pattern, so that the aggregate would simply delegate the action to the state concrete object. However, now that I have implemented it, I found meself with the following concerns:

  • There are operations that can be invoked in more than one state, thus I ended up repeating implementations.
  • There are operations that generate domain events, so I had to pass the root's event collection so they can add the events properly.
  • Some operations require access to the private members of the aggregate root, so I ended up either declaring them as internal (C#) or creating internal methods that modify the private members.

So now I'm wondering whether the implementation was worth it, or if the state object should only have CanPerformOperation1 properties, and let the aggregate root check this property and if false, throw an InvalidOperationException.

The following code is a summary of what I'm trying to attempt.

interface IState {
    void Register(DomainName domain, CustomerCode code);
    void Activate(ActivationManifest manifest);
    void Lock();
    void Unlock();
    void EnsureConsistency();
}
class NewState : IState {
    // can only call Register method, transitions to RegisteredState
}
class RegisteredState : IState {
    // can only call Activate method, transitions to ActiveState
}
class ActiveState : IState {
    // can call Lock or EnsureConsistency
    // Lock transitions to locked state
    // EnsureConsistency can transition to RestrictedState or ActiveState
}
class LockedState : IState {
    // can only call Unlock, transitions to ActiveState
}
class RestrictedState : IState {
    // can only call EnsureConsistency, which can transition
    // to ActiveState or RestrictedState
}

class Tenant {
    private IState _state = new NewState(this);
    private readonly UserAccountCollection _accounts;
    private readonly LicenseCollection _licenses;
    private readonly ApplicationCollection _applications;

    // had to make these internal accesors to be used by
    // EnsureConsistency in ActiveState and RestrictedState
    internal UserAccountCollection _accounts => _accounts;

    internal Application RegisterApplication(AppKey key, UserAccount admin){
        // this method is called by the RegisteredState.Activate method
        // so what's the point of delegating?
    }
    internal License RegisterLicense(LicenseKey key) {
        // this method is also called by the RegisteredState.Activate
        // method, just like the one above.
    }
    // etc
}

Now, this will only increase in complexity, as the customer requires me to add more methods that depend on state. So I was just wondering whether I should just add properties like CanRegisterApplication, CanRegisterLicense, etc., and then the states will only be acting as a flag switch.

What would be a proper way to implement what I'm trying to achieve? Or maybe I'm getting the state pattern wrong?

Best Answer

IMHO, "customer requires me to add more methods" is the exact reason why we need the state pattern in the first place. Without state pattern, it's likely that you need to repeat the switch or if/else in the new added methods.

From the implementation point view, abstract class is much easier than interface in state pattern.

Regarding your access modifier, you can make the each individual state as internal class of the client (Tenant class in your case, see nested types). By this way, you actually have better encapsulation as the external world doesn't need to know the actual state detail.

abstract class State {
    void Register(DomainName domain, CustomerCode code){};
    void Activate(ActivationManifest manifest){};
    void Lock(){};
    void Unlock(){};
    void EnsureConsistency(){};
}


class Tenant {
    class NewState : State {
        // can only call Register method, transitions to RegisteredState
    }
    class RegisteredState : State {
        // can only call Activate method, transitions to ActiveState
    }
    class ActiveState : State {
        // can call Lock or EnsureConsistency
        // Lock transitions to locked state
        // EnsureConsistency can transition to RestrictedState or ActiveState
    }
    class LockedState : State {
        // can only call Unlock, transitions to ActiveState
    }
    class RestrictedState : State {
        // can only call EnsureConsistency, which can transition
        // to ActiveState or RestrictedState
    }
    private State _state = new NewState(this);
    private readonly UserAccountCollection _accounts;
    private readonly LicenseCollection _licenses;
    private readonly ApplicationCollection _applications;

    // Should this go to abstract class State 
    private UserAccountCollection _accounts => _accounts;

    private Application RegisterApplication(AppKey key, UserAccount admin){
        // Should this go to abstract class State 
    }
    private License RegisterLicense(LicenseKey key) {
        // Should this go to abstract class State 
    }
    // etc
}

P.S: In java, people usually use enum to implement state pattern. However, .net doesn't allow polymorphism in enum.

Related Topic