From 2fa8261dc88d1545c92d16dcda0540cf8259c4d0 Mon Sep 17 00:00:00 2001 From: amintalukder Date: Mon, 27 Jun 2022 13:45:37 +0100 Subject: [PATCH 1/2] #33 OpenAccountUseCase - Publishing events --- docker-compose.yml | 22 ++++++----- .../com/optivem/kata/banking/core/Facade.java | 6 +-- .../domain/common/events/EventPublisher.java | 8 ++++ .../common/events/EventPublisherToQueue.java | 21 ++++++++++ .../domain/common/events/UseCaseEvent.java | 24 ++++++++++++ .../common/events/UseCaseEventHandler.java | 31 +++++++++++++++ .../AccountOpenedUseCaseEvent.java | 14 +++++++ .../openaccount/OpenAccountUseCase.java | 8 +++- .../infra/real/inmemory/EventQueue.java | 30 ++++++++++++++ src/main/resources/application.yml | 6 +-- .../core/common/factories/FacadeFactory.java | 6 ++- .../core/usecases/OpenAccountUseCaseTest.java | 39 ++++++++++++++++++- 12 files changed, 196 insertions(+), 19 deletions(-) create mode 100644 src/main/java/com/optivem/kata/banking/core/domain/common/events/EventPublisher.java create mode 100644 src/main/java/com/optivem/kata/banking/core/domain/common/events/EventPublisherToQueue.java create mode 100644 src/main/java/com/optivem/kata/banking/core/domain/common/events/UseCaseEvent.java create mode 100644 src/main/java/com/optivem/kata/banking/core/domain/common/events/UseCaseEventHandler.java create mode 100644 src/main/java/com/optivem/kata/banking/core/domain/common/events/UseCaseEvents/AccountOpenedUseCaseEvent.java create mode 100644 src/main/java/com/optivem/kata/banking/infra/real/inmemory/EventQueue.java diff --git a/docker-compose.yml b/docker-compose.yml index dcca8d0f..18bf6800 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,13 +1,15 @@ +version: '3.8' services: - postgres: - image: postgres:latest - container_name: postgres + db: + image: postgres:14.1-alpine + restart: always + environment: + - POSTGRES_USER=postgres + - POSTGRES_PASSWORD=postgres ports: - - "5432:5432" - restart: unless-stopped + - '5432:5432' volumes: - - ./db_persist:/var/lib/postgresql/data - environment: - POSTGRES_USER: ${POSTGRES_USER} - POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} - POSTGRES_DB: ${POSTGRES_DB} + - db:/var/lib/postgresql/data +volumes: + db: + driver: local \ No newline at end of file diff --git a/src/main/java/com/optivem/kata/banking/core/Facade.java b/src/main/java/com/optivem/kata/banking/core/Facade.java index 30c72a58..a6ebe364 100644 --- a/src/main/java/com/optivem/kata/banking/core/Facade.java +++ b/src/main/java/com/optivem/kata/banking/core/Facade.java @@ -1,10 +1,10 @@ package com.optivem.kata.banking.core; +import com.optivem.kata.banking.core.domain.common.events.EventPublisher; import com.optivem.kata.banking.core.domain.scoring.DefaultScoreCalculator; import com.optivem.kata.banking.core.domain.accounts.AccountIdGenerator; import com.optivem.kata.banking.core.domain.accounts.AccountNumberGenerator; import com.optivem.kata.banking.core.domain.accounts.BankAccountRepository; -import com.optivem.kata.banking.core.domain.accounts.scoring.*; import com.optivem.kata.banking.core.ports.driven.DateTimeServicePort; import com.optivem.kata.banking.core.usecases.depositfunds.DepositFundsRequest; import com.optivem.kata.banking.core.usecases.depositfunds.DepositFundsUseCase; @@ -22,11 +22,11 @@ public class Facade { private final ViewAccountUseCase viewAccountUseCase; // TODO: VC: Perhaps server-side API facade? And server-side API facade? - public Facade(AccountIdGenerator accountIdGenerator, AccountNumberGenerator accountNumberGenerator, DateTimeServicePort dateTimeService, BankAccountRepository bankAccountRepository) { + public Facade(AccountIdGenerator accountIdGenerator, AccountNumberGenerator accountNumberGenerator, DateTimeServicePort dateTimeService, BankAccountRepository bankAccountRepository, EventPublisher eventPublisher) { var scoreCalculator = DefaultScoreCalculator.create(dateTimeService); this.depositFundsUseCase = new DepositFundsUseCase(bankAccountRepository); - this.openAccountUseCase = new OpenAccountUseCase(accountIdGenerator, accountNumberGenerator, dateTimeService, bankAccountRepository); + this.openAccountUseCase = new OpenAccountUseCase(accountIdGenerator, accountNumberGenerator, dateTimeService, bankAccountRepository, eventPublisher); this.viewAccountUseCase = new ViewAccountUseCase(bankAccountRepository, scoreCalculator); } diff --git a/src/main/java/com/optivem/kata/banking/core/domain/common/events/EventPublisher.java b/src/main/java/com/optivem/kata/banking/core/domain/common/events/EventPublisher.java new file mode 100644 index 00000000..cbbd5649 --- /dev/null +++ b/src/main/java/com/optivem/kata/banking/core/domain/common/events/EventPublisher.java @@ -0,0 +1,8 @@ +package com.optivem.kata.banking.core.domain.common.events; + +/** + * Event Publisher interface will provide eventpublishing functionality to usecases + */ +public interface EventPublisher { + public void publishEvent(UseCaseEvent event); +} diff --git a/src/main/java/com/optivem/kata/banking/core/domain/common/events/EventPublisherToQueue.java b/src/main/java/com/optivem/kata/banking/core/domain/common/events/EventPublisherToQueue.java new file mode 100644 index 00000000..19c8dbac --- /dev/null +++ b/src/main/java/com/optivem/kata/banking/core/domain/common/events/EventPublisherToQueue.java @@ -0,0 +1,21 @@ +package com.optivem.kata.banking.core.domain.common.events; + + +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.stereotype.Component; + + +@Component +public class EventPublisherToQueue implements EventPublisher { + + private ApplicationEventPublisher applicationEventPublisher; + + public EventPublisherToQueue(ApplicationEventPublisher applicationEventPublisher){ + this.applicationEventPublisher = applicationEventPublisher; + } + + @Override + public void publishEvent(UseCaseEvent event) { + applicationEventPublisher.publishEvent(event); + } +} diff --git a/src/main/java/com/optivem/kata/banking/core/domain/common/events/UseCaseEvent.java b/src/main/java/com/optivem/kata/banking/core/domain/common/events/UseCaseEvent.java new file mode 100644 index 00000000..42ef976d --- /dev/null +++ b/src/main/java/com/optivem/kata/banking/core/domain/common/events/UseCaseEvent.java @@ -0,0 +1,24 @@ +package com.optivem.kata.banking.core.domain.common.events; + +import com.optivem.kata.banking.core.domain.accounts.AccountId; +import lombok.Getter; +import org.springframework.context.ApplicationEvent; + +import java.time.LocalDateTime; + +/** + * This class serves as the Event Object that to be puslished by ApplicationEventPublisher + */ +@Getter +public class UseCaseEvent extends ApplicationEvent { + + private AccountId id; + private String eventName; + private LocalDateTime localDateTime; + public UseCaseEvent(AccountId id, String eventName) { + super(id); + this.id = id; + this.eventName = eventName; + this.localDateTime = LocalDateTime.now(); + } +} diff --git a/src/main/java/com/optivem/kata/banking/core/domain/common/events/UseCaseEventHandler.java b/src/main/java/com/optivem/kata/banking/core/domain/common/events/UseCaseEventHandler.java new file mode 100644 index 00000000..62e87d0c --- /dev/null +++ b/src/main/java/com/optivem/kata/banking/core/domain/common/events/UseCaseEventHandler.java @@ -0,0 +1,31 @@ +package com.optivem.kata.banking.core.domain.common.events; + +import com.optivem.kata.banking.infra.real.inmemory.EventQueue; +import org.springframework.context.event.EventListener; +import org.springframework.stereotype.Component; + +/** + * EventListener class that will observe any event of type UseCaseEvent and act accordingly + * It will also insert the event into queue. + */ +@Component +public class UseCaseEventHandler{ + + private final EventQueue queue; + + public UseCaseEventHandler(EventQueue eventEventQueue){ + this.queue = eventEventQueue; + } + + @EventListener + public void handleEvent(UseCaseEvent event){ + queue.addEvent(event); + + // var eventFromQueue = queue.next(); + // System.out.println(eventFromQueue.getId()); + // System.out.println(eventFromQueue.getEventName()); + // System.out.println(eventFromQueue.getLocalDateTime()); + + } + +} diff --git a/src/main/java/com/optivem/kata/banking/core/domain/common/events/UseCaseEvents/AccountOpenedUseCaseEvent.java b/src/main/java/com/optivem/kata/banking/core/domain/common/events/UseCaseEvents/AccountOpenedUseCaseEvent.java new file mode 100644 index 00000000..11dd2de5 --- /dev/null +++ b/src/main/java/com/optivem/kata/banking/core/domain/common/events/UseCaseEvents/AccountOpenedUseCaseEvent.java @@ -0,0 +1,14 @@ +package com.optivem.kata.banking.core.domain.common.events.UseCaseEvents; + + +import com.optivem.kata.banking.core.domain.accounts.AccountId; +import com.optivem.kata.banking.core.domain.common.events.UseCaseEvent; + +public class AccountOpenedUseCaseEvent { + + public static UseCaseEvent generateEventOnSuccess(AccountId id){ + return new UseCaseEvent(id,"AccountOpened"); + } + + +} diff --git a/src/main/java/com/optivem/kata/banking/core/usecases/openaccount/OpenAccountUseCase.java b/src/main/java/com/optivem/kata/banking/core/usecases/openaccount/OpenAccountUseCase.java index 6ae29fb1..3bc26297 100644 --- a/src/main/java/com/optivem/kata/banking/core/usecases/openaccount/OpenAccountUseCase.java +++ b/src/main/java/com/optivem/kata/banking/core/usecases/openaccount/OpenAccountUseCase.java @@ -5,6 +5,8 @@ import com.optivem.kata.banking.core.domain.accounts.AccountIdGenerator; import com.optivem.kata.banking.core.domain.accounts.AccountNumberGenerator; import com.optivem.kata.banking.core.domain.accounts.BankAccountRepository; +import com.optivem.kata.banking.core.domain.common.events.EventPublisher; +import com.optivem.kata.banking.core.domain.common.events.UseCaseEvents.AccountOpenedUseCaseEvent; import com.optivem.kata.banking.core.ports.driven.DateTimeServicePort; import org.springframework.stereotype.Component; @@ -14,12 +16,14 @@ public class OpenAccountUseCase implements Command.Handler { + + private final Queue queue; + + public EventQueue(){ + this.queue = new ArrayDeque<>(); + } + + public void addEvent(T event){ + queue.add(event); + } + + public T next(){ + if(queue.isEmpty()){ + throw new NoSuchElementException(); + } + return queue.remove(); + } + +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index a44f41e4..6eed32c2 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,8 +1,8 @@ spring: datasource: - url: ${POSTGRES_URL} - username: ${POSTGRES_USER} - password: ${POSTGRES_PASSWORD} + url: jdbc:postgresql://localhost:5432/postgres + username: postgres + password: postgres driver-class-name: org.postgresql.Driver jpa: diff --git a/src/test/java/com/optivem/kata/banking/core/common/factories/FacadeFactory.java b/src/test/java/com/optivem/kata/banking/core/common/factories/FacadeFactory.java index 80a16969..0f14e14f 100644 --- a/src/test/java/com/optivem/kata/banking/core/common/factories/FacadeFactory.java +++ b/src/test/java/com/optivem/kata/banking/core/common/factories/FacadeFactory.java @@ -1,10 +1,12 @@ package com.optivem.kata.banking.core.common.factories; import com.optivem.kata.banking.core.Facade; +import com.optivem.kata.banking.core.domain.common.events.EventPublisher; import com.optivem.kata.banking.infra.fake.FakeAccountIdGenerator; import com.optivem.kata.banking.infra.fake.FakeAccountNumberGenerator; import com.optivem.kata.banking.infra.fake.FakeBankAccountRepository; import com.optivem.kata.banking.infra.fake.FakeDateTimeService; +import org.springframework.beans.factory.annotation.Autowired; import java.time.LocalDateTime; @@ -21,6 +23,8 @@ public class FacadeFactory { private static final LocalDateTime DATE_TIME_1 = LocalDateTime.of(2022, 3, 28, 10, 50); private static final LocalDateTime DATE_TIME_2 = LocalDateTime.of(2022, 4, 15, 9, 1); + @Autowired + private EventPublisher eventPublisher; public Facade create() { var accountIdGenerator = new FakeAccountIdGenerator(); @@ -37,6 +41,6 @@ public Facade create() { var bankAccountRepository = new FakeBankAccountRepository(); - return new Facade(accountIdGenerator, accountNumberGenerator, dateTimeService, bankAccountRepository); + return new Facade(accountIdGenerator, accountNumberGenerator, dateTimeService, bankAccountRepository,eventPublisher); } } diff --git a/src/test/java/com/optivem/kata/banking/core/usecases/OpenAccountUseCaseTest.java b/src/test/java/com/optivem/kata/banking/core/usecases/OpenAccountUseCaseTest.java index 4d6dd6ba..8115e182 100644 --- a/src/test/java/com/optivem/kata/banking/core/usecases/OpenAccountUseCaseTest.java +++ b/src/test/java/com/optivem/kata/banking/core/usecases/OpenAccountUseCaseTest.java @@ -1,6 +1,8 @@ package com.optivem.kata.banking.core.usecases; import com.optivem.kata.banking.core.domain.accounts.BankAccountRepository; +import com.optivem.kata.banking.core.domain.common.events.EventPublisher; +import com.optivem.kata.banking.core.domain.common.events.UseCaseEvent; import com.optivem.kata.banking.core.domain.common.exceptions.ValidationMessages; import com.optivem.kata.banking.core.usecases.openaccount.OpenAccountResponse; import com.optivem.kata.banking.core.usecases.openaccount.OpenAccountUseCase; @@ -8,10 +10,13 @@ import com.optivem.kata.banking.infra.fake.FakeAccountIdGenerator; import com.optivem.kata.banking.infra.fake.FakeBankAccountRepository; import com.optivem.kata.banking.infra.fake.FakeDateTimeService; +import com.optivem.kata.banking.infra.real.inmemory.EventQueue; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; import java.time.LocalDate; import java.time.LocalDateTime; @@ -23,7 +28,9 @@ import static com.optivem.kata.banking.core.common.builders.requests.OpenAccountRequestBuilder.openAccountRequest; import static com.optivem.kata.banking.core.common.data.MethodSources.NEGATIVE_INTEGERS; import static com.optivem.kata.banking.core.common.data.MethodSources.NULL_EMPTY_WHITESPACE; +import static org.assertj.core.api.Assertions.assertThat; +@SpringBootTest class OpenAccountUseCaseTest { private FakeAccountIdGenerator accountIdGenerator; private FakeAccountNumberGenerator accountNumberGenerator; @@ -31,6 +38,11 @@ class OpenAccountUseCaseTest { private BankAccountRepository bankAccountRepository; private OpenAccountUseCase useCase; + @Autowired + private EventPublisher eventPublisher; + @Autowired + private EventQueue eventQueue; + private static Stream should_open_account_given_valid_request() { return Stream.of(Arguments.of("John", "Smith", 0, 3456, "GB41OMQP68570038161775", LocalDate.of(2022, 5, 20)), Arguments.of("Mary", "McDonald", 50, 735353, "GB36BMFK75394735916876", LocalDate.of(2021, 6, 15))); @@ -42,7 +54,7 @@ void init() { this.accountNumberGenerator = new FakeAccountNumberGenerator(); this.dateTimeService = new FakeDateTimeService(); this.bankAccountRepository = new FakeBankAccountRepository(); - this.useCase = new OpenAccountUseCase(accountIdGenerator, accountNumberGenerator, dateTimeService, bankAccountRepository); + this.useCase = new OpenAccountUseCase(accountIdGenerator, accountNumberGenerator, dateTimeService, bankAccountRepository, eventPublisher); } @ParameterizedTest @@ -95,4 +107,29 @@ void should_throw_exception_given_negative_balance(int balance) { verifyThat(useCase).withRequest(request).shouldThrowValidationException(ValidationMessages.BALANCE_NEGATIVE); } + + @ParameterizedTest + @MethodSource("should_open_account_given_valid_request") + void should_generate_event_given_valid_request(String firstName, String lastName, int initialBalance, long generatedAccountId, String generatedAccountNumber, LocalDate openingDate) { + givenThat(accountIdGenerator).willGenerate(generatedAccountId); + givenThat(accountNumberGenerator).willGenerate(generatedAccountNumber); + givenThat(dateTimeService).willReturn(LocalDateTime.of(openingDate, LocalTime.MIN)); + + var request = openAccountRequest() + .withFirstName(firstName) + .withLastName(lastName) + .withBalance(initialBalance) + .build(); + + var expectedResponse = new OpenAccountResponse(); + expectedResponse.setAccountNumber(generatedAccountNumber); + + verifyThat(useCase).withRequest(request).shouldReturnResponse(expectedResponse); + + verifyThat(bankAccountRepository).shouldContain(generatedAccountId, generatedAccountNumber, firstName, lastName, openingDate, initialBalance); + + + assertThat(eventQueue.next().getEventName()).isEqualTo("AccountOpened"); + + } } From 020173d71b741f2a7becc275d7f91c787cf8d9ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Valentina=20Cupa=C4=87?= Date: Mon, 27 Jun 2022 17:09:43 +0200 Subject: [PATCH 2/2] #33 Reverted application.yml since the environment variables should remain there --- README.md | 8 -------- src/main/resources/application.yml | 6 +++--- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 008ab162..3357b086 100644 --- a/README.md +++ b/README.md @@ -25,14 +25,6 @@ We implement a Banking system with the following use cases: ## Environment variables -When you open up the file `application.yml` we can see the usage of the following environment variables for the Postgres Database - -``` -url: ${POSTGRES_URL} -username: ${POSTGRES_USER} -password: ${POSTGRES_PASSWORD} -``` - To be able to run the tests (since some of the tests are dependent on the database - the integration tests), we then need to set the environment variables. In IntelliJ, for the `Tests in 'banking-kata.test'` configuration, you can copy this into the `Environment variables` diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 6eed32c2..a44f41e4 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,8 +1,8 @@ spring: datasource: - url: jdbc:postgresql://localhost:5432/postgres - username: postgres - password: postgres + url: ${POSTGRES_URL} + username: ${POSTGRES_USER} + password: ${POSTGRES_PASSWORD} driver-class-name: org.postgresql.Driver jpa: