Code should be self-explaining, although I bet you’re wondering about 2 things:

Why TransferNumber gets a Guid?

Why there’s no VO implementation for the creation date?

Well, let me start with no. 2. The creation date doesn’t need encapsulation as there are no actual business constraints (in this example). Why complicate the implementation with another class?

TransferNumber acts as a natural id for the Transfer, however, for technical purposes, I prefer to have a technical id too, hence the Guid. We’ll be using it in situations where we need to specify the entity, but we don’t need a full Value Object, for example in messages.

A nice, flattened data structure, containing valid data. Valid, because it’s the Aggregate Root (AR) who’s in charge of its generation. Now, I’m going to use a more “exotic” approach for the AR’s implementation.

Wait… what?! Static class??? Static function???!!! Say what?! Yeah, it’s called a functional approach. I prefer a hybrid OOP-FP style to code DDD models, especially ARs. Why? Because it’s the simplest implementation. Remember that AR is a role , and the function is the implementation. It makes little sense to make it a full class, when a function is sufficient.

Now, sure, I may have another command case involving Transfer but you see, each model is relevant to one case only and I want my code to reflect that. Basically,I want some boundaries between cases, so the class Transfer is the concept and each static method is an AR enforcing a specific model. Things being static it means they shouldn’t share any state. They’re grouped together only as a convenience.

Our AR makes sure the business rules are respected and then it communicates the change by generating and returning a domain event.

This is not the only way to implement our model, but I like it since it keeps things simple and our code is Event Sourcing friendly. In a future post, when we’ll talk about Application Services, we’re going to see how the AR is used by the service and what to do with the event.