Value Objects in CQRS - where to use
Asked Answered
E

5

42

Let's say we have CQRS-inspired architecture, with components such as Commands, Domain Model, Domain Events, Read Model DTOs.
Of course, we can use Value Objects in our Domain Model. My question is, should they also be used in:

  1. Commands
  2. Events
  3. DTOs

I haven't seen any examples where Value Objects (VO) are used in the components mentioned above. Instead, primitive types are used. Maybe it's just the simplistic examples. After all, my understanding of VOs use in DDD is that they act as a glue for the whole application.

My motivation:

Commands.
Let's say user submits a form which contains address fields. We have Address Value Object to represent this concept. When constructing command in the client, we should validate user input anyway, and when it is well-formed, we can create Address object right there and initialize Command with it. I see no need to delegate creation of Address object to command handler.

Domain Events.
Domain Model already operates in terms of Value Objects, so by publishing events with VOs instead of converting them to primitive types, we can avoid some mapping code. I'm pretty sure it's alright to use VOs in this case.

DTOs.
If our query-side DTOs can contain Value Objects, this allows for some more flexibility. E.g., if we have Money object, we can choose whether to display it in EUR or USD, no need to change Read Model.

Esquire answered 2/2, 2011 at 0:14 Comment(2)
After pondering this for a while, my conclusion: having behaviour-rich objects in Events is simply impossible, as they must represent historical data, and we have no way to serialize behaviour today. As for Commands and Read Model DTOs, this might work, and it is still unclear to me if the coupling it introduces is acceptable or not (anyway, this is more about 'should both Domain and Presentation layers reference the same implementation of Money VO' rather than 'should TransferMoneyCommand contain Money VO or MoneyDTO' ).Esquire
There is a similar discussion going on on the PHPDDD repository. Feel free to join: github.com/webdevilopers/php-ddd/issues/14Taxexempt
C
31

Ok I've changed my mind. I have been trying to deal with VOs a bunch lately and after watching this http://www.infoq.com/presentations/Value-Objects-Dan-Bergh-Johnsson it clarified a couple of things for me.

Commands and Event are messages (and not objects, objects are data + behavior), in some respects much like DTOs, they communicate data about an event and they themselves encapsulate no behavior.

Value Objects are not like DTOs at all. They are a domain representation and they are, generally speaking, rich on behavior like all other domain representations.

Commands and Events communicate information into and out of the domain respectively, but they themselves do not encapsulate any behavior. From that perspective it seems wrong and a possibly a violation context boundaries to pass VOs inside of them.

To paraphrase Oren (though he was referring to nHibernate and WCF) "Don't send your domain across the wire". http://ayende.com/Blog/archive/2009/05/14/the-stripper-pattern.aspx

If you want to communicate a value object, then I suggest passing the necessary attributes needed to re-construct the VO within them instead.

Original Text (for posterity):

If you are asking if Value Objects can be passed by the domain model to events or passed in by commands, I don't really see a huge problem with the former, though the latter may violate some of the rules of the aggregate root being the "owner" of values.

That said a value object represents concepts like for example a color. You don't have green, you are green or not. There seems to be nothing intrinsically wrong with a command telling you that you are green by passing this.

Reading the chapter from DDD on the Aggregate Root pattern explains Entities and Value Objects quite well and is worth reading over a few times.

Constringe answered 7/2, 2011 at 17:7 Comment(5)
I think it's ok from pure DDD-perspective to have shared VO's, but from cqrs point of view, this can introduce some technical problems, like versioning of events if Event Sourcing is applied. And then the natural question arises, is this really just a technical issue, or is it something intrinsically wrong with the whole idea.Esquire
The understanding I took from the DDD book is that while aggregate roots are the owners of their respective entities and value objects they may pass on a reference to those objects transiently. The sole purpose of the aggregate root is to define the transactional boundaries of the system. Again, I may be misinterpreting some of this and as always YMMV. While it would definitely be bad to serialize an entity as part of an event I can't see any problems for VOs since they should represent a true immutable value.Constringe
"Value Objects are not like DTOs at all. They are a domain representation and they are [...] rich on behavior like all other domain representations." I'd strongly disagree with the latter part. While they are part of the domain (eg. defining what an Address is), they're meant to be disposable and not have any behaviour attached to them. That's why they're usually immutable, to prevent you from wanting to mess with your VO.Juttajutty
Immutability and behaviour are not mutually exclusive concepts. Value objects, for example Money or DateRange would certainly provide some related functionality and logic based on the domain concept it represents. If it was simply data the built in basic types would suffice. See: martinfowler.com/eaaCatalog/valueObject.htmlConstringe
Here's a nice comparison of Value Object and DTO: enterprisecraftsmanship.com/2015/04/13/…Eyesore
S
5

I say it's a bad idea.

