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.Activity;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
@RequiredArgsConstructor
@PersistenceAdapter
@Component
class AccountPersistenceAdapter implements
LoadAccountPort,
UpdateAccountStatePort {
@@ -24,7 +25,9 @@ class AccountPersistenceAdapter implements
private final AccountMapper accountMapper;
@Override
public Account loadAccount(AccountId accountId, LocalDateTime baselineDate) {
public Account loadAccount(
AccountId accountId,
LocalDateTime baselineDate) {
AccountJpaEntity account =
accountRepository.findById(accountId.getValue())

View File

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

View File

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

View File

@@ -1,13 +1,13 @@
package io.reflectoring.buckpal.application.port.in;
import javax.validation.constraints.NotNull;
import io.reflectoring.buckpal.domain.Account;
import io.reflectoring.buckpal.domain.Account.AccountId;
import io.reflectoring.buckpal.domain.Money;
import io.reflectoring.buckpal.testdata.SelfValidating;
import lombok.EqualsAndHashCode;
import lombok.Value;
import javax.validation.constraints.NotNull;
public interface SendMoneyUseCase {
boolean sendMoney(SendMoneyCommand command);
@@ -17,17 +17,17 @@ public interface SendMoneyUseCase {
class SendMoneyCommand extends SelfValidating<SendMoneyCommand> {
@NotNull
private final Account.AccountId sourceAccountId;
private final AccountId sourceAccountId;
@NotNull
private final Account.AccountId targetAccountId;
private final AccountId targetAccountId;
@NotNull
private final Money money;
public SendMoneyCommand(
Account.AccountId sourceAccountId,
Account.AccountId targetAccountId,
AccountId sourceAccountId,
AccountId targetAccountId,
Money money) {
this.sourceAccountId = sourceAccountId;
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.UpdateAccountStatePort;
import io.reflectoring.buckpal.domain.Account;
import io.reflectoring.buckpal.domain.Account.AccountId;
import io.reflectoring.buckpal.testdata.UseCase;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import javax.transaction.Transactional;
import java.time.LocalDateTime;
@RequiredArgsConstructor
@UseCase
@Component
@Transactional
public class SendMoneyService implements SendMoneyUseCase {
@@ -24,9 +26,7 @@ public class SendMoneyService implements SendMoneyUseCase {
@Override
public boolean sendMoney(SendMoneyCommand command) {
if(command.getMoney().isGreaterThan(moneyTransferProperties.getMaximumTransferThreshold())){
throw new ThresholdExceededException(moneyTransferProperties.getMaximumTransferThreshold(), command.getMoney());
}
checkThreshold(command);
LocalDateTime baselineDate = LocalDateTime.now().minusDays(10);
@@ -38,25 +38,36 @@ public class SendMoneyService implements SendMoneyUseCase {
command.getTargetAccountId(),
baselineDate);
accountLock.lockAccount(sourceAccount.getId());
if (!sourceAccount.withdraw(command.getMoney(), targetAccount.getId())) {
accountLock.releaseAccount(sourceAccount.getId());
AccountId sourceAccountId = sourceAccount.getId()
.orElseThrow(() -> new IllegalStateException("expected source account ID not to be empty"));
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;
}
accountLock.lockAccount(targetAccount.getId());
if (!targetAccount.deposit(command.getMoney(), sourceAccount.getId())) {
accountLock.releaseAccount(sourceAccount.getId());
accountLock.releaseAccount(targetAccount.getId());
accountLock.lockAccount(targetAccountId);
if (!targetAccount.deposit(command.getMoney(), sourceAccountId)) {
accountLock.releaseAccount(sourceAccountId);
accountLock.releaseAccount(targetAccountId);
return false;
}
updateAccountStatePort.updateActivities(sourceAccount);
updateAccountStatePort.updateActivities(targetAccount);
accountLock.releaseAccount(sourceAccount.getId());
accountLock.releaseAccount(targetAccount.getId());
accountLock.releaseAccount(sourceAccountId);
accountLock.releaseAccount(targetAccountId);
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;
import java.time.LocalDateTime;
import java.util.Optional;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
@@ -19,36 +20,43 @@ public class Account {
/**
* The unique ID of the account.
*/
@Getter
private AccountId id;
@Getter private final AccountId id;
/**
* The baseline balance of the account. This was the balance of the account before the first
* activity in the activityWindow.
*/
private Money baselineBalance;
@Getter private final Money baselineBalance;
/**
* The window of latest activities on this account.
*/
@Getter
private ActivityWindow activityWindow;
@Getter private final ActivityWindow activityWindow;
/**
* Creates an {@link Account} entity without an ID. Use to create a new entity that is not yet
* persisted.
*/
public static Account withoutId(Money baselineBalance, ActivityWindow activityWindow) {
public static Account withoutId(
Money baselineBalance,
ActivityWindow activityWindow) {
return new Account(null, baselineBalance, activityWindow);
}
/**
* 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);
}
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.
*/

View File

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

View File

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