Hi,
In DDD it is considered common practice to let an Aggregate enforce its own validity. In other words, an Aggregate can never be in an invalid state. In your case this means that the constructor ensures the arguments meet the requirements.
You can (and often should) enforce simple rules at the boundaries (UI, app service, etc.) as well, but the most important concern is (business) rule enforcement on an aggregate level.
Best
Dennis
--
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.
For more options, visit https://groups.google.com/groups/opt_out.
Pros and cons
· UI - No round trip .. very useful for web UI else you need to build a mechanism to send arrays of errors back to the UI . No complex logic checks though
· Command - Keeps aggregates light ( eg good for things that do not relate to business logic eg must be a number with 2 decimals) , allows using asynchronous handlers without a complex mechanism to communicate back to the source ( eg you can fire and forget after doing the check) . Bad commands are never added to the command bus. Full access to query model !
· Command Handlers – I very rarely use these. The only advantage is it keeps it out of the aggregate and it could fetch other aggregates ( but this is better than command) . Maybe in cases where you don’t trust the layer that creates the commands.
· Aggregates – Good for business logic type validation , Eg consider a courier company where a shipping label is damaged and only some of the chars can be entered , but it must still be recorded and someone later needs to hunt down the details. Similar validation code can be more easily shared.
So I use 3 for web type UIs.
· UI with local checking sometimes with Regular Expressions eg is it an email or phone.
· Commands basic checking at constructor is it an int ,non zero int , is it a non empty string. Just 1-2 lines per command , but sometimes I put a query here eg is the name already used .
· Aggregates . The rest
Regards,
Ben
--
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.
For more options, visit https://groups.google.com/groups/opt_out.
--
In my code EmailAddress is a value object which I pass around in command and store in event. It knows how to validate itself when it's created.
So, I can't even send command with invalid EmailAddress because I can't create invalid value object. When I replay events I get value objects too.
So I'd have something like:
class RegisterUser
{
UserName Name;
EmailAddress Email;
}
As much as possible I create value object for any data I pass around. This approach has really helped me to simplify the code, validation, etc. In UI, I can't even create a value object with invalid value. If I need to manipulate value object, it provides convenient methods. It's easy to test, etc.
I even almost never use List or Array for collection, but create a value object specifically for the collection I need to pass in the commend. For example instead of having List< ItemCode > I usually create ItemCodes value object specifically for working with ItemCodes.
Of course, there's some overhead from this during serialization / deserialization. I decided that benefits in the code outweight this.
I think Greg also spoke against using value objects everywhere. Not sure why, my guess would be due to the overhead and potential explosion of value objects (but so far I don't have a problem with this).
Slav Ivanyuk
--
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.
For more options, visit https://groups.google.com/groups/opt_out.
Even without value objects if I refactor things can break. How can VO makes refactoring worse?
And in the very worst case switch to EventV2 or ValueObjectV2?
I've never had an issue with this so far. As I use protobuf I can add new members to VO.
Any validation VO happens only on VO creation by the client. It doesn't happen during deserialization, and I try to keep it very simple in VO (e.g. email string is not null and is valid "some...@email.com"). It just checks that VO is in valid format. But it's not the place to check whether the data is valid from the business point of view.
If validation rules change, I would still need to be able to handle old, potentially now invalid data. Whatever happened in the past, happened.
I trust my event stream, yes. The event with invalid VO wouldn't have been saved simply because command for it with invalid VO cannot be created.
Slav Ivanyuk
--
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.
For more options, visit https://groups.google.com/groups/opt_out.
--
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.
For more options, visit https://groups.google.com/groups/opt_out.
I cannot. I use factory methods with VOs, or VOs within. But I cannot see how VO can act as a factory and/or specification?
Could you post a sample of what you're talking about?
Sample of my aggregate test:
[ Test ]
public void when_no_item_in_inventory()
{
this.CreateContext();
this.When( new AddItem( Id, Code, ItemLocation, 10, this.TransactionReason ) );
this.Expect( new ItemAdded( Id, Code, ItemLocation, 10, QuantityDifference.With( 0, 10 ), this.TransactionReason );
}
When - setup command
Expect - setup expected event.
Slav Ivanyuk
Yves, it does, thanks.
Yep, you're right. The fact that I use VO in messaging makes VO more complex, such as having extra attributes, or private constructor or whatever.
On the other hand I get a ready to use VO from the message. I don't have to write extra code to convert data in the message to VOs.
I guess it's a matter of preference and the project J Still, thank you for taking time to explain.
Slav
- your command handlers have reference to the domain AND to messages, as you said.
- your domain talks in the UL, so it accepts the EmailAddress as a parameter of one of one of the aggregate's methods.
- the command handler can do EmailAddress.Parse, then construct the aggregate and pass the valid email in. Or it can "validate" first if you want.
And I don't care if some regex will be the same or similar in JavaScript UI and inside EmailAddress type or in my command handler.
In fact you can even question whether you do need Parse method there or not. Do you _really_ parse it there? Do you really need it to be deconstructed internally? Or is it just another fancy way of doing validation? ;)
If you don't, why not validate the string explicitly in the command handler and then just pass a valid string in a constructor of EmailAddress which will not validate anything just assuming that the parameter is correct? In the email case it will just assign the property and that's it.
Another example can be, say, a projectname. What if you have a validation rule that it must be at least 2 chars and at max 10 chars long. It is NOT a business rule, it is a validation rule.
Do you want to enforce this rule in the VO?
What if in a week they tell you 'no, let's make it 10 to 20'? Not only you will have to change your VO to update the rule. You will have to deal with the existing data which does not comply with the rule. Should you still be able to construct VOs with 'old' data? Sure. Does it look weird then that there is a 'backdoor' somewhere in the VO which allows you to initialize it with invalid data? Sure it does.
I am trying to make a point that _validation_ and _business_ rules are different and are different concerns.
There may even be situations where you will have different _validation_ rules for different scenarios, even for the same VO or whatever.
Say, a command from the UI cannot accept username starting with $, but the 'system' command can do it! And from the Username VO point of view it is a perfectly OK in both cases.
That's one of the reasons I prefer keeping validation outside the domain and let my domain assume that everything passed to it is valid.
That's why I look at validation as of validating _commands_: what is required for one command can be optional for another, what is acceptable for one command can potentially be wrong for another.
And I totally agree with Yves: figuring out some totally reusable-everywhere validation layer not only can complicate your logic, but it also can limit you in some cases.
Cheers,
Alexey.
public override int GetHashCode() {unchecked {return 666 * _from.GetHashCode() ^ _to.GetHashCode();}}hah!
--