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..687d1add --- /dev/null +++ b/apps/main/tv/codely/apps/mooc/backend/controller/students/StudentsPutController.java @@ -0,0 +1,67 @@ +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.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; + +import java.util.HashMap; + +@RestController +public final class StudentsPutController extends ApiController { + + public StudentsPutController( + QueryBus queryBus, + CommandBus commandBus + ) { + super(queryBus, commandBus); + } + + @PutMapping(value = "/students/{id}") + public ResponseEntity index(@PathVariable String id, @RequestBody Request request) { + 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 { + 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..7ed233d1 --- /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\": \"email@email.com\"}", + 201 + ); + } +} 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/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/RegisterStudentCommand.java b/src/mooc/main/tv/codely/mooc/students/application/register/RegisterStudentCommand.java new file mode 100644 index 00000000..1a12cd99 --- /dev/null +++ b/src/mooc/main/tv/codely/mooc/students/application/register/RegisterStudentCommand.java @@ -0,0 +1,33 @@ +package tv.codely.mooc.students.application.register; + +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 RegisterStudentCommand(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/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 new file mode 100644 index 00000000..d59d46c4 --- /dev/null +++ b/src/mooc/main/tv/codely/mooc/students/application/register/StudentRegistrar.java @@ -0,0 +1,28 @@ +package tv.codely.mooc.students.application.register; + +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, EventBus eventBus) { + this.repository = repository; + this.eventBus = eventBus; + } + + 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/main/tv/codely/mooc/students/domain/Student.java b/src/mooc/main/tv/codely/mooc/students/domain/Student.java index eb0353d1..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,31 +1,67 @@ package tv.codely.mooc.students.domain; -public final class Student { - private final StudentId id; - private final String name; - private final String surname; - private final String email; +import tv.codely.shared.domain.AggregateRoot; +import tv.codely.shared.domain.student.StudentRegisteredDomainEvent; - public Student(StudentId id, String name, String surname, String email) { +import java.util.Objects; + +public final class Student extends AggregateRoot { + private final StudentId id; + private final StudentName name; + private final StudentSurname surname; + private final StudentEmail email; + + public Student(StudentId id, StudentName name, StudentSurname surname, StudentEmail email) { this.id = id; this.name = name; this.surname = surname; 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) { + 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() { 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; } + + @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/StudentEmail.java b/src/mooc/main/tv/codely/mooc/students/domain/StudentEmail.java new file mode 100644 index 00000000..a7952ea4 --- /dev/null +++ b/src/mooc/main/tv/codely/mooc/students/domain/StudentEmail.java @@ -0,0 +1,13 @@ +package tv.codely.mooc.students.domain; + +import tv.codely.shared.domain.EmailValueObject; + +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 new file mode 100644 index 00000000..ad4afe20 --- /dev/null +++ b/src/mooc/main/tv/codely/mooc/students/domain/StudentName.java @@ -0,0 +1,13 @@ +package tv.codely.mooc.students.domain; + +import tv.codely.shared.domain.StringValueObject; + +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/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/domain/StudentSurname.java b/src/mooc/main/tv/codely/mooc/students/domain/StudentSurname.java new file mode 100644 index 00000000..6c0fd0de --- /dev/null +++ b/src/mooc/main/tv/codely/mooc/students/domain/StudentSurname.java @@ -0,0 +1,13 @@ +package tv.codely.mooc.students.domain; + +import tv.codely.shared.domain.StringValueObject; + +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/InMemoryStudentRepository.java deleted file mode 100644 index aabd3abf..00000000 --- a/src/mooc/main/tv/codely/mooc/students/infrastructure/InMemoryStudentRepository.java +++ /dev/null @@ -1,27 +0,0 @@ -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.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; - - public InMemoryStudentRepository(UuidGenerator generator) { - this.generator = 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") - ); - } -} diff --git a/src/mooc/main/tv/codely/mooc/students/infrastructure/persistence/InMemoryStudentRepository.java b/src/mooc/main/tv/codely/mooc/students/infrastructure/persistence/InMemoryStudentRepository.java new file mode 100644 index 00000000..c46d2566 --- /dev/null +++ b/src/mooc/main/tv/codely/mooc/students/infrastructure/persistence/InMemoryStudentRepository.java @@ -0,0 +1,37 @@ +package tv.codely.mooc.students.infrastructure.persistence; + +import tv.codely.mooc.students.domain.*; +import tv.codely.shared.domain.UuidGenerator; + +import java.util.Arrays; +import java.util.List; + +public final class InMemoryStudentRepository implements StudentRepository { + private UuidGenerator generator; + + public InMemoryStudentRepository(UuidGenerator generator) { + this.generator = generator; + } + + @Override + public List searchAll() { + return Arrays.asList( + 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) { + } +} 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/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/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/RegisterStudentCommandHandlerShould.java b/src/mooc/test/tv/codely/mooc/students/application/register/RegisterStudentCommandHandlerShould.java new file mode 100644 index 00000000..e00c8a35 --- /dev/null +++ b/src/mooc/test/tv/codely/mooc/students/application/register/RegisterStudentCommandHandlerShould.java @@ -0,0 +1,32 @@ +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.StudentRegisteredDomainEventMother; +import tv.codely.shared.domain.student.StudentRegisteredDomainEvent; + +final class RegisterStudentCommandHandlerShould extends StudentsModuleUnitTestCase { + private RegisterStudentCommandHandler handler; + + @BeforeEach + protected void setUp() { + super.setUp(); + + handler = new RegisterStudentCommandHandler(new StudentRegistrar(repository, eventBus)); + } + + @Test + void register_a_valid_student() { + RegisterStudentCommand command = RegisterStudentCommandMother.random(); + Student student = StudentMother.fromCommand(command); + StudentRegisteredDomainEvent domainEvent = StudentRegisteredDomainEventMother.fromStudent(student); + + handler.handle(command); + + shouldHaveSaved(student); + shouldHavePublished(domainEvent); + } +} diff --git a/src/mooc/test/tv/codely/mooc/students/application/register/RegisterStudentCommandMother.java b/src/mooc/test/tv/codely/mooc/students/application/register/RegisterStudentCommandMother.java new file mode 100644 index 00000000..c6f10dfe --- /dev/null +++ b/src/mooc/test/tv/codely/mooc/students/application/register/RegisterStudentCommandMother.java @@ -0,0 +1,18 @@ +package tv.codely.mooc.students.application.register; + +import tv.codely.mooc.students.domain.*; + +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 RegisterStudentCommand random() { + return create( + StudentIdMother.random(), + StudentNameMother.random(), + StudentSurnameMother.random(), + StudentEmailMother.random() + ); + } +} 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..ece8e1d4 --- /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.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 fromCommand(RegisterStudentCommand 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/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/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()); + } +} 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()); + } +} 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..033e324c --- /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 String value; + + public EmailValueObject(String value) { + ensureValidEmail(value); + this.value = value; + } + + public String value() { + return value; + } + + 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(value, that.value); + } + + @Override + public int hashCode() { + return Objects.hash(value); + } +} 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)); + } +} 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); + } +}