Can domain model uses value objects in the state transition part?

128 views
Skip to first unread message

José Luis Martinez de la Riva Manzano

unread,
Dec 9, 2016, 7:48:10 AM12/9/16
to DDD/CQRS
In domain models that use event sourcing the behavior and state transition are separated. 
The behavior creates events; events are public schema so events holds primitive values, then no value objects hold inside.
The state transition apply the event to move the domain model to another state; therefore no exception must be thrown here due the assertions and checks are verified in the behavior.

The behavior part of the domain models use value objects inside to help to fulfill its goal. Value objects could throw exceptions; in this part is ok. 

Can domain model in the state transition part uses value objects? 
- If yes, then we need to rebuild the VO from the event with the risk related to throw exceptions.
- If no, then we assume that domain model state only holds primitive values and in next behavior calls we'll need to create VO from then.

Thanks.
Jose.

Kyle Cordes

unread,
Dec 9, 2016, 8:22:15 AM12/9/16
to ddd...@googlegroups.com
On December 9, 2016 at 6:48:12 AM, José Luis Martinez de la Riva
Manzano (martinez...@gmail.com(mailto:martinez...@gmail.com))
wrote:

> The behavior creates events; events are public schema so events holds primitive values, then no value objects hold inside.

> Can domain model in the state transition part uses value objects?


Value objects are “just data” and seem completely harmless in nearly
any context. Why would you exclude their use from either events or
domain code?




--
Kyle Cordes
kyle....@oasisdigital.com

João Bragança

unread,
Dec 9, 2016, 9:20:40 AM12/9/16
to ddd...@googlegroups.com
Because your value objects have a different reason to change than your message contracts. Also, unless your domain model will always be completely in memory, how would you serialize them without breaking encapsulation?

To address the original question, it's perfectly ok to use value objects inside your aggregates. Your aggregates should know if you have a new version of a value object and react accordingly.

On Fri, Dec 9, 2016 at 2:22 PM, Kyle Cordes <kyle....@oasisdigital.com> wrote:
On December 9, 2016 at 6:48:12 AM, José Luis Martinez de la Riva

wrote:

> The behavior creates events; events are public schema so events holds primitive values, then no value objects hold inside.

> Can domain model in the state transition part uses value objects?


Value objects are “just data” and seem completely harmless in nearly
any context. Why would you exclude their use from either events or
domain code?




--
Kyle Cordes
kyle....@oasisdigital.com

--
You received this message because you are subscribed to the Google Groups "DDD/CQRS" group.
To unsubscribe from this group and stop receiving emails from it, send an email to dddcqrs+unsubscribe@googlegroups.com.
Visit this group at https://groups.google.com/group/dddcqrs.
For more options, visit https://groups.google.com/d/optout.



--

José Luis Martinez de la Riva Manzano

unread,
Dec 9, 2016, 10:44:05 AM12/9/16
to DDD/CQRS, joao...@braganca.name
So, I'm right if the lifecycle of a VO with behavior and state transition separated are:

behaviour: VO -> primitives (event)
state transition: primitives (event) -> VO

Maybe the following sample clarify a little:

class Pocket
{
    private Guid _id;
    private Money _total;   // value object

    public void add(Money m)
    {
        if (_total.add(m).amount() > 100) {
            throw new PocketReachesMaxCapacity();
        }

        ApplyChange(new MoneyAddedToPocket(_id, m.amount(), m.currency()); // 1. Convert VO to primitives
    }

    private void Apply(MoneyAddedToPocket e)
    {
        m = new Money(e.amount(), e.currency());    // 2. Reconstitute VO from primitives
        _total = _total.add(m);
    }
}


@Kyle regarding events and public schema, you could get further info at https://groups.google.com/forum/#!searchin/dddcqrs/value$20object$20refactor|sort:relevance/dddcqrs/LNNIDqT7Kvc/Ifx6XKm-tKcJ

Thanks,
Jose.


El viernes, 9 de diciembre de 2016, 15:20:40 (UTC+1), João Bragança escribió:
Because your value objects have a different reason to change than your message contracts. Also, unless your domain model will always be completely in memory, how would you serialize them without breaking encapsulation?

To address the original question, it's perfectly ok to use value objects inside your aggregates. Your aggregates should know if you have a new version of a value object and react accordingly.
On Fri, Dec 9, 2016 at 2:22 PM, Kyle Cordes <kyle....@oasisdigital.com> wrote:
On December 9, 2016 at 6:48:12 AM, José Luis Martinez de la Riva

wrote:

> The behavior creates events; events are public schema so events holds primitive values, then no value objects hold inside.

> Can domain model in the state transition part uses value objects?


Value objects are “just data” and seem completely harmless in nearly
any context. Why would you exclude their use from either events or
domain code?




--
Kyle Cordes
kyle....@oasisdigital.com

--
You received this message because you are subscribed to the Google Groups "DDD/CQRS" group.
To unsubscribe from this group and stop receiving emails from it, send an email to dddcqrs+u...@googlegroups.com.

Visit this group at https://groups.google.com/group/dddcqrs.
For more options, visit https://groups.google.com/d/optout.

Kyle Cordes

unread,
Dec 9, 2016, 11:12:00 AM12/9/16
to ddd...@googlegroups.com
On December 9, 2016 at 9:44:07 AM, José Luis Martinez de la Riva Manzano (martinez...@gmail.com) wrote:
So, I'm right if the lifecycle of a VO with behavior and state transition separated are:

behaviour: VO -> primitives (event)
state transition: primitives (event) -> VO

Maybe the following sample clarify a little:

class Pocket
{
    private Guid _id;
    private Money _total;   // value object