There's a reason we don't do the same with entities - to avoid coupling other parts of the system to the domain (in the wrong places). The same is true for Value Objects, the only difference between value objects and entities is lifetime and ownership - these differences do not affect how we should and should not couple to them.

Imagine that you make an event contain a VO. A change in your domain requires you to change that VO. You've now boxed yourself into a corner where your event is also forced to change, ditto for any Commands or DTO's it's a part of.

This is to be avoided.

Use DTO's and/or primitives. Map them (AutoMapper makes it a 1-line deal).

Severini answered 2/2, 2011 at 0:35 Comment(7)
Well, those VOs wouldn't belong solely to Domain Model. They would be a shared knowledge across all system components (like MyApp.Core assembly which may be referenced by anyone). As long as the concepts there are stable, or even, have no breaking changes, I guess this should be fine. Ofc, if we use Event Sourcing, this would clutter our code with legacy concepts which are no longer in use but are part of old events. So this is one drawback I can think of. But still, the idea of having a module of shared concepts (like Currency, Money, Velocity) across application sounds so tempting to me.Esquire
If they are shared concepts across applications, I would say they aren't so much part of the domain (as the domain, practically by definition, is specific to the application) as they are infrastructure or common code. I was thinking that about Money specifically when I answered.Severini
I also feel that VO in CQRS is a bad idea, but then, how can we encapsulate domain logic when aggregate become to complex?Charlottcharlotta
@driushkin, did you ever come to a conclusion of how to treat these "shared concepts"? I have the same question and am leaning to making the DTOs the shared concept. I know this is a very old thread, but maybe you are still tracking it.Weathers
Same here @StefandeKok. Stumbled on this old post. I'm curious whether to comprise my commands with VOs or leave them flat (primitives). Such a tough call!Clockmaker
Because my VOs contain some validation logic. What's the point in keeping the commands primitive if you are just going to copy the validation (from the VOs) and perform it beforehand, and then again when the command is handled. A user shouldn't have to wait to learn that things like Address, Phone, Email, Money didn't validate IMHO. But I also understand this is can be leaky. So tough call!Clockmaker
@prograhammer, I ended up keeping value objects completely within the domain.Weathers
M
3

Similar to other answers, in SOA this would break encapsulation of the service as the domain is now leaking out.

Magdeburg answered 17/2, 2011 at 3:33 Comment(0)
L
0

According to Clean Code your DTOs are data structures (just to add another term), while value objects are objects. The difference that objects can have behavior. Mixing data structures with objects is generally a very bad idea, because it will be hard to maintain the hybrid you get.

I don't feel right to put value objects to DTOs from an architecture perspective as well. The value objects are inside of the domain model while the DTOs you mentioned are defining the interface of the model. We usually build an interface to decouple the outside world from the inside of something. So in the current case we added DTOs to decouple the outside world from the value objects (and other model related stuff). After that adding value objects to the interface is crazy.

So you haven't met this solution yet because it is an anti-pattern.

Lair answered 25/9, 2014 at 5:33 Comment(5)
So we should not add any kind of String or Integer to DTO, since it is also value object?Thallic
@bojanv55 Strings and integers are primitives, not value objects. A Date or DateTime object would have been a better example. I meant that DTOs should not transfer VOs defined in the domain. For example we have a VO in our domain related to the birth of somebody, e.g. 1985-12-20. If we query the birthday, then we should return 12-20 in the DTO. If we query the birth date, then we should return 1985-12-20 in the DTO. If we query the age, then we should return 31 years in the DTO. So the DTO won't contain the entire VO with its logic, just the representation we really needed.Lair
@bojanv55 At least that's how I use DTOs and VOs. This approach worked so far, but ofc. I am not infallible. :-)Lair
sure you can do whatever you want :) I just found that using VOs in DTOs helps with keeping the code tidier - e.g. when creating events instead new Event(month, day, year) I can say new Event(BirthDay). Also, since events are part of domain makes sense to use VOs there...Thallic
@bojanv55 You can use a vanilla Date object in your DTO if you want. The DTO does not have to be flat. This whole thing is about creating an interface from DTOs to decouple the outside of the domain from the inside of the domain. The aggregates, entities, value objects are inside the domain, while the commands and events are part of the interface of the domain. So the outside world won't depend on the inside (aggregates, entities, VOs), just on the interface (DTOs). You follow the same concept on a smaller scale by defining interfaces and classes...Lair
M
0

Value Objects are or at least should be immutable. Once instantiated with a value that value will never change throughout the lifetime of the object. Thus, passing VOs as data to DTOs (such as Events) should not be an issue since all you can do with them is get their value. At the most their value in a differing representation such as toString() as opposed to an original getValue() which might return an integer or whatever the value is.

Miso answered 13/12, 2019 at 12:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.