0
votes

Can we use value object in command ?

Suppose I have a Shop (aggregate) in which there is one value object Address. In the value object constructor Address ,I was put the some validation logic for address. So if I am using that Address object in command (CreateShopCmd) , then it get validated at the making of command , but What I want or Read that validation should be present in command handler.

But problem is that , I have to put that validation again in command handler (Since validation is already present in it Address constructor) and if I am not putting that in command handler , then the validation will occur when I am making the Address object in event handler and assign to Shop aggregate(Which is incorrect)

So, please guide me.

Below are code example

   @Aggregate
   @AggregateRoot
   public class Shop {

   @AggregateIdentifier
   private ShopId shopId;
   private String shopName;
   private Address address;

   @CommandHandler
   public Shop(CreateShopCmd cmd){

     //Validation Logic here , if not using the Address in 
     // in cmd

         //Fire an event after validation
         ShopRegistredEvt shopRegistredEvt = new ShopRegistredEvt();
         AggregateLifecycle.apply(shopRegistredEvt);
     }

     @EventSourcingHandler
     public void on(ShopRegistredEvt evt) {

     this.shopName = evt.getShopName();

     //Validation happend here if not put in cmd at the time of making 
     //Address object - this is wrong
     this.address = new Address(evt.getCity(),evt.getCountry(),evt.getZipCode())

     }


   }

  public class CreateShopCmd{

    private String shopId;
    private String shopName;
    private String city;
    private String zipCode;
    private String country;

   }

 public ShopCreatedEvent{

    private String shopId;
    private String shopName;
    private String city;
    private String zipCode;
    private String country;

}
5

5 Answers

4
votes

There is nothing conceptually wrong with using Value Objects in Commands or Events. However, you should use them with caution.

The structure of a Message may change over time. If you have used Value Object excessively inside your messages, it may become less clear how a change in one of the value objects changes the structure of different messages.

For Value Objects that represent a "common" concept, such as an Address, this is not so much of a problem. But as soon as the Value Objects become more domain-specific, this may come up as an issue.

3
votes

This is a very good question and I have been thoroughly thinking about embedding value objects in commands or not. I came to the conclusion you should definitely not use Value Objects in commands:

Commands are part of the application layer, they are supposed to work as simple as possible, avoiding any typed objects, and work best using literal (think serialization). What happen when an external system wants to plugin on your hexagon (application layer) and send commands to your application, do they need your command library to be able to use the objects and the structure defined ? Hell no ! You don't want that, so keep command simple.

Another reason is, as DmitriBodiu said, VO contains business logic and validation, they belong to the domain layer, do not ever put them in commands. Application service will do the translation, and be responsible of throwing validation error to any non conforming commands at the client.

There is nothing wrong in your design, its actually how Vaughn Vernon (the author of Implementing Domain Driven Design - IDDD book) did in his repository, you might want to check the application layer at this link:

https://github.com/VaughnVernon/IDDD_Samples/blob/master/iddd_identityaccess/src/main/java/com/saasovation/identityaccess/application/IdentityApplicationService.java

Notice how he reconstruct every objects from flat commands to value object belonging to the domain layer:

@Transactional
public void changeUserContactInformation(ChangeContactInfoCommand aCommand) {
    User user = this.existingUser(aCommand.getTenantId(), aCommand.getUsername());

    this.internalChangeUserContactInformation(
            user,
            new ContactInformation(
                    new EmailAddress(aCommand.getEmailAddress()),
                    new PostalAddress(
                            aCommand.getAddressStreetAddress(),
                            aCommand.getAddressCity(),
                            aCommand.getAddressStateProvince(),
                            aCommand.getAddressPostalCode(),
                            aCommand.getAddressCountryCode()),
                    new Telephone(aCommand.getPrimaryTelephone()),
                    new Telephone(aCommand.getSecondaryTelephone())));
}

Commands must not contain business logic, so they cannot carry a value object.

2
votes

I wouldn't suggest using Value Objects in commands. Cause your commands are part of the application layer, but Value Objects are kept in Domain Layer. You can use your ValueObjects in DomainEvens though. Because if domain model changes, modification of your domain event wouln't be that painful, cause the modification is done in the same bounded context. You should never use ValueObjects in integration events though.

1
votes

Short answer: Have you ever thought about Integer, String, Boolean, etc.? Those are Value Objects, too. The only difference is, that you didn't create them yourself. Now try to build a Command without any Value Objects ;-)

Long answer: In general I don't see any issue with Value Objects within Commands. As long as you follow a few simple guidelines:

The most important code in your application is your Domain Model. The Domain Model defines the data structures it expects for Command handling. This means: The only reason to change your Command Model is if your Domain Model requires this change. The same applies to your Value Objects: Value Objects only change if this change is required by your Domain Model. No exceptions!

Commands can in general fail either because of business constraints, or because of invalid data (or because of optimistic locking, or whatever).

As said above: Integers and Strings are Value Objects, too. If you only use basic types within your Command, it will already throw an exception if you try new SetAgeCommand(aggId, "foo"), because String cannot be assigned to int. The same applies if you don't provide an Aggregate ID to your UpdatePersonCommand. These are no business constraints, but instead very basic data and type validation. Your Command will never be created if you pass malformed data.

Now let's say you have a PersonAge Value Object. I doesn't matter where you construct this object, because in any case it must throw an Exception if you try to construct it with a negative number: -5 cannot be assigned to PersonAge - looks familiar? As long as you can make sure that your code created those Value Object instances, you can know for sure that they are valid.

Business rules should be checked by the Command Handler within your Domain Model. In general business constraints are specific to your Domain, and most often they rely on the data within your Aggregate. Take for example SendMoneyCommand. Your Money Value Object can validate if it's a valid currency, but it cannot validate if the user's bank account has enough money to execute the transaction. This is a business validation and it's part of your Domain Model.

And a word regarding Events: I'd suggest to only use very basic Value Objects inside your events. For example: String, Integer, Date, etc. Basically every kind of Value Object that will never change. The reason behind it: Business requirements can change. For example: Maybe your Domain Model requires your Address Value Object to change, and it's now required to provide geo-coordinates. Then this will implicitly change your NewAddressAddedEvent. But your already persisted Events didn't have this requirement, though you're unable to construct Address Value Objects from your past event data, because the new Address Value Object will throw an Exception if there are no geo-coordinates provided.

There are (at least) two solutions for this problem:

  1. Versioned Events: After modifying your Address Value Object, you have now a NewAddressAddedEvent_Version2 which uses the new Address Value Object, and you have the old NewAddressAddedEvent which must use a backup copy of the old Address Value Object.
  2. Write a Script that "repairs" your event database by adding geo-coordinates to every Event that uses the Address Value Object. So you can throw away the old NewAddressAddedEvent.
0
votes

That's OK as long as the value objects are conceptually a part of your message contract, and not used in entities.

And if they are a part of your entity, don't expose them as public properties of your message or you'll be in soop.