Entity or Value object (specific case)

261 views
Skip to first unread message

Denny Crane

unread,
Jan 22, 2018, 6:46:25 PM1/22/18
to DDD/CQRS
Hey all,
 
I'm kind of wrestling semantics here in an particular scenario, trying to figure out if this object should be an entity or value object. The object in question is a "Transaction" that comes in from a different service/bounded-context. Now, I realize that value objects are generally thought of in a value context, like an RGB color or monetary unit, etc, but I've seen a lot of different on opinions on how to look at it. Generally speaking:

Value Objects:
- Equality is based on the state of its properties rather than a unique ID
- The object has a 'value' semantic, thus by changing the value of a property, you're technically creating a new value, thus a new instance should be created (Immutability)

Kay... but...

Transactions are interesting because while they have a unique ID (which would make you immediately think 'Entity'), they cannot change. They are immutable by business standards. But they have quite a bit of state in them such as a potentially non-standardized list of properties or metadata (think the difference between sales vs. service transactions), but those values cannot ever change. 

I have a service that receives events that a transaction was brought into the system. This happens in a different service, but I still need to place this into a domain object before the domain can process it. I'm trying to decide whether it should be modeled as an entity or a value object. I'm leaning towards a value object because it should be immutable and it has no behavior; it's simply a bag of properties that is used by domain services to perform a task -- but I feel like some might say that it should be an entity because it is globally unique and identifiable. I want to say that within this service, it is a value object, but within the ingestion service

What is your opinion and why?

Rickard Öberg

unread,
Jan 22, 2018, 7:10:02 PM1/22/18
to ddd...@googlegroups.com
The only thing that really matters is what is the purpose of your model. That is the guide for this decision, in every case. Models are not about reality, models are either useful or useless. So if letting a transaction be a value in your system model makes it useful, power to ya.

Sent from my iPad
--
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.

Denny Crane

unread,
Jan 22, 2018, 7:29:09 PM1/22/18
to DDD/CQRS
I think that's a generic truth, but with DDD, inside of the domain, I should be able to see the business process taking place using objects that the business can talk about with the engineers. So to some degree, it actually is about reality. In this case, the transaction is one of those items that is being described by the domain expert and should be represented as such. Other parts of the discussion include "The survey gets these values from the transaction" so imo, both of these things should be modeled to reflect the business's understanding of those objects. I think this is one of those key differences in DDD vs. "practical" design. 

My actual question here is really about choosing entity or value object for its implementation.

Alan Bem

unread,
Jan 22, 2018, 10:32:45 PM1/22/18
to ddd...@googlegroups.com
Well, you stated that this transaction has an id. Is your other bounded context allowing duplicate ids? If no Transaction is clearly an entity, if yes then Transactions id is not really id.

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.



--
Pozdrawiam,
Alan Bem

Denny Crane

unread,
Jan 22, 2018, 11:56:43 PM1/22/18
to DDD/CQRS
No duplicate ID's. Is it *clearly* an entity though? It seems to me that if the other bounded context was written using DDD (it's not) then it would be an entity in that context but potentially a value object in this context. I seem to recall reading somewhere that the model could and would change between contexts but I can't find where I read it. You're thinking what I was initially thinking, but I don't know that something simply having an ID is enough to justify it as being an entity, particularly when that ID belongs to a different context and the value of such an object can never change, and it has no behavior and thus does not necessarily require an aggregate root. While the ID will be recorded for integrity sake, it means nothing and will never be used for anything in this context. This is the subtlety that I am struggling with.
Pozdrawiam,
Alan Bem

Denny Crane

unread,
Jan 23, 2018, 12:13:22 AM1/23/18
to DDD/CQRS
Ah maybe it was this, it has a reference to the book: https://stackoverflow.com/a/8245542/462631


On Monday, January 22, 2018 at 8:32:45 PM UTC-7, Alan Bem wrote:
Pozdrawiam,
Alan Bem

nevaeh...@stu.carter.kyschools.us

unread,
Jan 27, 2018, 4:20:58 AM1/27/18
to DDD/CQRS
hey

Alec Henninger

unread,
Jan 29, 2018, 6:43:35 AM1/29/18
to DDD/CQRS
+1 to usefulness framing... Ultimately it sounds like it doesn't really matter that much.

FWIW, having an id is significant. It is not just another value. It means there is a definition of identity beyond its values. All values could be the same, but it is still its own "thing" because it has its own id—its own identity. It does not make sense to give the color "blue" a separate identity unless we are talking about specific palettes or swatches or something, as an example. So, if in you model the identity of a transaction is separate from the values that make it up, it is an entity. Entities can be immutable there's no problem in that. If you could throw away the id and your model still works, you could do that and call it a value. IDs are generally useful though in both algorithms (cheaper ==) and to give continuity between contexts. And so if it's useful...

