Ultimately, this is a question of encapsulation. Where does the if statement about whether or not to send email live?
Option 1 (a) - Use a different URI
In this option, there are at least two distinct pieces of software: the calling application and the service. And, the if statement about whether or not to send the email lives in the calling application, which tells the service which branch to take by calling different endpoints.
Option 1 (b) - Use a parameter
As above, there are the same two pieces of software, but the if statement lives in the service, closer to the user creation code.
Option 2 - High level service
Here, there are at least three pieces of software: the calling application, the high-level service, and the user creation service. This option technically boils down to the same question as Option 1: if the high-level service now owns the if statement, how does it know which branch to execute? Does the calling application call a different endpoint on the high-level service? Or, does it pass a parameter to it?
Option 3 - Introduce a Message Oriented Middleware
This option really sounds more like an how rather than what. Introducing a MOM is more about passing messages via queues vs. passing messages via endpoints on the services. As such, the calling application would need to know whether to pass different message types (use a different endpoint), or whether to pass a parameter as part of the message payload (use a parameter). In other words, you are left with the same two choices.
So, ultimately, the decision boils down to whether or not to create separate endpoints (or messages), or to pass a parameter (either on the endpoint, or in the message body).
Single Responsibility Principle says that we should gather together the things that will likely change together, and separate those things that will change for different reasons. So, we need to decide whether or not the if statement belongs with the use cases in the calling application, or with the service itself.
As I wrote this, I literally went back and forth as to which to recommend. I'm not sure there is a correct answer. In the end, I'm leaning toward an option that wasn't in the OP, which goes like this:
Option 4 - Use case driven design
In this option, there are three pieces of software, the calling application, the user creation service, and a user notification service. The calling application has different "use case" based classes, each of which knows whether or not to notify the user. And, the services are only responsible for the one thing it knows about.
So, in this option, the if statement lives in the calling application, and there is only one endpoint on each of the underlying services:
Calling Application (in pseudo Ruby)
class RegistrationController
def create
if agent_registration
UseCases::AgentRegistration.register(user_attributes)
else
UseCases::SelfRegistration.register(user_attributes)
end
end
end
class AgentRegistrationUseCase
def register(user_attributes)
UserRegistrationServivce.create(user_attributes)
end
end
class UserRegistrationUseCase
def register(user_attributes)
UserRegistrationService.create(user_attributes)
UserNotificationService.create(user_attributes)
end
end
Notice that the if statement lives in the calling application, close to the UI, and could easily be swapped out for a case statement in the event that you have additional use cases. The UseCases classes are specific to one of the two use cases. And, the services only have a single create endpoint on them.