Let's begin with defining a simple command object that will interact with your domain. We'll do a good and a bad example right at the beginning to compare them.
#### BAD #### module Communications class SendEmailNotification def initialize(email:, notification_id:) raise WrongEmailFormat unless email.is_a?(Communications::Email) @email = email @notification_id = notification_id end end end
#### GOOD #### module Communications class SendEmailNotification def initialize(email:, notification_id:) @email = Email.new(email) @notification_id = notification_id end end end
At first, the difference might look insignificant. The only thing different is that the first command validates whether the object passed through
Communications::Email - an internal Value Object of the
Communications module that represents an email address. While this looks innocent, and may even look better at first (because we validate whether the passed parameter is really an email) it brings maintenance complexities.
The biggest problem it introduces here is that the external users of the
Communications module will be forced to reach into the
Communications module and build this Value Object before they can use the command. They will have to rummage around your domain and to use its internal objects. This will make it harder for you to introduce changes as you fit to the
So! The main lesson here is that it's better to use primitives or some objects specifically created for passing them into the command instead of using the internal objects representing your domain.