Denny Crane

unread,
Jan 29, 2018, 12:47:36 PM1/29/18
to DDD/CQRS
One of the other things that crossed my mind was that things shouldn't have a reference to non-root entities outside of the aggregate root, but in this sense, it would begin as being a reference outside of the root. I guess to avoid that, I would have to take the info from the command message and ask the root to create the transaction and just give me back its ID, however that makes no semantic sense because the 'Survey' doesn't create a transaction; it is created *because* of the transaction. And a result of that would be an event on the root stating that the transaction was created which makes even less sense. This is why I was thinking that it's more like a value object than an entity. Value objects can have an ID, but like you said, their equality going forward, would not be determined by that ID alone. 

Some might be inclined to call this an academic exercise at this point, and I wouldn't disagree, but it's this type of exercise that generally allows one to achieve a deeper understanding which is why I am engaging it. Otherwise, you're right -- I don't think it really matters that much in this context.

In the meanwhile, I think I am settling on it being modeled as a value object, both because of its role in this context and really, the way it is being used. It is an entity in it's originating context (a different service), but by the time it gets to this service, it's relevance is completely different. 

Alec Henninger

unread,
Jan 30, 2018, 6:25:10 AM1/30/18
to DDD/CQRS
I don't think it makes sense for a value to have an id. Does Evans or Vernon give an example of this?

Alexander Langer

unread,
Jan 30, 2018, 6:31:22 AM1/30/18
to ddd...@googlegroups.com
On 30.01.18 12:25, Alec Henninger wrote:
> I don't think it makes sense for a value to have an id. Does Evans or Vernon give an example of this?

Yes, it's in the IDDD book:

The "Author" of a forum post is a value object in the forum context. It
has an identity (and a name/email) that is a reference to the underlying
account, but in this context it's a value object:

https://github.com/VaughnVernon/IDDD_Samples/blob/master/iddd_collaboration/src/main/java/com/saasovation/collaboration/domain/model/collaborator/Author.java


Denny Crane

unread,
Jan 30, 2018, 11:53:15 AM1/30/18
to DDD/CQRS
Alec, it's important to distinguish between "having an ID" and "having an identity". When I said that the value object would "have an ID" I meant that it would "contain an ID" -- sorry for the confusion. The ID in this sense is just a value. It could have several IDs.

Herman Peeren

unread,
Feb 1, 2018, 3:50:38 AM2/1/18
to DDD/CQRS
A simpler way to look at it is this: a transaction in your model is an event. Transactions are messages from another model (from another "bounded context" in DDD-jargon) that something has occurred (or a series of related events have occurred). In this model you want to make a projection (a "Survey") of a stream of such transactions. In this simple model you have transactions in -> survey out, you don't change any state, you just give another representation of the transactions that have occurred. It is a "read-model" in the CQRS-sense; you might call it a "viewmodel". Within this model you have no state-consistency to guard and so nothing to do with "aggregates" in the DDD-sense. Just a stream of events (hence: immutable and no behaviour) and a projection.

Jeremy Stafford

unread,
Feb 1, 2018, 12:26:07 PM2/1/18
to ddd...@googlegroups.com
Yes that is precisely what is happening. The event is 'TransactionReceived' which comes from a different bounded context; a different service. But in order to process that transaction to produce a survey, that transaction information needs to be run through a lot of business rules. So for that to happen, that transaction event message needs to be placed into a business object before it can cross from the service context (the event handler) to the domain. That's what brought us to this exercise -- what kind of business object, entity or value object? Behavior-wise, it's just a value -- a bag of values actually. Like you said, there's no state changes and it has nothing to do with aggregates. But there's a few properties and then a dictionary of arbitrary values on it, and that doesn't feel very Value-ish to me. But it's not part of any aggregate, so making it an Entity feels wrong too. I suppose I could make it be neither? That doesn't feel right either. 

If I chose neither, how would I deal with it? I guess the only thing that is going to be persisted into the aggregate is the TransactionID and one of the metadata items (the dictionary I described). So perhaps, I create a domain service to analyze the transaction data and make some decisions before interacting with the aggregate? *ponder* still feels a bit dirty.

--
You received this message because you are subscribed to a topic in the Google Groups "DDD/CQRS" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/dddcqrs/Caeami-JHRs/unsubscribe.
To unsubscribe from this group and all its topics, 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.



--
(Love) A temporary insanity curable by marriage.
-- Ambrose Bierce

Herman Peeren

unread,
Feb 1, 2018, 3:29:21 PM2/1/18
to DDD/CQRS
Could you elaborate a bit on: "in order to process that transaction to produce a survey, that transaction information needs to be run through a lot of business rules. So for that to happen, that transaction event message needs to be placed into a business object before it can cross from the service context (the event handler) to the domain".