    public void add(Money m)
    {
        if (_total.add(m).amount() > 100) {
            throw new PocketReachesMaxCapacity();
        }

        ApplyChange(new MoneyAddedToPocket(_id, m.amount(), m.currency()); // 1. Convert VO to primitives
    }

    private void Apply(MoneyAddedToPocket e)
    {
        m = new Money(e.amount(), e.currency());    // 2. Reconstitute VO from primitives
        _total = _total.add(m);
    }
}


@Kyle regarding events and public schema, you could get further info at https://groups.google.com/forum/#!searchin/dddcqrs/value$20object$20refactor|sort:relevance/dddcqrs/LNNIDqT7Kvc/Ifx6XKm-tKcJ


I am skeptical that the code and process you describe above is generating enough business value to pay for itself. Working functionality is an asset, but lines of code are a liability. It seems like we spend our careers writing the same code again and again, doing push-ups shuffling data back and forth between one kind of object in another, between one tier and another. So I strongly resist any development mechanism that requires doing much of that by hand.  Not so much on philosophical grounds (If creating this kind of duplication creates value, let’s do it!), but because I feel that doing it leaves me vulnerable to being steamrolled by a competitor who finds a way not to.

A value object, that’s just a data type, that’s just a way of grouping fields semantically. There are issues including the ones brought up in that linked thread. If we choose to use value objects as part of your events, the exact shape of those value objects (the names and types of the fields) the come part of the “schema” of the event history, and therefore need to be immutable/versioned/casted just like events themselves. But is that worse than exploding value objects into yet another different set of classes to represent the same things, in the event schema?

In the example code snip above, we just have to decide whether a Money is part of the event schema or not. If it is, treat it as such, and if we change its definition the future we will need to write a way to cast it and so on. If it’s not, then we need to do all the shuffling above every time we use one. Something like Money clearly falls on the “just use it” side of things, for me anyway. Your mileage may vary of course, depending on the likelihood of the shape of a Money changing in the future for the problem domain. If it’s likely we will need to change that definition, then we be signed up to create a MoneyV2 or whatever, ouch.

I really like Greg’s notion of defining the event schema in some cross language form, then generating the definitions we need for whatever languages we are going to work with those events. I’ve done that myself spanning JavaScript and Java, and some other groupings that I’ve forgotten the details of from past projects. When defining such a schema, the value objects we plan to use as part of events belong in that same schema mechanism, while value objects that we're using only outside of the events don’t need to be there.

Hope this helps?

--
Kyle Cordes

Alexander Langer

unread,
Dec 9, 2016, 11:44:17 AM12/9/16
to ddd...@googlegroups.com
Just my two cents: The code shown below really looks very similar to
code we usually write, particularly the assembly of the value object
(Money) from primitives in the event, the public interface (add()), the
internal "ApplyChange()" state transition, which itself then uses the
internal Apply() method (which is also used for rebuilding state from
the event stream).

I'd be very happy to work with such code.

On 09/12/2016 16:44, José Luis Martinez de la Riva Manzano wrote:

> class Pocket
> {
> private Guid _id;
> private Money _total; *// value object*
>
> public void add(Money m)
> {
> if (_total.add(m).amount() > 100) {
> throw new PocketReachesMaxCapacity();
> }
>
> ApplyChange(new MoneyAddedToPocket(_id, m.amount(),
> m.currency()); *// 1. Convert VO to primitives*
> }
>
> private void Apply(MoneyAddedToPocket e)
> {
> m = new Money(e.amount(), e.currency()); *// 2. Reconstitute
> VO from primitives*
> _total = _total.add(m);
> }
> }
>

@yreynhout

unread,
Jan 5, 2017, 6:23:57 AM1/5/17
to DDD/CQRS
Hmmmm ... your and my definition of value objects are very much at odds. I'm both a model and contract first type of person, but when working in the model, I don't want to be thinking about the contract too much. Some value objects are more valuable than others, I'll acknowledge that. Yet, I have to come across the first person that is proficient in crafting them without practice. Hence, why in general, I tend to tilt towards creating "more" value objects than strictly necessary. It opens the door to dry-ing up guarding/constraining logic all over the place, makes you think of what a good representation is using the primitives at your disposal (or other value objects once we venture into composition), makes you think of what the "value" is, opens the door to renewed insight as you play with them and move logic around, trying to embed domain language in their collaborating behavior, stimulate closure of operations, etc ... That said, some examples are trivial and we could debate whether or not a primitive wouldn't have done the same job. Personally, I tend to stay clear from the primitives of my programming language as much as possible when working on a model. Yet most people I get to work with tend to obsess over their primitives, so I'm used to being met with resistance on this particular subject.
On the contract side, I tend to put on another hat, looking for a serialization format that embraces versioning (avro, protobuf, etc), opting for micro-formats, standards and prior art if available, which may be very different representations from the value object's internals. The mapping between both the model constructs and contract constructs is a seam I like control over. Pushing it even further, I guess it's no longer seen as beneficial to write your own serialization, given the proliferation of reflection based serialization libraries/fxs, but oddly enough I've done that on multiple occasions while still delivering on time. Not that I'm trying to promote such practices.
What often gets ignored in this type of debate is that some models naturally tend to favor data orientation while others clearly benefit from behavior orientation. Some models have complexity on the inside, others have complexity on the interaction side. Knowing which one you're dealing with might make the choice of what to invest in easier.

Disclaimer: This is my personal opinion, I'm not trying to convince anyone of how to go about these things.
Reply all
Reply to author
Forward
0 new messages