From 3f4db1d48f2f9f257ed5d1876f5a8d0598e5fedf Mon Sep 17 00:00:00 2001 From: Octavi Pascual Date: Sun, 8 Nov 2020 17:38:48 +0100 Subject: [PATCH 1/9] Add register student use case --- .../students/StudentsPutController.java | 55 +++++++++++++++++++ .../students/StudentsPutControllerShould.java | 16 ++++++ .../register/StudentRegistrar.java | 18 ++++++ .../codely/mooc/students/domain/Student.java | 22 ++++++++ .../students/domain/StudentRepository.java | 1 + .../InMemoryStudentRepository.java | 3 + .../register/StudentRegistrarTestShould.java | 28 ++++++++++ 7 files changed, 143 insertions(+) create mode 100644 apps/main/tv/codely/apps/mooc/backend/controller/students/StudentsPutController.java create mode 100644 apps/test/tv/codely/apps/mooc/backend/controller/students/StudentsPutControllerShould.java create mode 100644 src/mooc/main/tv/codely/mooc/students/application/register/StudentRegistrar.java create mode 100644 src/mooc/test/tv/codely/mooc/students/application/register/StudentRegistrarTestShould.java diff --git a/apps/main/tv/codely/apps/mooc/backend/controller/students/StudentsPutController.java b/apps/main/tv/codely/apps/mooc/backend/controller/students/StudentsPutController.java new file mode 100644 index 00000000..e8c30252 --- /dev/null +++ b/apps/main/tv/codely/apps/mooc/backend/controller/students/StudentsPutController.java @@ -0,0 +1,55 @@ +package tv.codely.apps.mooc.backend.controller.students; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; +import tv.codely.mooc.students.application.register.StudentRegistrar; + +@RestController +public final class StudentsPutController { + + private final StudentRegistrar registrar; + + public StudentsPutController(StudentRegistrar registrar) { + this.registrar = registrar; + } + + @PutMapping(value = "/students/{id}") + public ResponseEntity index(@PathVariable String id, @RequestBody Request request) { + registrar.register(id, request.name(), request.surname(), request.email()); + return new ResponseEntity<>(HttpStatus.CREATED); + } +} + +final class Request { + private String name; + private String surname; + private String email; + + String name() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String surname() { + return surname; + } + + public void setSurname(String surname) { + this.surname = surname; + } + + public String email() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } +} diff --git a/apps/test/tv/codely/apps/mooc/backend/controller/students/StudentsPutControllerShould.java b/apps/test/tv/codely/apps/mooc/backend/controller/students/StudentsPutControllerShould.java new file mode 100644 index 00000000..e24f8d31 --- /dev/null +++ b/apps/test/tv/codely/apps/mooc/backend/controller/students/StudentsPutControllerShould.java @@ -0,0 +1,16 @@ +package tv.codely.apps.mooc.backend.controller.students; + +import org.junit.jupiter.api.Test; +import tv.codely.apps.mooc.MoocApplicationTestCase; + +public final class StudentsPutControllerShould extends MoocApplicationTestCase { + @Test + void register_a_valid_non_existing_student() throws Exception { + assertRequestWithBody( + "PUT", + "/students/1bab45ba-3c7a-4344-8936-78466eca17fa", + "{\"name\": \"some-name\", \"surname\": \"some-surname\", \"email\": \"some-email\"}", + 201 + ); + } +} diff --git a/src/mooc/main/tv/codely/mooc/students/application/register/StudentRegistrar.java b/src/mooc/main/tv/codely/mooc/students/application/register/StudentRegistrar.java new file mode 100644 index 00000000..d5813763 --- /dev/null +++ b/src/mooc/main/tv/codely/mooc/students/application/register/StudentRegistrar.java @@ -0,0 +1,18 @@ +package tv.codely.mooc.students.application.register; + +import tv.codely.mooc.students.domain.*; +import tv.codely.shared.domain.Service; + +@Service +public class StudentRegistrar { + private final StudentRepository repository; + + public StudentRegistrar(StudentRepository repository) { + this.repository = repository; + } + + public void register(String id, String name, String surname, String email) { + Student student = Student.create(new StudentId(id), name, surname, email); + repository.register(student); + } +} diff --git a/src/mooc/main/tv/codely/mooc/students/domain/Student.java b/src/mooc/main/tv/codely/mooc/students/domain/Student.java index eb0353d1..ffd694f4 100644 --- a/src/mooc/main/tv/codely/mooc/students/domain/Student.java +++ b/src/mooc/main/tv/codely/mooc/students/domain/Student.java @@ -1,5 +1,7 @@ package tv.codely.mooc.students.domain; +import java.util.Objects; + public final class Student { private final StudentId id; private final String name; @@ -13,6 +15,10 @@ public Student(StudentId id, String name, String surname, String email) { this.email = email; } + public static Student create(StudentId id, String name, String surname, String email) { + return new Student(id, name, surname, email); + } + public StudentId id() { return id; } @@ -28,4 +34,20 @@ public String surname() { public String email() { return email; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Student student = (Student) o; + return id.equals(student.id) && + name.equals(student.name) && + surname.equals(student.surname) && + email.equals(student.email); + } + + @Override + public int hashCode() { + return Objects.hash(id, name, surname, email); + } } diff --git a/src/mooc/main/tv/codely/mooc/students/domain/StudentRepository.java b/src/mooc/main/tv/codely/mooc/students/domain/StudentRepository.java index 0fbd2e6c..8990f3b0 100644 --- a/src/mooc/main/tv/codely/mooc/students/domain/StudentRepository.java +++ b/src/mooc/main/tv/codely/mooc/students/domain/StudentRepository.java @@ -3,5 +3,6 @@ import java.util.List; public interface StudentRepository { + void register(Student student); List searchAll(); } diff --git a/src/mooc/main/tv/codely/mooc/students/infrastructure/InMemoryStudentRepository.java b/src/mooc/main/tv/codely/mooc/students/infrastructure/InMemoryStudentRepository.java index aabd3abf..74041dd6 100644 --- a/src/mooc/main/tv/codely/mooc/students/infrastructure/InMemoryStudentRepository.java +++ b/src/mooc/main/tv/codely/mooc/students/infrastructure/InMemoryStudentRepository.java @@ -24,4 +24,7 @@ public List searchAll() { new Student(new StudentId(generator.generate()), "Other name", "Other surname", "another@mail.com") ); } + + @Override + public void register(Student student) {} } diff --git a/src/mooc/test/tv/codely/mooc/students/application/register/StudentRegistrarTestShould.java b/src/mooc/test/tv/codely/mooc/students/application/register/StudentRegistrarTestShould.java new file mode 100644 index 00000000..27cfb516 --- /dev/null +++ b/src/mooc/test/tv/codely/mooc/students/application/register/StudentRegistrarTestShould.java @@ -0,0 +1,28 @@ +package tv.codely.mooc.students.application.register; + +import org.junit.jupiter.api.Test; +import tv.codely.mooc.students.domain.Student; +import tv.codely.mooc.students.domain.StudentId; +import tv.codely.mooc.students.domain.StudentRepository; +import tv.codely.shared.domain.UuidMother; + +import static org.mockito.Mockito.*; + +final class StudentRegistrarTestShould { + @Test + void register_a_valid_student() { + StudentRepository repository = mock(StudentRepository.class); + StudentRegistrar registrar = new StudentRegistrar(repository); + + StudentId id = new StudentId(UuidMother.random()); + String name = "name"; + String surname = "surname"; + String email = "email"; + + Student student = new Student(id, name, surname, email); + + registrar.register(id.value(), student.name(), student.surname(), student.email()); + + verify(repository, atLeastOnce()).register(student); + } +} From 4e9c42b36b3eb03b61d9fc6bf2c1153d9cd8a6be Mon Sep 17 00:00:00 2001 From: Octavi Pascual Date: Mon, 9 Nov 2020 20:00:45 +0100 Subject: [PATCH 2/9] Use RegisterStudentRequest in application service --- .../students/StudentsPutController.java | 3 +- .../register/RegisterStudentRequest.java | 31 +++++++++++++++++++ .../register/StudentRegistrar.java | 8 +++-- .../register/StudentRegistrarTestShould.java | 12 ++++--- 4 files changed, 45 insertions(+), 9 deletions(-) create mode 100644 src/mooc/main/tv/codely/mooc/students/application/register/RegisterStudentRequest.java diff --git a/apps/main/tv/codely/apps/mooc/backend/controller/students/StudentsPutController.java b/apps/main/tv/codely/apps/mooc/backend/controller/students/StudentsPutController.java index e8c30252..4af756b7 100644 --- a/apps/main/tv/codely/apps/mooc/backend/controller/students/StudentsPutController.java +++ b/apps/main/tv/codely/apps/mooc/backend/controller/students/StudentsPutController.java @@ -6,6 +6,7 @@ import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; +import tv.codely.mooc.students.application.register.RegisterStudentRequest; import tv.codely.mooc.students.application.register.StudentRegistrar; @RestController @@ -19,7 +20,7 @@ public StudentsPutController(StudentRegistrar registrar) { @PutMapping(value = "/students/{id}") public ResponseEntity index(@PathVariable String id, @RequestBody Request request) { - registrar.register(id, request.name(), request.surname(), request.email()); + registrar.register(new RegisterStudentRequest(id, request.name(), request.surname(), request.email())); return new ResponseEntity<>(HttpStatus.CREATED); } } diff --git a/src/mooc/main/tv/codely/mooc/students/application/register/RegisterStudentRequest.java b/src/mooc/main/tv/codely/mooc/students/application/register/RegisterStudentRequest.java new file mode 100644 index 00000000..2fdd4527 --- /dev/null +++ b/src/mooc/main/tv/codely/mooc/students/application/register/RegisterStudentRequest.java @@ -0,0 +1,31 @@ +package tv.codely.mooc.students.application.register; + +public final class RegisterStudentRequest { + private final String id; + private final String name; + private final String surname; + private final String email; + + public RegisterStudentRequest(String id, String name, String surname, String email) { + this.id = id; + this.name = name; + this.surname = surname; + this.email = email; + } + + public String id() { + return id; + } + + public String name() { + return name; + } + + public String surname() { + return surname; + } + + public String email() { + return email; + } +} diff --git a/src/mooc/main/tv/codely/mooc/students/application/register/StudentRegistrar.java b/src/mooc/main/tv/codely/mooc/students/application/register/StudentRegistrar.java index d5813763..6e2b6b61 100644 --- a/src/mooc/main/tv/codely/mooc/students/application/register/StudentRegistrar.java +++ b/src/mooc/main/tv/codely/mooc/students/application/register/StudentRegistrar.java @@ -1,6 +1,8 @@ package tv.codely.mooc.students.application.register; -import tv.codely.mooc.students.domain.*; +import tv.codely.mooc.students.domain.Student; +import tv.codely.mooc.students.domain.StudentId; +import tv.codely.mooc.students.domain.StudentRepository; import tv.codely.shared.domain.Service; @Service @@ -11,8 +13,8 @@ public StudentRegistrar(StudentRepository repository) { this.repository = repository; } - public void register(String id, String name, String surname, String email) { - Student student = Student.create(new StudentId(id), name, surname, email); + public void register(RegisterStudentRequest request) { + Student student = Student.create(new StudentId(request.id()), request.name(), request.surname(), request.email()); repository.register(student); } } diff --git a/src/mooc/test/tv/codely/mooc/students/application/register/StudentRegistrarTestShould.java b/src/mooc/test/tv/codely/mooc/students/application/register/StudentRegistrarTestShould.java index 27cfb516..a80c6351 100644 --- a/src/mooc/test/tv/codely/mooc/students/application/register/StudentRegistrarTestShould.java +++ b/src/mooc/test/tv/codely/mooc/students/application/register/StudentRegistrarTestShould.java @@ -14,14 +14,16 @@ void register_a_valid_student() { StudentRepository repository = mock(StudentRepository.class); StudentRegistrar registrar = new StudentRegistrar(repository); - StudentId id = new StudentId(UuidMother.random()); - String name = "name"; - String surname = "surname"; - String email = "email"; + StudentId id = new StudentId(UuidMother.random()); + String name = "name"; + String surname = "surname"; + String email = "email"; + + RegisterStudentRequest request = new RegisterStudentRequest(id.value(), name, surname, email); Student student = new Student(id, name, surname, email); - registrar.register(id.value(), student.name(), student.surname(), student.email()); + registrar.register(request); verify(repository, atLeastOnce()).register(student); } From b1582b2e874e71d481fd204acb1d46d4d9f6c9ea Mon Sep 17 00:00:00 2001 From: Octavi Pascual Date: Tue, 10 Nov 2020 09:38:21 +0100 Subject: [PATCH 3/9] Use Value Objects for Student --- .../students/StudentsPutControllerShould.java | 2 +- .../students/application/StudentResponse.java | 7 ++- .../register/StudentRegistrar.java | 11 +++-- .../codely/mooc/students/domain/Student.java | 18 +++---- .../mooc/students/domain/StudentEmail.java | 9 ++++ .../mooc/students/domain/StudentName.java | 9 ++++ .../mooc/students/domain/StudentSurname.java | 9 ++++ .../InMemoryStudentRepository.java | 21 ++++++--- .../register/StudentRegistrarTestShould.java | 16 +++---- .../shared/domain/EmailValueObject.java | 47 +++++++++++++++++++ .../tv/codely/shared/domain/InvalidEmail.java | 7 +++ 11 files changed, 127 insertions(+), 29 deletions(-) create mode 100644 src/mooc/main/tv/codely/mooc/students/domain/StudentEmail.java create mode 100644 src/mooc/main/tv/codely/mooc/students/domain/StudentName.java create mode 100644 src/mooc/main/tv/codely/mooc/students/domain/StudentSurname.java create mode 100644 src/shared/main/tv/codely/shared/domain/EmailValueObject.java create mode 100644 src/shared/main/tv/codely/shared/domain/InvalidEmail.java diff --git a/apps/test/tv/codely/apps/mooc/backend/controller/students/StudentsPutControllerShould.java b/apps/test/tv/codely/apps/mooc/backend/controller/students/StudentsPutControllerShould.java index e24f8d31..7ed233d1 100644 --- a/apps/test/tv/codely/apps/mooc/backend/controller/students/StudentsPutControllerShould.java +++ b/apps/test/tv/codely/apps/mooc/backend/controller/students/StudentsPutControllerShould.java @@ -9,7 +9,7 @@ void register_a_valid_non_existing_student() throws Exception { assertRequestWithBody( "PUT", "/students/1bab45ba-3c7a-4344-8936-78466eca17fa", - "{\"name\": \"some-name\", \"surname\": \"some-surname\", \"email\": \"some-email\"}", + "{\"name\": \"some-name\", \"surname\": \"some-surname\", \"email\": \"email@email.com\"}", 201 ); } diff --git a/src/mooc/main/tv/codely/mooc/students/application/StudentResponse.java b/src/mooc/main/tv/codely/mooc/students/application/StudentResponse.java index 9b8bc05a..1ce361a8 100644 --- a/src/mooc/main/tv/codely/mooc/students/application/StudentResponse.java +++ b/src/mooc/main/tv/codely/mooc/students/application/StudentResponse.java @@ -17,7 +17,12 @@ public StudentResponse(String id, String name, String surname, String email) { } public static StudentResponse fromAggregate(Student student) { - return new StudentResponse(student.id().value(), student.name(), student.surname(), student.email()); + return new StudentResponse( + student.id().value(), + student.name().value(), + student.surname().value(), + student.email().value() + ); } public String id() { diff --git a/src/mooc/main/tv/codely/mooc/students/application/register/StudentRegistrar.java b/src/mooc/main/tv/codely/mooc/students/application/register/StudentRegistrar.java index 6e2b6b61..01a4055a 100644 --- a/src/mooc/main/tv/codely/mooc/students/application/register/StudentRegistrar.java +++ b/src/mooc/main/tv/codely/mooc/students/application/register/StudentRegistrar.java @@ -1,8 +1,6 @@ package tv.codely.mooc.students.application.register; -import tv.codely.mooc.students.domain.Student; -import tv.codely.mooc.students.domain.StudentId; -import tv.codely.mooc.students.domain.StudentRepository; +import tv.codely.mooc.students.domain.*; import tv.codely.shared.domain.Service; @Service @@ -14,7 +12,12 @@ public StudentRegistrar(StudentRepository repository) { } public void register(RegisterStudentRequest request) { - Student student = Student.create(new StudentId(request.id()), request.name(), request.surname(), request.email()); + Student student = Student.create( + new StudentId(request.id()), + new StudentName(request.name()), + new StudentSurname(request.surname()), + new StudentEmail(request.email()) + ); repository.register(student); } } diff --git a/src/mooc/main/tv/codely/mooc/students/domain/Student.java b/src/mooc/main/tv/codely/mooc/students/domain/Student.java index ffd694f4..543557be 100644 --- a/src/mooc/main/tv/codely/mooc/students/domain/Student.java +++ b/src/mooc/main/tv/codely/mooc/students/domain/Student.java @@ -3,19 +3,19 @@ import java.util.Objects; public final class Student { - private final StudentId id; - private final String name; - private final String surname; - private final String email; + private final StudentId id; + private final StudentName name; + private final StudentSurname surname; + private final StudentEmail email; - public Student(StudentId id, String name, String surname, String email) { + public Student(StudentId id, StudentName name, StudentSurname surname, StudentEmail email) { this.id = id; this.name = name; this.surname = surname; this.email = email; } - public static Student create(StudentId id, String name, String surname, String email) { + public static Student create(StudentId id, StudentName name, StudentSurname surname, StudentEmail email) { return new Student(id, name, surname, email); } @@ -23,15 +23,15 @@ public StudentId id() { return id; } - public String name() { + public StudentName name() { return name; } - public String surname() { + public StudentSurname surname() { return surname; } - public String email() { + public StudentEmail email() { return email; } diff --git a/src/mooc/main/tv/codely/mooc/students/domain/StudentEmail.java b/src/mooc/main/tv/codely/mooc/students/domain/StudentEmail.java new file mode 100644 index 00000000..a3d395b2 --- /dev/null +++ b/src/mooc/main/tv/codely/mooc/students/domain/StudentEmail.java @@ -0,0 +1,9 @@ +package tv.codely.mooc.students.domain; + +import tv.codely.shared.domain.EmailValueObject; + +public final class StudentEmail extends EmailValueObject { + public StudentEmail(String email) { + super(email); + } +} diff --git a/src/mooc/main/tv/codely/mooc/students/domain/StudentName.java b/src/mooc/main/tv/codely/mooc/students/domain/StudentName.java new file mode 100644 index 00000000..9003196b --- /dev/null +++ b/src/mooc/main/tv/codely/mooc/students/domain/StudentName.java @@ -0,0 +1,9 @@ +package tv.codely.mooc.students.domain; + +import tv.codely.shared.domain.StringValueObject; + +public final class StudentName extends StringValueObject { + public StudentName(String value) { + super(value); + } +} diff --git a/src/mooc/main/tv/codely/mooc/students/domain/StudentSurname.java b/src/mooc/main/tv/codely/mooc/students/domain/StudentSurname.java new file mode 100644 index 00000000..5ffd44ec --- /dev/null +++ b/src/mooc/main/tv/codely/mooc/students/domain/StudentSurname.java @@ -0,0 +1,9 @@ +package tv.codely.mooc.students.domain; + +import tv.codely.shared.domain.StringValueObject; + +public final class StudentSurname extends StringValueObject { + public StudentSurname(String value) { + super(value); + } +} diff --git a/src/mooc/main/tv/codely/mooc/students/infrastructure/InMemoryStudentRepository.java b/src/mooc/main/tv/codely/mooc/students/infrastructure/InMemoryStudentRepository.java index 74041dd6..e4703e58 100644 --- a/src/mooc/main/tv/codely/mooc/students/infrastructure/InMemoryStudentRepository.java +++ b/src/mooc/main/tv/codely/mooc/students/infrastructure/InMemoryStudentRepository.java @@ -1,8 +1,6 @@ package tv.codely.mooc.students.infrastructure; -import tv.codely.mooc.students.domain.Student; -import tv.codely.mooc.students.domain.StudentId; -import tv.codely.mooc.students.domain.StudentRepository; +import tv.codely.mooc.students.domain.*; import tv.codely.shared.domain.Service; import tv.codely.shared.domain.UuidGenerator; @@ -20,11 +18,22 @@ public InMemoryStudentRepository(UuidGenerator generator) { @Override public List searchAll() { return Arrays.asList( - new Student(new StudentId(generator.generate()), "name", "surname", "email@mail.com"), - new Student(new StudentId(generator.generate()), "Other name", "Other surname", "another@mail.com") + new Student( + new StudentId(generator.generate()), + new StudentName("name"), + new StudentSurname("surname"), + new StudentEmail("email@mail.com") + ), + new Student( + new StudentId(generator.generate()), + new StudentName("Other name"), + new StudentSurname("Other surname"), + new StudentEmail("another@mail.com") + ) ); } @Override - public void register(Student student) {} + public void register(Student student) { + } } diff --git a/src/mooc/test/tv/codely/mooc/students/application/register/StudentRegistrarTestShould.java b/src/mooc/test/tv/codely/mooc/students/application/register/StudentRegistrarTestShould.java index a80c6351..25f92b3a 100644 --- a/src/mooc/test/tv/codely/mooc/students/application/register/StudentRegistrarTestShould.java +++ b/src/mooc/test/tv/codely/mooc/students/application/register/StudentRegistrarTestShould.java @@ -1,9 +1,7 @@ package tv.codely.mooc.students.application.register; import org.junit.jupiter.api.Test; -import tv.codely.mooc.students.domain.Student; -import tv.codely.mooc.students.domain.StudentId; -import tv.codely.mooc.students.domain.StudentRepository; +import tv.codely.mooc.students.domain.*; import tv.codely.shared.domain.UuidMother; import static org.mockito.Mockito.*; @@ -14,12 +12,14 @@ void register_a_valid_student() { StudentRepository repository = mock(StudentRepository.class); StudentRegistrar registrar = new StudentRegistrar(repository); - StudentId id = new StudentId(UuidMother.random()); - String name = "name"; - String surname = "surname"; - String email = "email"; + StudentId id = new StudentId(UuidMother.random()); + StudentName name = new StudentName("name"); + StudentSurname surname = new StudentSurname("surname"); + StudentEmail email = new StudentEmail("email@email.com"); - RegisterStudentRequest request = new RegisterStudentRequest(id.value(), name, surname, email); + RegisterStudentRequest request = new RegisterStudentRequest( + id.value(), name.value(), surname.value(), email.value() + ); Student student = new Student(id, name, surname, email); diff --git a/src/shared/main/tv/codely/shared/domain/EmailValueObject.java b/src/shared/main/tv/codely/shared/domain/EmailValueObject.java new file mode 100644 index 00000000..ea0c6607 --- /dev/null +++ b/src/shared/main/tv/codely/shared/domain/EmailValueObject.java @@ -0,0 +1,47 @@ +package tv.codely.shared.domain; + +import org.hibernate.validator.internal.constraintvalidators.hv.EmailValidator; + +import java.util.Objects; + +public abstract class EmailValueObject { + private static final EmailValidator emailValidator = new EmailValidator(); + private final String email; + + public EmailValueObject(String email) { + ensureValidEmail(email); + this.email = email; + } + + public String value() { + return email; + } + + private void ensureValidEmail(String value) { + if (!emailValidator.isValid(value, null)) { + throw new InvalidEmail(value); + } + } + + @Override + public String toString() { + return this.value(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof EmailValueObject)) { + return false; + } + EmailValueObject that = (EmailValueObject) o; + return Objects.equals(email, that.email); + } + + @Override + public int hashCode() { + return Objects.hash(email); + } +} diff --git a/src/shared/main/tv/codely/shared/domain/InvalidEmail.java b/src/shared/main/tv/codely/shared/domain/InvalidEmail.java new file mode 100644 index 00000000..31ece9ce --- /dev/null +++ b/src/shared/main/tv/codely/shared/domain/InvalidEmail.java @@ -0,0 +1,7 @@ +package tv.codely.shared.domain; + +public final class InvalidEmail extends DomainError { + public InvalidEmail(String email) { + super("invalid_email", String.format("The email <%s> is invalid", email)); + } +} From aab00a0d5b78db0a3ae73dbf1bf0aa286d9029bb Mon Sep 17 00:00:00 2001 From: Octavi Pascual Date: Wed, 11 Nov 2020 11:18:24 +0100 Subject: [PATCH 4/9] Use ObjectMother for Student fields --- .../RegisterStudentRequestMother.java | 18 +++++++++++++ .../register/StudentRegistrarTestShould.java | 17 ++++-------- .../students/domain/StudentEmailMother.java | 13 +++++++++ .../mooc/students/domain/StudentMother.java | 27 +++++++++++++++++++ .../students/domain/StudentNameMother.java | 14 ++++++++++ .../students/domain/StudentSurnameMother.java | 13 +++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) create mode 100644 src/mooc/test/tv/codely/mooc/students/application/register/RegisterStudentRequestMother.java create mode 100644 src/mooc/test/tv/codely/mooc/students/domain/StudentEmailMother.java create mode 100644 src/mooc/test/tv/codely/mooc/students/domain/StudentMother.java create mode 100644 src/mooc/test/tv/codely/mooc/students/domain/StudentNameMother.java create mode 100644 src/mooc/test/tv/codely/mooc/students/domain/StudentSurnameMother.java diff --git a/src/mooc/test/tv/codely/mooc/students/application/register/RegisterStudentRequestMother.java b/src/mooc/test/tv/codely/mooc/students/application/register/RegisterStudentRequestMother.java new file mode 100644 index 00000000..2237d091 --- /dev/null +++ b/src/mooc/test/tv/codely/mooc/students/application/register/RegisterStudentRequestMother.java @@ -0,0 +1,18 @@ +package tv.codely.mooc.students.application.register; + +import tv.codely.mooc.students.domain.*; + +public class RegisterStudentRequestMother { + public static RegisterStudentRequest create(StudentId id, StudentName name, StudentSurname surname, StudentEmail email) { + return new RegisterStudentRequest(id.value(), name.value(), surname.value(), email.value()); + } + + public static RegisterStudentRequest random() { + return create( + StudentIdMother.random(), + StudentNameMother.random(), + StudentSurnameMother.random(), + StudentEmailMother.random() + ); + } +} diff --git a/src/mooc/test/tv/codely/mooc/students/application/register/StudentRegistrarTestShould.java b/src/mooc/test/tv/codely/mooc/students/application/register/StudentRegistrarTestShould.java index 25f92b3a..e87f87c4 100644 --- a/src/mooc/test/tv/codely/mooc/students/application/register/StudentRegistrarTestShould.java +++ b/src/mooc/test/tv/codely/mooc/students/application/register/StudentRegistrarTestShould.java @@ -1,8 +1,9 @@ package tv.codely.mooc.students.application.register; import org.junit.jupiter.api.Test; -import tv.codely.mooc.students.domain.*; -import tv.codely.shared.domain.UuidMother; +import tv.codely.mooc.students.domain.Student; +import tv.codely.mooc.students.domain.StudentMother; +import tv.codely.mooc.students.domain.StudentRepository; import static org.mockito.Mockito.*; @@ -12,16 +13,8 @@ void register_a_valid_student() { StudentRepository repository = mock(StudentRepository.class); StudentRegistrar registrar = new StudentRegistrar(repository); - StudentId id = new StudentId(UuidMother.random()); - StudentName name = new StudentName("name"); - StudentSurname surname = new StudentSurname("surname"); - StudentEmail email = new StudentEmail("email@email.com"); - - RegisterStudentRequest request = new RegisterStudentRequest( - id.value(), name.value(), surname.value(), email.value() - ); - - Student student = new Student(id, name, surname, email); + RegisterStudentRequest request = RegisterStudentRequestMother.random(); + Student student = StudentMother.fromRequest(request); registrar.register(request); diff --git a/src/mooc/test/tv/codely/mooc/students/domain/StudentEmailMother.java b/src/mooc/test/tv/codely/mooc/students/domain/StudentEmailMother.java new file mode 100644 index 00000000..5a065725 --- /dev/null +++ b/src/mooc/test/tv/codely/mooc/students/domain/StudentEmailMother.java @@ -0,0 +1,13 @@ +package tv.codely.mooc.students.domain; + +import tv.codely.shared.domain.EmailMother; + +public final class StudentEmailMother { + public static StudentEmail create(String value) { + return new StudentEmail(value); + } + + public static StudentEmail random() { + return create(EmailMother.random()); + } +} diff --git a/src/mooc/test/tv/codely/mooc/students/domain/StudentMother.java b/src/mooc/test/tv/codely/mooc/students/domain/StudentMother.java new file mode 100644 index 00000000..964d833f --- /dev/null +++ b/src/mooc/test/tv/codely/mooc/students/domain/StudentMother.java @@ -0,0 +1,27 @@ +package tv.codely.mooc.students.domain; + +import tv.codely.mooc.students.application.register.RegisterStudentRequest; + +public class StudentMother { + public static Student create(StudentId id, StudentName name, StudentSurname surname, StudentEmail email) { + return new Student(id, name, surname, email); + } + + public static Student fromRequest(RegisterStudentRequest request) { + return create( + StudentIdMother.create(request.id()), + StudentNameMother.create(request.name()), + StudentSurnameMother.create(request.surname()), + StudentEmailMother.create(request.email()) + ); + } + + public static Student random() { + return create( + StudentIdMother.random(), + StudentNameMother.random(), + StudentSurnameMother.random(), + StudentEmailMother.random() + ); + } +} diff --git a/src/mooc/test/tv/codely/mooc/students/domain/StudentNameMother.java b/src/mooc/test/tv/codely/mooc/students/domain/StudentNameMother.java new file mode 100644 index 00000000..5633ba94 --- /dev/null +++ b/src/mooc/test/tv/codely/mooc/students/domain/StudentNameMother.java @@ -0,0 +1,14 @@ +package tv.codely.mooc.students.domain; + +import tv.codely.mooc.courses.domain.CourseName; +import tv.codely.shared.domain.WordMother; + +public final class StudentNameMother { + public static StudentName create(String value) { + return new StudentName(value); + } + + public static StudentName random() { + return create(WordMother.random()); + } +} diff --git a/src/mooc/test/tv/codely/mooc/students/domain/StudentSurnameMother.java b/src/mooc/test/tv/codely/mooc/students/domain/StudentSurnameMother.java new file mode 100644 index 00000000..223c5dbb --- /dev/null +++ b/src/mooc/test/tv/codely/mooc/students/domain/StudentSurnameMother.java @@ -0,0 +1,13 @@ +package tv.codely.mooc.students.domain; + +import tv.codely.shared.domain.WordMother; + +public final class StudentSurnameMother { + public static StudentSurname create(String value) { + return new StudentSurname(value); + } + + public static StudentSurname random() { + return create(WordMother.random()); + } +} From b3c1984f777007e2659a7ca07d22245b7c5e7dac Mon Sep 17 00:00:00 2001 From: Octavi Pascual Date: Wed, 11 Nov 2020 11:23:52 +0100 Subject: [PATCH 5/9] Add semantics to StudentRegistrar test --- .../students/StudentsModuleUnitTestCase.java | 23 +++++++++++++++++++ .../register/StudentRegistrarTestShould.java | 19 +++++++++------ 2 files changed, 35 insertions(+), 7 deletions(-) create mode 100644 src/mooc/test/tv/codely/mooc/students/StudentsModuleUnitTestCase.java diff --git a/src/mooc/test/tv/codely/mooc/students/StudentsModuleUnitTestCase.java b/src/mooc/test/tv/codely/mooc/students/StudentsModuleUnitTestCase.java new file mode 100644 index 00000000..092b4128 --- /dev/null +++ b/src/mooc/test/tv/codely/mooc/students/StudentsModuleUnitTestCase.java @@ -0,0 +1,23 @@ +package tv.codely.mooc.students; + +import org.junit.jupiter.api.BeforeEach; +import tv.codely.mooc.students.domain.Student; +import tv.codely.mooc.students.domain.StudentRepository; +import tv.codely.shared.infrastructure.UnitTestCase; + +import static org.mockito.Mockito.*; + +public abstract class StudentsModuleUnitTestCase extends UnitTestCase { + protected StudentRepository repository; + + @BeforeEach + protected void setUp() { + super.setUp(); + + repository = mock(StudentRepository.class); + } + + public void shouldHaveSaved(Student student) { + verify(repository, atLeastOnce()).register(student); + } +} diff --git a/src/mooc/test/tv/codely/mooc/students/application/register/StudentRegistrarTestShould.java b/src/mooc/test/tv/codely/mooc/students/application/register/StudentRegistrarTestShould.java index e87f87c4..116161ae 100644 --- a/src/mooc/test/tv/codely/mooc/students/application/register/StudentRegistrarTestShould.java +++ b/src/mooc/test/tv/codely/mooc/students/application/register/StudentRegistrarTestShould.java @@ -1,23 +1,28 @@ package tv.codely.mooc.students.application.register; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import tv.codely.mooc.students.StudentsModuleUnitTestCase; import tv.codely.mooc.students.domain.Student; import tv.codely.mooc.students.domain.StudentMother; -import tv.codely.mooc.students.domain.StudentRepository; -import static org.mockito.Mockito.*; +final class StudentRegistrarTestShould extends StudentsModuleUnitTestCase { + StudentRegistrar registrar; + + @BeforeEach + protected void setUp() { + super.setUp(); + + registrar = new StudentRegistrar(repository); + } -final class StudentRegistrarTestShould { @Test void register_a_valid_student() { - StudentRepository repository = mock(StudentRepository.class); - StudentRegistrar registrar = new StudentRegistrar(repository); - RegisterStudentRequest request = RegisterStudentRequestMother.random(); Student student = StudentMother.fromRequest(request); registrar.register(request); - verify(repository, atLeastOnce()).register(student); + shouldHaveSaved(student); } } From ef2f3666d9aecaccfbd2c4c59467bdcd5250e688 Mon Sep 17 00:00:00 2001 From: Octavi Pascual Date: Wed, 11 Nov 2020 17:38:04 +0100 Subject: [PATCH 6/9] Add hibernate to Student --- src/mooc/main/resources/database/mooc.sql | 11 +++++++ .../codely/mooc/students/domain/Student.java | 7 +++++ .../mooc/students/domain/StudentEmail.java | 4 +++ .../mooc/students/domain/StudentId.java | 3 ++ .../mooc/students/domain/StudentName.java | 4 +++ .../mooc/students/domain/StudentSurname.java | 4 +++ .../InMemoryStudentRepository.java | 4 +-- .../persistence/MySqlStudentRepository.java | 29 +++++++++++++++++++ .../persistence/hibernate/Student.hbm.xml | 24 +++++++++++++++ .../shared/domain/EmailValueObject.java | 14 ++++----- 10 files changed, 94 insertions(+), 10 deletions(-) rename src/mooc/main/tv/codely/mooc/students/infrastructure/{ => persistence}/InMemoryStudentRepository.java (91%) create mode 100644 src/mooc/main/tv/codely/mooc/students/infrastructure/persistence/MySqlStudentRepository.java create mode 100644 src/mooc/main/tv/codely/mooc/students/infrastructure/persistence/hibernate/Student.hbm.xml diff --git a/src/mooc/main/resources/database/mooc.sql b/src/mooc/main/resources/database/mooc.sql index 0a92a816..21a26ca2 100644 --- a/src/mooc/main/resources/database/mooc.sql +++ b/src/mooc/main/resources/database/mooc.sql @@ -59,3 +59,14 @@ CREATE TABLE IF NOT EXISTS domain_events ( ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci; + +CREATE TABLE IF NOT EXISTS students ( + id CHAR(36) NOT NULL, + name VARCHAR(255) NOT NULL, + surname VARCHAR(255) NOT NULL, + email VARCHAR(255) NOT NULL, + PRIMARY KEY (id) +) + ENGINE = InnoDB + DEFAULT CHARSET = utf8mb4 + COLLATE = utf8mb4_unicode_ci; diff --git a/src/mooc/main/tv/codely/mooc/students/domain/Student.java b/src/mooc/main/tv/codely/mooc/students/domain/Student.java index 543557be..c06d7329 100644 --- a/src/mooc/main/tv/codely/mooc/students/domain/Student.java +++ b/src/mooc/main/tv/codely/mooc/students/domain/Student.java @@ -15,6 +15,13 @@ public Student(StudentId id, StudentName name, StudentSurname surname, StudentEm this.email = email; } + private Student() { + id = null; + name = null; + surname = null; + email = null; + } + public static Student create(StudentId id, StudentName name, StudentSurname surname, StudentEmail email) { return new Student(id, name, surname, email); } diff --git a/src/mooc/main/tv/codely/mooc/students/domain/StudentEmail.java b/src/mooc/main/tv/codely/mooc/students/domain/StudentEmail.java index a3d395b2..a7952ea4 100644 --- a/src/mooc/main/tv/codely/mooc/students/domain/StudentEmail.java +++ b/src/mooc/main/tv/codely/mooc/students/domain/StudentEmail.java @@ -6,4 +6,8 @@ public final class StudentEmail extends EmailValueObject { public StudentEmail(String email) { super(email); } + + private StudentEmail() { + super(""); + } } diff --git a/src/mooc/main/tv/codely/mooc/students/domain/StudentId.java b/src/mooc/main/tv/codely/mooc/students/domain/StudentId.java index 3e6735ed..10fbfd57 100644 --- a/src/mooc/main/tv/codely/mooc/students/domain/StudentId.java +++ b/src/mooc/main/tv/codely/mooc/students/domain/StudentId.java @@ -6,4 +6,7 @@ public final class StudentId extends Identifier { public StudentId(String value) { super(value); } + + private StudentId() { + } } diff --git a/src/mooc/main/tv/codely/mooc/students/domain/StudentName.java b/src/mooc/main/tv/codely/mooc/students/domain/StudentName.java index 9003196b..ad4afe20 100644 --- a/src/mooc/main/tv/codely/mooc/students/domain/StudentName.java +++ b/src/mooc/main/tv/codely/mooc/students/domain/StudentName.java @@ -6,4 +6,8 @@ public final class StudentName extends StringValueObject { public StudentName(String value) { super(value); } + + private StudentName() { + super(""); + } } diff --git a/src/mooc/main/tv/codely/mooc/students/domain/StudentSurname.java b/src/mooc/main/tv/codely/mooc/students/domain/StudentSurname.java index 5ffd44ec..6c0fd0de 100644 --- a/src/mooc/main/tv/codely/mooc/students/domain/StudentSurname.java +++ b/src/mooc/main/tv/codely/mooc/students/domain/StudentSurname.java @@ -6,4 +6,8 @@ public final class StudentSurname extends StringValueObject { public StudentSurname(String value) { super(value); } + + private StudentSurname() { + super(""); + } } diff --git a/src/mooc/main/tv/codely/mooc/students/infrastructure/InMemoryStudentRepository.java b/src/mooc/main/tv/codely/mooc/students/infrastructure/persistence/InMemoryStudentRepository.java similarity index 91% rename from src/mooc/main/tv/codely/mooc/students/infrastructure/InMemoryStudentRepository.java rename to src/mooc/main/tv/codely/mooc/students/infrastructure/persistence/InMemoryStudentRepository.java index e4703e58..c46d2566 100644 --- a/src/mooc/main/tv/codely/mooc/students/infrastructure/InMemoryStudentRepository.java +++ b/src/mooc/main/tv/codely/mooc/students/infrastructure/persistence/InMemoryStudentRepository.java @@ -1,13 +1,11 @@ -package tv.codely.mooc.students.infrastructure; +package tv.codely.mooc.students.infrastructure.persistence; import tv.codely.mooc.students.domain.*; -import tv.codely.shared.domain.Service; import tv.codely.shared.domain.UuidGenerator; import java.util.Arrays; import java.util.List; -@Service public final class InMemoryStudentRepository implements StudentRepository { private UuidGenerator generator; diff --git a/src/mooc/main/tv/codely/mooc/students/infrastructure/persistence/MySqlStudentRepository.java b/src/mooc/main/tv/codely/mooc/students/infrastructure/persistence/MySqlStudentRepository.java new file mode 100644 index 00000000..328e4468 --- /dev/null +++ b/src/mooc/main/tv/codely/mooc/students/infrastructure/persistence/MySqlStudentRepository.java @@ -0,0 +1,29 @@ +package tv.codely.mooc.students.infrastructure.persistence; + +import org.hibernate.SessionFactory; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.transaction.annotation.Transactional; +import tv.codely.mooc.students.domain.Student; +import tv.codely.mooc.students.domain.StudentRepository; +import tv.codely.shared.domain.Service; +import tv.codely.shared.infrastructure.hibernate.HibernateRepository; + +import java.util.List; + +@Service +@Transactional("mooc-transaction_manager") +public class MySqlStudentRepository extends HibernateRepository implements StudentRepository { + public MySqlStudentRepository(@Qualifier("mooc-session_factory") SessionFactory sessionFactory) { + super(sessionFactory, Student.class); + } + + @Override + public void register(Student student) { + persist(student); + } + + @Override + public List searchAll() { + return all(); + } +} diff --git a/src/mooc/main/tv/codely/mooc/students/infrastructure/persistence/hibernate/Student.hbm.xml b/src/mooc/main/tv/codely/mooc/students/infrastructure/persistence/hibernate/Student.hbm.xml new file mode 100644 index 00000000..3be11a0c --- /dev/null +++ b/src/mooc/main/tv/codely/mooc/students/infrastructure/persistence/hibernate/Student.hbm.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/shared/main/tv/codely/shared/domain/EmailValueObject.java b/src/shared/main/tv/codely/shared/domain/EmailValueObject.java index ea0c6607..033e324c 100644 --- a/src/shared/main/tv/codely/shared/domain/EmailValueObject.java +++ b/src/shared/main/tv/codely/shared/domain/EmailValueObject.java @@ -6,15 +6,15 @@ public abstract class EmailValueObject { private static final EmailValidator emailValidator = new EmailValidator(); - private final String email; + private String value; - public EmailValueObject(String email) { - ensureValidEmail(email); - this.email = email; + public EmailValueObject(String value) { + ensureValidEmail(value); + this.value = value; } public String value() { - return email; + return value; } private void ensureValidEmail(String value) { @@ -37,11 +37,11 @@ public boolean equals(Object o) { return false; } EmailValueObject that = (EmailValueObject) o; - return Objects.equals(email, that.email); + return Objects.equals(value, that.value); } @Override public int hashCode() { - return Objects.hash(email); + return Objects.hash(value); } } From 586c0d2f8547a739167eedcf8b609e4f02c26703 Mon Sep 17 00:00:00 2001 From: Octavi Pascual Date: Wed, 11 Nov 2020 20:03:50 +0100 Subject: [PATCH 7/9] Add Student repository integration test --- .../StudentsModuleInfrastructureTestCase.java | 10 +++++ .../MySqlStudentRepositoryShould.java | 41 +++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 src/mooc/test/tv/codely/mooc/students/StudentsModuleInfrastructureTestCase.java create mode 100644 src/mooc/test/tv/codely/mooc/students/infrastructure/persistence/MySqlStudentRepositoryShould.java diff --git a/src/mooc/test/tv/codely/mooc/students/StudentsModuleInfrastructureTestCase.java b/src/mooc/test/tv/codely/mooc/students/StudentsModuleInfrastructureTestCase.java new file mode 100644 index 00000000..9ad7f892 --- /dev/null +++ b/src/mooc/test/tv/codely/mooc/students/StudentsModuleInfrastructureTestCase.java @@ -0,0 +1,10 @@ +package tv.codely.mooc.students; + +import org.springframework.beans.factory.annotation.Autowired; +import tv.codely.mooc.MoocContextInfrastructureTestCase; +import tv.codely.mooc.students.domain.StudentRepository; + +public abstract class StudentsModuleInfrastructureTestCase extends MoocContextInfrastructureTestCase { + @Autowired + protected StudentRepository mySqlStudentRepository; +} diff --git a/src/mooc/test/tv/codely/mooc/students/infrastructure/persistence/MySqlStudentRepositoryShould.java b/src/mooc/test/tv/codely/mooc/students/infrastructure/persistence/MySqlStudentRepositoryShould.java new file mode 100644 index 00000000..fad6760e --- /dev/null +++ b/src/mooc/test/tv/codely/mooc/students/infrastructure/persistence/MySqlStudentRepositoryShould.java @@ -0,0 +1,41 @@ +package tv.codely.mooc.students.infrastructure.persistence; + +import org.junit.jupiter.api.Test; +import tv.codely.mooc.students.StudentsModuleInfrastructureTestCase; +import tv.codely.mooc.students.domain.Student; +import tv.codely.mooc.students.domain.StudentMother; + +import javax.transaction.Transactional; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@Transactional +class MySqlStudentRepositoryShould extends StudentsModuleInfrastructureTestCase { + @Test + void register_a_student() { + Student student = StudentMother.random(); + + mySqlStudentRepository.register(student); + } + + @Test + void return_all_existing_students() { + Student student1 = StudentMother.random(); + Student student2 = StudentMother.random(); + + mySqlStudentRepository.register(student1); + mySqlStudentRepository.register(student2); + + List students = mySqlStudentRepository.searchAll(); + assertEquals(2, students.size()); + assertEquals(student1, students.get(0)); + assertEquals(student2, students.get(1)); + } + + @Test + void not_return_non_existing_students() { + assertTrue(mySqlStudentRepository.searchAll().isEmpty()); + } +} From 5bd2db51ac2edee66a6211d9af070f091269c6a6 Mon Sep 17 00:00:00 2001 From: Octavi Pascual Date: Fri, 13 Nov 2020 09:36:52 +0100 Subject: [PATCH 8/9] Raise event on Student registered --- .../register/StudentRegistrar.java | 6 +- .../codely/mooc/students/domain/Student.java | 11 +- .../register/StudentRegistrarTestShould.java | 10 +- .../StudentRegisteredDomainEventMother.java | 24 ++++ .../student/StudentRegisteredDomainEvent.java | 106 ++++++++++++++++++ 5 files changed, 151 insertions(+), 6 deletions(-) create mode 100644 src/mooc/test/tv/codely/mooc/students/domain/StudentRegisteredDomainEventMother.java create mode 100644 src/shared/main/tv/codely/shared/domain/student/StudentRegisteredDomainEvent.java diff --git a/src/mooc/main/tv/codely/mooc/students/application/register/StudentRegistrar.java b/src/mooc/main/tv/codely/mooc/students/application/register/StudentRegistrar.java index 01a4055a..836d4f4d 100644 --- a/src/mooc/main/tv/codely/mooc/students/application/register/StudentRegistrar.java +++ b/src/mooc/main/tv/codely/mooc/students/application/register/StudentRegistrar.java @@ -2,13 +2,16 @@ import tv.codely.mooc.students.domain.*; import tv.codely.shared.domain.Service; +import tv.codely.shared.domain.bus.event.EventBus; @Service public class StudentRegistrar { private final StudentRepository repository; + private final EventBus eventBus; - public StudentRegistrar(StudentRepository repository) { + public StudentRegistrar(StudentRepository repository, EventBus eventBus) { this.repository = repository; + this.eventBus = eventBus; } public void register(RegisterStudentRequest request) { @@ -19,5 +22,6 @@ public void register(RegisterStudentRequest request) { new StudentEmail(request.email()) ); repository.register(student); + eventBus.publish(student.pullDomainEvents()); } } diff --git a/src/mooc/main/tv/codely/mooc/students/domain/Student.java b/src/mooc/main/tv/codely/mooc/students/domain/Student.java index c06d7329..ebd6b656 100644 --- a/src/mooc/main/tv/codely/mooc/students/domain/Student.java +++ b/src/mooc/main/tv/codely/mooc/students/domain/Student.java @@ -1,8 +1,11 @@ package tv.codely.mooc.students.domain; +import tv.codely.shared.domain.AggregateRoot; +import tv.codely.shared.domain.student.StudentRegisteredDomainEvent; + import java.util.Objects; -public final class Student { +public final class Student extends AggregateRoot { private final StudentId id; private final StudentName name; private final StudentSurname surname; @@ -23,7 +26,11 @@ private Student() { } public static Student create(StudentId id, StudentName name, StudentSurname surname, StudentEmail email) { - return new Student(id, name, surname, email); + Student student = new Student(id, name, surname, email); + + student.record(new StudentRegisteredDomainEvent(id.value(), name.value(), surname.value(), email.value())); + + return student; } public StudentId id() { diff --git a/src/mooc/test/tv/codely/mooc/students/application/register/StudentRegistrarTestShould.java b/src/mooc/test/tv/codely/mooc/students/application/register/StudentRegistrarTestShould.java index 116161ae..a6fd25ce 100644 --- a/src/mooc/test/tv/codely/mooc/students/application/register/StudentRegistrarTestShould.java +++ b/src/mooc/test/tv/codely/mooc/students/application/register/StudentRegistrarTestShould.java @@ -5,6 +5,8 @@ import tv.codely.mooc.students.StudentsModuleUnitTestCase; import tv.codely.mooc.students.domain.Student; import tv.codely.mooc.students.domain.StudentMother; +import tv.codely.mooc.students.domain.StudentRegisteredDomainEventMother; +import tv.codely.shared.domain.student.StudentRegisteredDomainEvent; final class StudentRegistrarTestShould extends StudentsModuleUnitTestCase { StudentRegistrar registrar; @@ -13,16 +15,18 @@ final class StudentRegistrarTestShould extends StudentsModuleUnitTestCase { protected void setUp() { super.setUp(); - registrar = new StudentRegistrar(repository); + registrar = new StudentRegistrar(repository, eventBus); } @Test void register_a_valid_student() { - RegisterStudentRequest request = RegisterStudentRequestMother.random(); - Student student = StudentMother.fromRequest(request); + RegisterStudentRequest request = RegisterStudentRequestMother.random(); + Student student = StudentMother.fromRequest(request); + StudentRegisteredDomainEvent domainEvent = StudentRegisteredDomainEventMother.fromStudent(student); registrar.register(request); shouldHaveSaved(student); + shouldHavePublished(domainEvent); } } diff --git a/src/mooc/test/tv/codely/mooc/students/domain/StudentRegisteredDomainEventMother.java b/src/mooc/test/tv/codely/mooc/students/domain/StudentRegisteredDomainEventMother.java new file mode 100644 index 00000000..ede2446c --- /dev/null +++ b/src/mooc/test/tv/codely/mooc/students/domain/StudentRegisteredDomainEventMother.java @@ -0,0 +1,24 @@ +package tv.codely.mooc.students.domain; + +import tv.codely.shared.domain.student.StudentRegisteredDomainEvent; + +public final class StudentRegisteredDomainEventMother { + public static StudentRegisteredDomainEvent create( + StudentId id, StudentName name, StudentSurname surname, StudentEmail email + ) { + return new StudentRegisteredDomainEvent(id.value(), name.value(), surname.value(), email.value()); + } + + public static StudentRegisteredDomainEvent fromStudent(Student student) { + return create(student.id(), student.name(), student.surname(), student.email()); + } + + public static StudentRegisteredDomainEvent random() { + return create( + StudentIdMother.random(), + StudentNameMother.random(), + StudentSurnameMother.random(), + StudentEmailMother.random() + ); + } +} diff --git a/src/shared/main/tv/codely/shared/domain/student/StudentRegisteredDomainEvent.java b/src/shared/main/tv/codely/shared/domain/student/StudentRegisteredDomainEvent.java new file mode 100644 index 00000000..7762aa31 --- /dev/null +++ b/src/shared/main/tv/codely/shared/domain/student/StudentRegisteredDomainEvent.java @@ -0,0 +1,106 @@ +package tv.codely.shared.domain.student; + +import tv.codely.shared.domain.bus.event.DomainEvent; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Objects; + +public final class StudentRegisteredDomainEvent extends DomainEvent { + private final String name; + private final String surname; + private final String email; + + public StudentRegisteredDomainEvent() { + super(null); + + this.name = null; + this.surname = null; + this.email = null; + } + + public StudentRegisteredDomainEvent(String aggregateId, String name, String surname, String email) { + super(aggregateId); + + this.name = name; + this.surname = surname; + this.email = email; + } + + public StudentRegisteredDomainEvent( + String aggregateId, + String eventId, + String occurredOn, + String name, + String surname, + String email + ) { + super(aggregateId, eventId, occurredOn); + + this.name = name; + this.surname = surname; + this.email = email; + } + + @Override + public String eventName() { + return "student.registered"; + } + + @Override + public HashMap toPrimitives() { + return new HashMap() {{ + put("name", name); + put("surname", surname); + put("email", email); + }}; + } + + @Override + public StudentRegisteredDomainEvent fromPrimitives( + String aggregateId, + HashMap body, + String eventId, + String occurredOn + ) { + return new StudentRegisteredDomainEvent( + aggregateId, + eventId, + occurredOn, + (String) body.get("name"), + (String) body.get("surname"), + (String) body.get("email") + ); + } + + public String name() { + return name; + } + + public String surname() { + return surname; + } + + public String email() { + return email; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + StudentRegisteredDomainEvent that = (StudentRegisteredDomainEvent) o; + return name.equals(that.name) && + surname.equals(that.surname) && + email.equals(that.email); + } + + @Override + public int hashCode() { + return Objects.hash(name, surname, email); + } +} From 2249b135a57879d5e3cdb49ba06e08bd07257a40 Mon Sep 17 00:00:00 2001 From: Octavi Pascual Date: Sat, 14 Nov 2020 20:14:50 +0100 Subject: [PATCH 9/9] Migrate register Student use case to command --- .../students/StudentsPutController.java | 27 +++++++++++++------ ...quest.java => RegisterStudentCommand.java} | 6 +++-- .../RegisterStudentCommandHandler.java | 27 +++++++++++++++++++ .../register/StudentRegistrar.java | 15 ++++++----- ... RegisterStudentCommandHandlerShould.java} | 12 ++++----- ...java => RegisterStudentCommandMother.java} | 8 +++--- .../mooc/students/domain/StudentMother.java | 4 +-- 7 files changed, 70 insertions(+), 29 deletions(-) rename src/mooc/main/tv/codely/mooc/students/application/register/{RegisterStudentRequest.java => RegisterStudentCommand.java} (75%) create mode 100644 src/mooc/main/tv/codely/mooc/students/application/register/RegisterStudentCommandHandler.java rename src/mooc/test/tv/codely/mooc/students/application/register/{StudentRegistrarTestShould.java => RegisterStudentCommandHandlerShould.java} (68%) rename src/mooc/test/tv/codely/mooc/students/application/register/{RegisterStudentRequestMother.java => RegisterStudentCommandMother.java} (64%) diff --git a/apps/main/tv/codely/apps/mooc/backend/controller/students/StudentsPutController.java b/apps/main/tv/codely/apps/mooc/backend/controller/students/StudentsPutController.java index 4af756b7..687d1add 100644 --- a/apps/main/tv/codely/apps/mooc/backend/controller/students/StudentsPutController.java +++ b/apps/main/tv/codely/apps/mooc/backend/controller/students/StudentsPutController.java @@ -6,23 +6,34 @@ import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; -import tv.codely.mooc.students.application.register.RegisterStudentRequest; -import tv.codely.mooc.students.application.register.StudentRegistrar; +import tv.codely.mooc.students.application.register.RegisterStudentCommand; +import tv.codely.shared.domain.DomainError; +import tv.codely.shared.domain.bus.command.CommandBus; +import tv.codely.shared.domain.bus.query.QueryBus; +import tv.codely.shared.infrastructure.spring.ApiController; -@RestController -public final class StudentsPutController { +import java.util.HashMap; - private final StudentRegistrar registrar; +@RestController +public final class StudentsPutController extends ApiController { - public StudentsPutController(StudentRegistrar registrar) { - this.registrar = registrar; + public StudentsPutController( + QueryBus queryBus, + CommandBus commandBus + ) { + super(queryBus, commandBus); } @PutMapping(value = "/students/{id}") public ResponseEntity index(@PathVariable String id, @RequestBody Request request) { - registrar.register(new RegisterStudentRequest(id, request.name(), request.surname(), request.email())); + dispatch(new RegisterStudentCommand(id, request.name(), request.surname(), request.email())); return new ResponseEntity<>(HttpStatus.CREATED); } + + @Override + public HashMap, HttpStatus> errorMapping() { + return null; + } } final class Request { diff --git a/src/mooc/main/tv/codely/mooc/students/application/register/RegisterStudentRequest.java b/src/mooc/main/tv/codely/mooc/students/application/register/RegisterStudentCommand.java similarity index 75% rename from src/mooc/main/tv/codely/mooc/students/application/register/RegisterStudentRequest.java rename to src/mooc/main/tv/codely/mooc/students/application/register/RegisterStudentCommand.java index 2fdd4527..1a12cd99 100644 --- a/src/mooc/main/tv/codely/mooc/students/application/register/RegisterStudentRequest.java +++ b/src/mooc/main/tv/codely/mooc/students/application/register/RegisterStudentCommand.java @@ -1,12 +1,14 @@ package tv.codely.mooc.students.application.register; -public final class RegisterStudentRequest { +import tv.codely.shared.domain.bus.command.Command; + +public final class RegisterStudentCommand implements Command { private final String id; private final String name; private final String surname; private final String email; - public RegisterStudentRequest(String id, String name, String surname, String email) { + public RegisterStudentCommand(String id, String name, String surname, String email) { this.id = id; this.name = name; this.surname = surname; diff --git a/src/mooc/main/tv/codely/mooc/students/application/register/RegisterStudentCommandHandler.java b/src/mooc/main/tv/codely/mooc/students/application/register/RegisterStudentCommandHandler.java new file mode 100644 index 00000000..43ed312c --- /dev/null +++ b/src/mooc/main/tv/codely/mooc/students/application/register/RegisterStudentCommandHandler.java @@ -0,0 +1,27 @@ +package tv.codely.mooc.students.application.register; + +import tv.codely.mooc.students.domain.StudentEmail; +import tv.codely.mooc.students.domain.StudentId; +import tv.codely.mooc.students.domain.StudentName; +import tv.codely.mooc.students.domain.StudentSurname; +import tv.codely.shared.domain.Service; +import tv.codely.shared.domain.bus.command.CommandHandler; + +@Service +public final class RegisterStudentCommandHandler implements CommandHandler { + private final StudentRegistrar registrar; + + public RegisterStudentCommandHandler(StudentRegistrar registrar) { + this.registrar = registrar; + } + + @Override + public void handle(RegisterStudentCommand command) { + StudentId id = new StudentId(command.id()); + StudentName name = new StudentName(command.name()); + StudentSurname surname = new StudentSurname(command.surname()); + StudentEmail email = new StudentEmail(command.email()); + + registrar.register(id, name, surname, email); + } +} diff --git a/src/mooc/main/tv/codely/mooc/students/application/register/StudentRegistrar.java b/src/mooc/main/tv/codely/mooc/students/application/register/StudentRegistrar.java index 836d4f4d..d59d46c4 100644 --- a/src/mooc/main/tv/codely/mooc/students/application/register/StudentRegistrar.java +++ b/src/mooc/main/tv/codely/mooc/students/application/register/StudentRegistrar.java @@ -14,13 +14,14 @@ public StudentRegistrar(StudentRepository repository, EventBus eventBus) { this.eventBus = eventBus; } - public void register(RegisterStudentRequest request) { - Student student = Student.create( - new StudentId(request.id()), - new StudentName(request.name()), - new StudentSurname(request.surname()), - new StudentEmail(request.email()) - ); + public void register( + StudentId id, + StudentName name, + StudentSurname surname, + StudentEmail email + ) { + Student student = Student.create(id, name, surname, email); + repository.register(student); eventBus.publish(student.pullDomainEvents()); } diff --git a/src/mooc/test/tv/codely/mooc/students/application/register/StudentRegistrarTestShould.java b/src/mooc/test/tv/codely/mooc/students/application/register/RegisterStudentCommandHandlerShould.java similarity index 68% rename from src/mooc/test/tv/codely/mooc/students/application/register/StudentRegistrarTestShould.java rename to src/mooc/test/tv/codely/mooc/students/application/register/RegisterStudentCommandHandlerShould.java index a6fd25ce..e00c8a35 100644 --- a/src/mooc/test/tv/codely/mooc/students/application/register/StudentRegistrarTestShould.java +++ b/src/mooc/test/tv/codely/mooc/students/application/register/RegisterStudentCommandHandlerShould.java @@ -8,23 +8,23 @@ import tv.codely.mooc.students.domain.StudentRegisteredDomainEventMother; import tv.codely.shared.domain.student.StudentRegisteredDomainEvent; -final class StudentRegistrarTestShould extends StudentsModuleUnitTestCase { - StudentRegistrar registrar; +final class RegisterStudentCommandHandlerShould extends StudentsModuleUnitTestCase { + private RegisterStudentCommandHandler handler; @BeforeEach protected void setUp() { super.setUp(); - registrar = new StudentRegistrar(repository, eventBus); + handler = new RegisterStudentCommandHandler(new StudentRegistrar(repository, eventBus)); } @Test void register_a_valid_student() { - RegisterStudentRequest request = RegisterStudentRequestMother.random(); - Student student = StudentMother.fromRequest(request); + RegisterStudentCommand command = RegisterStudentCommandMother.random(); + Student student = StudentMother.fromCommand(command); StudentRegisteredDomainEvent domainEvent = StudentRegisteredDomainEventMother.fromStudent(student); - registrar.register(request); + handler.handle(command); shouldHaveSaved(student); shouldHavePublished(domainEvent); diff --git a/src/mooc/test/tv/codely/mooc/students/application/register/RegisterStudentRequestMother.java b/src/mooc/test/tv/codely/mooc/students/application/register/RegisterStudentCommandMother.java similarity index 64% rename from src/mooc/test/tv/codely/mooc/students/application/register/RegisterStudentRequestMother.java rename to src/mooc/test/tv/codely/mooc/students/application/register/RegisterStudentCommandMother.java index 2237d091..c6f10dfe 100644 --- a/src/mooc/test/tv/codely/mooc/students/application/register/RegisterStudentRequestMother.java +++ b/src/mooc/test/tv/codely/mooc/students/application/register/RegisterStudentCommandMother.java @@ -2,12 +2,12 @@ import tv.codely.mooc.students.domain.*; -public class RegisterStudentRequestMother { - public static RegisterStudentRequest create(StudentId id, StudentName name, StudentSurname surname, StudentEmail email) { - return new RegisterStudentRequest(id.value(), name.value(), surname.value(), email.value()); +public class RegisterStudentCommandMother { + public static RegisterStudentCommand create(StudentId id, StudentName name, StudentSurname surname, StudentEmail email) { + return new RegisterStudentCommand(id.value(), name.value(), surname.value(), email.value()); } - public static RegisterStudentRequest random() { + public static RegisterStudentCommand random() { return create( StudentIdMother.random(), StudentNameMother.random(), diff --git a/src/mooc/test/tv/codely/mooc/students/domain/StudentMother.java b/src/mooc/test/tv/codely/mooc/students/domain/StudentMother.java index 964d833f..ece8e1d4 100644 --- a/src/mooc/test/tv/codely/mooc/students/domain/StudentMother.java +++ b/src/mooc/test/tv/codely/mooc/students/domain/StudentMother.java @@ -1,13 +1,13 @@ package tv.codely.mooc.students.domain; -import tv.codely.mooc.students.application.register.RegisterStudentRequest; +import tv.codely.mooc.students.application.register.RegisterStudentCommand; public class StudentMother { public static Student create(StudentId id, StudentName name, StudentSurname surname, StudentEmail email) { return new Student(id, name, surname, email); } - public static Student fromRequest(RegisterStudentRequest request) { + public static Student fromCommand(RegisterStudentCommand request) { return create( StudentIdMother.create(request.id()), StudentNameMother.create(request.name()),