simplifiex code examples

This commit is contained in:
Tom Hombergs
2019-09-30 11:00:57 +02:00
parent e82850920f
commit 8bde95472d
8 changed files with 57 additions and 34 deletions

View File

@@ -12,9 +12,10 @@ import io.reflectoring.buckpal.domain.Account;
import io.reflectoring.buckpal.domain.Account.AccountId; import io.reflectoring.buckpal.domain.Account.AccountId;
import io.reflectoring.buckpal.domain.Activity; import io.reflectoring.buckpal.domain.Activity;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
@RequiredArgsConstructor @RequiredArgsConstructor
@PersistenceAdapter @Component
class AccountPersistenceAdapter implements class AccountPersistenceAdapter implements
LoadAccountPort, LoadAccountPort,
UpdateAccountStatePort { UpdateAccountStatePort {
@@ -24,7 +25,9 @@ class AccountPersistenceAdapter implements
private final AccountMapper accountMapper; private final AccountMapper accountMapper;
@Override @Override
public Account loadAccount(AccountId accountId, LocalDateTime baselineDate) { public Account loadAccount(
AccountId accountId,
LocalDateTime baselineDate) {
AccountJpaEntity account = AccountJpaEntity account =
accountRepository.findById(accountId.getValue()) accountRepository.findById(accountId.getValue())

View File

@@ -15,7 +15,7 @@ public class SendMoneyController {
private final SendMoneyUseCase sendMoneyUseCase; private final SendMoneyUseCase sendMoneyUseCase;
@PostMapping(path = "/accounts/sendMoney/{sourceAccountId}/{targetAccountId}/{amount}") @PostMapping(path = "/accounts/send/{sourceAccountId}/{targetAccountId}/{amount}")
void sendMoney( void sendMoney(
@PathVariable("sourceAccountId") Long sourceAccountId, @PathVariable("sourceAccountId") Long sourceAccountId,
@PathVariable("targetAccountId") Long targetAccountId, @PathVariable("targetAccountId") Long targetAccountId,

View File

@@ -25,7 +25,7 @@ class SendMoneyControllerTest {
@Test @Test
void testSendMoney() throws Exception { void testSendMoney() throws Exception {
mockMvc.perform(post("/accounts/sendMoney/{sourceAccountId}/{targetAccountId}/{amount}", mockMvc.perform(post("/accounts/send/{sourceAccountId}/{targetAccountId}/{amount}",
41L, 42L, 500) 41L, 42L, 500)
.header("Content-Type", "application/json")) .header("Content-Type", "application/json"))
.andExpect(status().isOk()); .andExpect(status().isOk());
@@ -37,4 +37,4 @@ class SendMoneyControllerTest {
Money.of(500L)))); Money.of(500L))));
} }
} }

View File

@@ -1,13 +1,13 @@
package io.reflectoring.buckpal.application.port.in; package io.reflectoring.buckpal.application.port.in;
import javax.validation.constraints.NotNull; import io.reflectoring.buckpal.domain.Account.AccountId;
import io.reflectoring.buckpal.domain.Account;
import io.reflectoring.buckpal.domain.Money; import io.reflectoring.buckpal.domain.Money;
import io.reflectoring.buckpal.testdata.SelfValidating; import io.reflectoring.buckpal.testdata.SelfValidating;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.Value; import lombok.Value;
import javax.validation.constraints.NotNull;
public interface SendMoneyUseCase { public interface SendMoneyUseCase {
boolean sendMoney(SendMoneyCommand command); boolean sendMoney(SendMoneyCommand command);
@@ -17,17 +17,17 @@ public interface SendMoneyUseCase {
class SendMoneyCommand extends SelfValidating<SendMoneyCommand> { class SendMoneyCommand extends SelfValidating<SendMoneyCommand> {
@NotNull @NotNull
private final Account.AccountId sourceAccountId; private final AccountId sourceAccountId;
@NotNull @NotNull
private final Account.AccountId targetAccountId; private final AccountId targetAccountId;
@NotNull @NotNull
private final Money money; private final Money money;
public SendMoneyCommand( public SendMoneyCommand(
Account.AccountId sourceAccountId, AccountId sourceAccountId,
Account.AccountId targetAccountId, AccountId targetAccountId,
Money money) { Money money) {
this.sourceAccountId = sourceAccountId; this.sourceAccountId = sourceAccountId;
this.targetAccountId = targetAccountId; this.targetAccountId = targetAccountId;

View File

@@ -5,14 +5,16 @@ import io.reflectoring.buckpal.application.port.out.AccountLock;
import io.reflectoring.buckpal.application.port.out.LoadAccountPort; import io.reflectoring.buckpal.application.port.out.LoadAccountPort;
import io.reflectoring.buckpal.application.port.out.UpdateAccountStatePort; import io.reflectoring.buckpal.application.port.out.UpdateAccountStatePort;
import io.reflectoring.buckpal.domain.Account; import io.reflectoring.buckpal.domain.Account;
import io.reflectoring.buckpal.domain.Account.AccountId;
import io.reflectoring.buckpal.testdata.UseCase; import io.reflectoring.buckpal.testdata.UseCase;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import javax.transaction.Transactional; import javax.transaction.Transactional;
import java.time.LocalDateTime; import java.time.LocalDateTime;
@RequiredArgsConstructor @RequiredArgsConstructor
@UseCase @Component
@Transactional @Transactional
public class SendMoneyService implements SendMoneyUseCase { public class SendMoneyService implements SendMoneyUseCase {
@@ -24,9 +26,7 @@ public class SendMoneyService implements SendMoneyUseCase {
@Override @Override
public boolean sendMoney(SendMoneyCommand command) { public boolean sendMoney(SendMoneyCommand command) {
if(command.getMoney().isGreaterThan(moneyTransferProperties.getMaximumTransferThreshold())){ checkThreshold(command);
throw new ThresholdExceededException(moneyTransferProperties.getMaximumTransferThreshold(), command.getMoney());
}
LocalDateTime baselineDate = LocalDateTime.now().minusDays(10); LocalDateTime baselineDate = LocalDateTime.now().minusDays(10);
@@ -38,25 +38,36 @@ public class SendMoneyService implements SendMoneyUseCase {
command.getTargetAccountId(), command.getTargetAccountId(),
baselineDate); baselineDate);
accountLock.lockAccount(sourceAccount.getId()); AccountId sourceAccountId = sourceAccount.getId()
if (!sourceAccount.withdraw(command.getMoney(), targetAccount.getId())) { .orElseThrow(() -> new IllegalStateException("expected source account ID not to be empty"));
accountLock.releaseAccount(sourceAccount.getId()); AccountId targetAccountId = targetAccount.getId()
.orElseThrow(() -> new IllegalStateException("expected target account ID not to be empty"));
accountLock.lockAccount(sourceAccountId);
if (!sourceAccount.withdraw(command.getMoney(), targetAccountId)) {
accountLock.releaseAccount(sourceAccountId);
return false; return false;
} }
accountLock.lockAccount(targetAccount.getId()); accountLock.lockAccount(targetAccountId);
if (!targetAccount.deposit(command.getMoney(), sourceAccount.getId())) { if (!targetAccount.deposit(command.getMoney(), sourceAccountId)) {
accountLock.releaseAccount(sourceAccount.getId()); accountLock.releaseAccount(sourceAccountId);
accountLock.releaseAccount(targetAccount.getId()); accountLock.releaseAccount(targetAccountId);
return false; return false;
} }
updateAccountStatePort.updateActivities(sourceAccount); updateAccountStatePort.updateActivities(sourceAccount);
updateAccountStatePort.updateActivities(targetAccount); updateAccountStatePort.updateActivities(targetAccount);
accountLock.releaseAccount(sourceAccount.getId()); accountLock.releaseAccount(sourceAccountId);
accountLock.releaseAccount(targetAccount.getId()); accountLock.releaseAccount(targetAccountId);
return true; return true;
} }
private void checkThreshold(SendMoneyCommand command) {
if(command.getMoney().isGreaterThan(moneyTransferProperties.getMaximumTransferThreshold())){
throw new ThresholdExceededException(moneyTransferProperties.getMaximumTransferThreshold(), command.getMoney());
}
}
} }

View File

@@ -1,6 +1,7 @@
package io.reflectoring.buckpal.domain; package io.reflectoring.buckpal.domain;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.Optional;
import lombok.AccessLevel; import lombok.AccessLevel;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
@@ -19,36 +20,43 @@ public class Account {
/** /**
* The unique ID of the account. * The unique ID of the account.
*/ */
@Getter @Getter private final AccountId id;
private AccountId id;
/** /**
* The baseline balance of the account. This was the balance of the account before the first * The baseline balance of the account. This was the balance of the account before the first
* activity in the activityWindow. * activity in the activityWindow.
*/ */
private Money baselineBalance; @Getter private final Money baselineBalance;
/** /**
* The window of latest activities on this account. * The window of latest activities on this account.
*/ */
@Getter @Getter private final ActivityWindow activityWindow;
private ActivityWindow activityWindow;
/** /**
* Creates an {@link Account} entity without an ID. Use to create a new entity that is not yet * Creates an {@link Account} entity without an ID. Use to create a new entity that is not yet
* persisted. * persisted.
*/ */
public static Account withoutId(Money baselineBalance, ActivityWindow activityWindow) { public static Account withoutId(
Money baselineBalance,
ActivityWindow activityWindow) {
return new Account(null, baselineBalance, activityWindow); return new Account(null, baselineBalance, activityWindow);
} }
/** /**
* Creates an {@link Account} entity with an ID. Use to reconstitute a persisted entity. * Creates an {@link Account} entity with an ID. Use to reconstitute a persisted entity.
*/ */
public static Account withId(AccountId accountId, Money baselineBalance, ActivityWindow activityWindow) { public static Account withId(
AccountId accountId,
Money baselineBalance,
ActivityWindow activityWindow) {
return new Account(accountId, baselineBalance, activityWindow); return new Account(accountId, baselineBalance, activityWindow);
} }
public Optional<AccountId> getId(){
return Optional.ofNullable(this.id);
}
/** /**
* Calculates the total balance of the account by adding the activity values to the baseline balance. * Calculates the total balance of the account by adding the activity values to the baseline balance.
*/ */

View File

@@ -3,6 +3,7 @@ package io.reflectoring.buckpal.domain;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.List; import java.util.List;
@@ -66,7 +67,7 @@ public class ActivityWindow {
} }
public List<Activity> getActivities() { public List<Activity> getActivities() {
return new ArrayList<>(this.activities); return Collections.unmodifiableList(this.activities);
} }
public void addActivity(Activity activity) { public void addActivity(Activity activity) {

View File

@@ -75,7 +75,7 @@ class SendMoneySystemTest {
HttpEntity<Void> request = new HttpEntity<>(null, headers); HttpEntity<Void> request = new HttpEntity<>(null, headers);
return restTemplate.exchange( return restTemplate.exchange(
"/accounts/sendMoney/{sourceAccountId}/{targetAccountId}/{amount}", "/accounts/send/{sourceAccountId}/{targetAccountId}/{amount}",
HttpMethod.POST, HttpMethod.POST,
request, request,
Object.class, Object.class,