Implement RegistrationApiController and its dependencies

Now, let's move on to implement RegistrationApiControler and its dependencies. The full name of the controller is com.taskagile.web.apis.RegistrationApiController, and it looks like the following:

...
@Controller
public class RegistrationApiController {

private UserService service;

public RegistrationApiController(UserService service) {
this.service = service;
}

@PostMapping("/api/registrations")
public ResponseEntity<ApiResult> register(
@Valid @RequestBody RegistrationPayload payload) {
try {
service.register(payload.toCommand());
return Result.created();
} catch (RegistrationException e) {
String errorMessage = "Registration failed";
if (e instanceof UsernameExistsException) {
errorMessage = "Username already exists";
} else if (e instanceof EmailAddressExistsException) {
errorMessage = "Email address already exists";
}
return Result.failure(errorMessage);
}
}
}

As you can see, we apply the @Valid annotation to RegistrationPayload. With the presence of this annotation, Spring MVC will perform data validation on RegistrationPayload to make sure its data is valid before passing it to the register() method. Inside the handler, we call the service API with an instance of the RegistrationCommand class that is converted from the RegistrationPayload instance. If everything works fine, we call Result.created() to return a response with HTTP status 201. Otherwise, we catch RegistrationException and return a 400 response with an message. 

The following is how RegistrationCommand looks. It is read-only. Once it is created, you cannot change its state because it only provides getters, not setters:

public class RegistrationCommand {
private String username;
private String emailAddress;
private String password;

public RegistrationCommand(String username, String emailAddress, String password) {
this.username = username;
this.emailAddress = emailAddress;
this.password = password;
}

// getters

@Override
public boolean equals(Object o) {
...
}

@Override
public int hashCode() {
....
}
}

As you can see, it also has the equals() and hashCode() methods. This is because, when Mockito compares the equality of two RegistrationCommand instances in the test, it will use these two methods to do the comparison. Otherwise, it will use the memory addresses of the objects to do the comparison of the equality, which will be different and the test will fail.

Here is how the com.taskagile.domain.application.UserService interface looks:

public interface UserService {
void register(RegistrationCommand command) throws RegistrationException;
}

We put this Application Service inside the com.taskagile.domain.model.user package. We won't use the layered architecture packaging style, which typically looks like the following:

  • com.taskagile.web
  • com.taskagile.service
  • com.taskagile.domain
  • com.taskagile.dao

Even though this structure works great for CRUD applications due to its simplicity, the biggest issue with this approach is that it divides classes of the same domain into different packages. When you look at the structure of the application, you have no idea what it provides.

In TaskAgile, we go with a different approach. That is, we put all of the classes of a domain in the same package and all domain knowledge will live inside that package. The following is the structure of our User domain:

  • com.taskagile.domain.application.UserService
  • com.taskagile.domain.application.commands
  • com.taskagile.domain.application.commands.RegistrationCommand
  • com.taskagile.domain.model.user.User
  • com.taskagile.domain.model.user.UserRepository
  • com.taskagile.domain.model.user.events.UserRegisteredEvent
  • com.taskagile.domain.model.user.RegistrationManagement
  • com.taskagile.domain.model.user.AuthenticationManagement

As you can see, the com.taskagile.domain.aplication package contains the Application Services that Controllers depend on. The com.taskagile.domain.model.user package contains the Domain Models, Repository interfaces, and Domain Events, as well as the Domain Services, RegistrationManagement and AuthenticationManagement. We will talk more about these in detail later in this chapter.

Back to making our test pass; we still need to get these dependencies/changes done:

  • Convert a RegistrationPayload instance into a RegistrationCommand instance
  • Create a com.taskagile.domain.model.user.RegistrationException exception, as well as UsernameExistsException and EmailAddressExistsException
  • Add a blank implementation of com.taskagile.domain.application.UserService
  • Add com.taskagile.web.results.ApiResult and com.taskagile.web.results.Result

Since most of these implementations and changes are quite straightforward, we will not list them in this book. You can find the details in the commit history shown in Figure 9.10.

Now, let's run the tests in RegistrationApiControllerTests from VS Code by clicking the Run Test link, as shown in Figure 9.9, and you will see all tests pass:

Figure 9.9: Run RegistrationApiControllerTests

After running the mvn clean install command, you should see that we have a successful installation before committing the changes. Figure 9.10 shows the commit history:

Figure 9.10: Implement register API handler commit
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.143.254.151