Maybe you can give a simple example of such a transaction and a survey. For I don't have an idea what "business rules" you mean, what would even be wrong with "business rules" in a projection and why that transaction (event) would have to be "placed into a business object". I have the feeling we are talking another language and don't see the problem. Thank you.
To unsubscribe from this group and all its topics, 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.

Jeremy Stafford

unread,
Feb 1, 2018, 7:26:42 PM2/1/18
to ddd...@googlegroups.com
To begin: Non-business objects will not enter the domain. That is, they will be either root entities, non-root entities, value objects, or objects that are in the shared interface assembly (onion architecture), but mostly just the domain objects. So either I use the event data to generate a value object or entity (this discussion), or I only work against properties of the event which is a bit on the anemic side.

- When transactions come into this service, they arrive as a transient bus event. That event will not be permitted to enter the domain as it would pollute it. Instead, an object that abides by the domain model will be created (the object that has been in discussion). 
- The transaction has various bits of data that are needed for the domain to make decisions on what to do next. For example, the source of the transaction can kick off different sets of rules that need to be evaluated (e.g. if provider A, run workflow 1, if provider B workflow 2) and other decisions like "If client configuration says to prefer email delivery, then search the transaction metadata for a customer email address; if prefers SMS delivery, the search the metadata for a phone number"  and even decisions such as "If transaction has this tag on it, then don't generate a survey at all; if it has this other tag on it then do the survey but also fire this other event to notify a supplementary 3rd party service that we generated the survey." These sound like technical rules, but they're not. They are very important business rules that need to be enforced for our partners, so it is modeled into the domain. So the first step in the process is running the transaction through this domain service in order to choose a saga to begin, and that saga will lead to variations of the survey generation process.

To unsubscribe from this group and all its topics, 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.

Herman Peeren

unread,
Feb 2, 2018, 8:57:32 AM2/2/18
to DDD/CQRS

First: lets clarify some language. You use the word "domain" where I would use the words "model", "domain model" or maybe "domain layer". I use the word "domain" in exactly the opposite meaning: that what is modelled; the business; the "real world". To understand the domain, to solve a problem in the domain or to automate something that has to be done in the domain we build models. A model for me is any way a domain and solutions are described (modelled), possibly including an implementation in a programming language, stickies on a wall in an event-stormning session, uml-diagrams etc. In this case I understand your (sub)domain to be about something like "notification and delivery to customers". The model gets transactions (events) as input and handles notifications and deliveries to customers based on those transactions and the preferences of the respective customers.


---


Now you seem to have some rules I don't have:

  • an event (like for instance a transaction) and in general a message between models is not a "business object".

  • only "business objects" may enter a domain model.

An event is not a first class citizen of your domain model: it is not allowed to enter the domain model "as it would pollute it". To me it is not clear what exactly you mean by "business object". I guess you use "business object" in about the same way I would use "domain object", that is: an object to build a domain model. But for you those objects seem to be very restricted and events are not part of them.


Because your model needs the transactions as input, but they are not seen, based on your rules above, as worthy enough objects to be used in your model, you make some kind of copy of those transactions in a form you seem to grade good enough to enter your model: you "upgrade" the events and their data to some domain object (= "an object that abides by the domain model").


What if you would just see an event, a transaction in your case, as an immutable object, coming from another model? What "pollution" are you fearing and could that be simply cleaned by some "anti-corruption layer" or other filter? What do you gain by copying the transaction/event to a "business object" in your domain model? What does an object need to be a "business object" for you?


---


In the end of your last answer you mention "sagas". Here is an article of why "process manager" might be a better word here: https://msdn.microsoft.com/en-us/library/jj591569.aspx


Please be aware of the fact that I'm quite a dissident from the official DDD-doctrines. For instance, one of the things that triggered me to answer your posting, was because I think: making a choice between an entity or a value object is nonsensical. I guess I disagree about that with about everybody in the DDD-community. I wrote about that before on this mailing list, see https://groups.google.com/forum/#!topic/dddcqrs/Icep9ujRllk "Category-mistakes and Value Objects". For me, when modelling in an OO-paradigm, I have a domain model with domain objects. That can be any object, like entities, use cases (which I use as objects where others would use an entity as aggregate root), events, etc.. Any object that is useful in modelling the domain can be a domain object. Some objects, like events for instance, can be immutable. Values (states, properties) of those objects can be of specific types. Value Objects are a kind of "poor mans types" as used in OO-languages, nothing more, nothing less. The main practical thing to learn from the concept of value objects is that immutability is to be preferred, because it makes our modelling easier.

Greg Young

unread,
Feb 2, 2018, 9:51:40 AM2/2/18
to ddd...@googlegroups.com

"In the end of your last answer you mention "sagas". Here is an article of why "process manager" might be a better word here: https://msdn.microsoft.com/en-us/library/jj591569.aspx"


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.



