0

I have this two use cases in a user registration process :

  • Case 1 : the user register an account online and the system should send him an email notification in order to confirm that his account has been created and validate his email at the same time.
  • Case 2 : the user account is created with an agent, in an sale office agency : this time there is no need to fire the notification mail process.

So I am wondering how should I model my account rest service :

  • Option 1 : present to different uri or use a parameter . In this case the application calling the service know which registration endpoint to call for its use case.

  • Option 2 : make a hight level service that should depending on the caller orchestrate the call and fire the notification process if needed?

  • Option 3 : complicate my actual architecture by introducing a mom and create a handler on the account registration event so that a notification mail is fired if needed...

3
  • Do you have to use one api endpoint for two cases? – RockOnGom Dec 21 '18 at 9:48
  • I meant two different uris or by using parameter to distinguish the two cases. – Dypso Dec 21 '18 at 11:50
  • 1
    I would keep it simple and use different endpoints. – RockOnGom Dec 21 '18 at 11:53
1

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.

3
  • So in the option 4, I would also use two different uris ? I was wondering what would be the more appropriate for maintenance purpose : I have only two case now : depending if a trusted agent is making the registration for the user or if the user do it by himself... – Dypso Dec 21 '18 at 11:48
  • No. I'll edit my answer to be more clear. – aridlehoover Dec 21 '18 at 15:29
  • Updated my post. – aridlehoover Dec 21 '18 at 15:49
0

Potentially, the situations for the creation of a user may potentially differ based on who is making the call. While there are many components in common, the day in which more information must be registered for a call by the agent, you'd need to change things around quite a bit.

So use two separate endpoints which both have the same simple logic, which is to request the data layer to create a new user with an additional parameter to indicate whether or not that user is confirmed or not. If the confirmed status is simply another field in users table, then you'd simply need to set the confirmed status in one case and not in the other. Only in one of the two, you'd perform the additional business logic to follow up with an e-mail.

This same endpoint can also handle the actual confirmation of said user.

What should remain the same is the data layer. Unless there are significant differences between the two requests (which there doesn't seem to be), the actual insertion into your database should be both stupid and simple.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.