Greg Young was the first speaker at Skillsmatter's DDD eXchange and was talking to us about how functional programming and Domain Driven Design can work together peacefully.
The origins of DDD lay firmly in the Object Oriented world. The Domain Models in our code are linked directly to the Ubiquitous Language, the code and the language we use to describe the domain are as one. Therefore in the object world the objects are the nouns and the methods are the verbs. So how does this work in a functional context where there are only functions? I think that there are still a few issues that Greg has not fully answered in this but I'm hoping to play with the ideas in the future to really understand my own doubts
So, assuming that we have an domain around eCommerce we may well have the concept of a InventoryItem which is something you can sell. Assume someone wants to deactivate the item:
public class InventoryItem
{
...
public void Deactivate(string reason)
{
if (!_active) throw new AlreadyDeactivatedException(_id);
_active = false;
}
...
}
At the moment that this is not even event sourced, but we also want to think about splitting apart the logic that decides whether the state change can occur from the actual state change.
public class InventoryItem
{
...
public void Deactivate(string reason)
{
if (!_active) throw new AlreadyDeactivatedException(_id);
DoDeactivate(reason);
}
private void DoDeactivate(string reason)
{
_active = false;
}
...
}
It is easy to make things Event Sourced from this position, we refactor further to extract out the parameters of the method into a class that represents the state change:
public class InventoryItem
{
...
public void Deactivate(string reason)
{
if (!_active) throw new AlreadyDeactivatedException(_id);
DoDeactivate(new InventoryItemDeactivated(_id, reason));
}
private void DoDeactivate(InventoryItemDeactivated event)
{
_active = false;
}
...
}
public class InventoryItemDeactivated : Event
{
public InventoryItemDeactivated(string reason)
{
...
}
}
When we have separated the code out in this manner we can re-build the state of the object by applying methods in sequence:
var item = new InventoryItem();
item.Created(...);
item.PriceUpdated(...);
item.Deactivated(...);
Also, we are now independent of any logic that may be imposed, any calculations. We are simply involved in state change but what that state change means can vary over time.
Greg argues that when you have made your model Event Sourced then the Events are representations of method calls, they are the serialized function calls of the DoXXX methods that change state not the public methods that decide whether or not to allow the state the change.
Let's make another change or two, assume that the InventoryItem is an immutable...
public class InventoryItem
{
...
public void Deactivate(string reason)
{
if (!_active) throw new AlreadyDeactivatedException(_id);
DoDeactivate(new InventoryItemDeactivated(_id, reason));
}
public InventoryItem DoDeactivate(InventoryItemDeactivated event)
{
return new InventoryItem(this, false);
}
...
}
public class InventoryItemDeactivated : Event
{
public InventoryItemDeactivated(string reason)
{
...
}
}
One advantage of this is that now we can re-build the state of the object at any time by chaining together method calls...
new InventoryItem()
.Created(inventoryItemCreated)
.PriceUpdated(inventoryItemPriceUpdated)
.Deactivated(inventoryItemDeactivated);
I would apply a simple refactoring here and given that the context of the method call is given in the event then we can call the methods Apply:
new InventoryItem()
.Apply(inventoryItemCreated)
.Apply(inventoryItemPriceUpdated)
.Apply(inventoryItemDeactivated)
So, what if we each function returns a current state of the InventoryItem then we can remove the need for the objects and we can, in a functional language, simply chain calls together to get to the current state of the item:
Apply(
Apply(
Apply(inventoryItemCreated),
,inventoryItemPriceUpdated),
inventoryItemDeactivated);
So, I've be focussed on re-hydration of the current state from Events but now what happens when we issue a command? Greg explained that these methods would return the event to represent the state change...
evt = Reactivate(
Apply(
Apply(
Apply(inventoryItemCreated),
inventoryItemPriceUpdated),
inventoryItemDeactivated))
currentState = Apply(
Apply(
Apply(
Apply(inventoryItemCreated),
inventoryItemPriceUpdated),
inventoryItemDeactivated),
evt)
Greg has a habit of killing my illusions, and I don't think that it is just me who takes a look at his work and thinks "hey, is it really that simple?" but I think he just looks at the problems from a different perspective. There are things that I have yet to be convinced about in terms of functional DDD: