Modeling superstate and substate combinations in DDD
I am designing an analytics application using domain driven design and test driven development. I am having difficulty modeling the following requirement: The application shows a workitem's state. States are modeled as a composition of a superstate and a substate. Superstate can be any of the following: "A", "B", "C", "D" Substate can be any of the following: "X", "Y", "Y1", "Y2", "Z" Each superstate has a set of valid substates: Valid substates of "A" are "Y", "Z". Valid substates of "C" are "X", "Y1", "Y2", "Z". All other superstates have the following valid substates: "X", "Y", "Z" I devised a model which includes the following aggregate: Workitem is the aggregate root and Workitem State is a value object. A check at the instantiation of Workitem will ensure that the state has a valid combination of superstate and substate. Since I'm following a TDD approach, the implementation of the state validation logic should be preceded by the definition of a corresponding test(psedudo-code): #test module func test_state_validity(): # tests all combinations of superstate and substate for sup in SuperState: for sub in SubState: try: s = State(sup, sub) assert sub in get_allowed_substates(sup) except InvalidStateException: assert sub not in get_allowed_substates(sup) Then i wrote the following implementation(psedudo-code): # states module enum SuperState: A, B, C, D enum SubState: X, Y, Y1, Y2, Z class WorkitemState: superstate: SuperState substate: SubState def create_state(superstate, substate): if substate in get_allowed_substates(superstate): return WorkitemState(superstate, substate) else: raise InvalidStateException func get_allowed_substates(superstate): switch superstate: case SuperState.A: return (SubState.Y, SubState.Z) case SuperState.C: return (SubState.X, SubState.Y1, SubState.Y2, SubState.Z) case _: return (SubState.X, SubState.Y, SubState.Z) In compliance with DDD, the invariant has been enforced within the Domain Layer (in the aggregate root). In compliance with TDD, a corresponding test has been defined as well. This made me realize there is significant overlap between the validation logic within the domain objects and unit tests (in my case I tried to keep it DRY by having both refer to the function get_allowed_substates). My questions are: is writing tests for the domain model redundant, and thus be avoided? (in general and in this specific case) is it a sensible choice to test all possible combinations of superstate and substate, or should I rather restrict the scope of the test? e.g. all valid combinations plus a few edge cases such as {("A","X"), ("C","Y"), ("B", "Y1")} note: differently from the example, my actual use case has ~100 possible state combinations. This number is unlikely to change in the future, though.

I am designing an analytics application using domain driven design and test driven development. I am having difficulty modeling the following requirement:
The application shows a workitem's state. States are modeled as a composition of a superstate and a substate.
- Superstate can be any of the following: "A", "B", "C", "D"
- Substate can be any of the following: "X", "Y", "Y1", "Y2", "Z"
Each superstate has a set of valid substates:
- Valid substates of "A" are "Y", "Z".
- Valid substates of "C" are "X", "Y1", "Y2", "Z".
- All other superstates have the following valid substates: "X", "Y", "Z"
I devised a model which includes the following aggregate:
Workitem
is the aggregate root and Workitem State
is a value object.
A check at the instantiation of Workitem
will ensure that the state has a valid combination of superstate and substate.
Since I'm following a TDD approach, the implementation of the state validation logic should be preceded by the definition of a corresponding test(psedudo-code):
#test module
func test_state_validity():
# tests all combinations of superstate and substate
for sup in SuperState:
for sub in SubState:
try:
s = State(sup, sub)
assert sub in get_allowed_substates(sup)
except InvalidStateException:
assert sub not in get_allowed_substates(sup)
Then i wrote the following implementation(psedudo-code):
# states module
enum SuperState:
A,
B,
C,
D
enum SubState:
X,
Y,
Y1,
Y2,
Z
class WorkitemState:
superstate: SuperState
substate: SubState
def create_state(superstate, substate):
if substate in get_allowed_substates(superstate):
return WorkitemState(superstate, substate)
else:
raise InvalidStateException
func get_allowed_substates(superstate):
switch superstate:
case SuperState.A:
return (SubState.Y, SubState.Z)
case SuperState.C:
return (SubState.X, SubState.Y1, SubState.Y2, SubState.Z)
case _:
return (SubState.X, SubState.Y, SubState.Z)
In compliance with DDD, the invariant has been enforced within the Domain Layer (in the aggregate root). In compliance with TDD, a corresponding test has been defined as well.
This made me realize there is significant overlap between the validation logic within the domain objects and unit tests (in my case I tried to keep it DRY by having both refer to the function get_allowed_substates
).
My questions are:
- is writing tests for the domain model redundant, and thus be avoided? (in general and in this specific case)
- is it a sensible choice to test all possible combinations of superstate and substate, or should I rather restrict the scope of the test? e.g. all valid combinations plus a few edge cases such as {("A","X"), ("C","Y"), ("B", "Y1")}
note: differently from the example, my actual use case has ~100 possible state combinations. This number is unlikely to change in the future, though.