--
Studying for the Turing test

Jeremy Stafford

unread,
Feb 2, 2018, 1:10:26 PM2/2/18
to ddd...@googlegroups.com
"I guess you use "business object" in about the same way I would use "domain object"

Correct. I apologize, I tend to use the terms interchangeably. 

"But for you those objects seem to be very restricted and events are not part of them."

Events are part of my domain, absolutely! Just not events from other areas of the system. We have bus events separated from domain events. I'm event sourced, so I'd describe bus events as transient whereas domain events are persistent (to the event stream). I've seen implementations that don't separate these concepts and everything is just tied into the event store, and it looked like a complete nightmare. The separation I describe sounds contrived, but it's really not compared to the aforementioned alternative. Every bounded context in my domain lives in isolation. It doesn't care about anything else going on in the system; they only care about their own domain. Now, the service context that acts as an entry point to these domain models (the handlers) subscribe to events on the service bus and delegate to the domain any events or commands that may be relevant. So I look at the service contexts as the transport mechanisms into the various domain models whereas the domain model is the actual meat and potatoes of that particular domain. Two separate concerns. It's a large system that has many "domains". I say this because we provide services to our clients which all have particular, complex rule sets (think: Automotive vs. Medical). I probably wouldn't bother adding this much separation if the system was simpler.

"Because your model needs the transactions as input, but they are not seen, based on your rules above, as worthy enough objects to be used in your model, you make some kind of copy of those transactions in a form you seem to grade good enough to enter your model: you "upgrade" the events and their data to some domain object (= "an object that abides by the domain model")."

Yes, this is correct. All objects in my domain are rich and restrictive and are all modeled in DDD patterns. Lots of protection/validation and semantic meaning. I make some concessions here and there (e.g. I might allow a GUID as an id instead of a ThingID value object -- although, I'm starting to do this more for coding safety reasons). The event that comes in on the bus exists in another assembly and namespace on which the domain assembly must have no dependency. The event that comes in on the bus is essentially a property bag that has no place in the domain model IMHO. So the handler for the event actually lives in the Command host process. It receives the event and creates a "proper" object within the domain model -- think "In this context, transactions are shaped like *this*". Also, there are other services in the system that react to transaction events coming in, so any modifications to that event could negatively affect a domain model should that model have a direct dependency on that event. I find it better to just separate those two things completely.

"What if you would just see an event, a transaction in your case, as an immutable object, coming from another model? What "pollution" are you fearing and could that be simply cleaned by some "anti-corruption layer" or other filter? What do you gain by copying the transaction/event to a "business object" in your domain model? What does an object need to be a "business object" for you?"

I do,  hence my leaning towards it being modeled as a value object within the domain (all of my VOs are designed as immutable). The pollution to which I refer, would be creating a reference to that event type within the domain. The domain model knows what a "Transaction" is but it doesn't know what a "TransactionIngestedEvent" is, and I don't think it should. That event dies at the handler that receives it. From that point forward, everything is modeled within the context of surveys. So in other words, the event message is modeled for the service bus which is a technical concern vs. a business concern and live in shared libraries. Why? Implementation detail, mostly. NServiceBus frames messages in a specific way and binds to assembly names/versions. It's easier to just share the messaging libraries so that each service can communicate with other services by dropping the correct message format on the bus. In fact, even in conversations with the domain expert for surveys, "Transactions" are rarely mentioned. We're aware they are there, but they are a trigger -- a means to an end. So they are sort of a second class citizen within the domain model.


"In the end of your last answer you mention "sagas". Here is an article of why "process manager" might be a better word here: https://msdn.microsoft.com/en-us/library/jj591569.aspx " 

I refer to them as Sagas because I think that it is more of a real-world-relevant term and it's what they're called in NServiceBus. "Process Manager" or "Orchestrator" are correct, but they are terms that feel far more technical IMO wheras a "Saga" describes the journey of a single command through a complex system. I think someone decided "Saga" was more in the spirit of DDD. I tend to agree, but it's not worth arguing about :)

"Value Objects are a kind of "poor mans types" as used in OO-languages"

Agreed, and I understand. As previously described, this is why I became torn between the two implementations. I prefer that any object within the domain model fit a common DDD pattern if DDD was the chosen design methodology for this particular service. I could just make it a POCO, but then one toes the line of the anemic domain model. I have no problem chewing glass on a seemingly nonsensical semantic debate like Entity vs. VO because it almost always leads to deeper understanding of the practice as well as the actual domain, and encourages one to view the bigger picture from different angles. I often discover more about the domain than I had previously realized. This exercise is actually why I love DDD... but thank god not every service is not modeled this way XD



To unsubscribe from this group and all its topics, 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.



--
Reply all
Reply to author
Forward
0 new messages