added example configuration parameter to show how to "cleanly" inject configuration parameters into the application

This commit is contained in:
Tom Hombergs
2019-09-17 21:08:52 +02:00
parent 005713d573
commit e82850920f
8 changed files with 85 additions and 6 deletions

View File

@@ -0,0 +1,18 @@
package io.reflectoring.buckpal.application.service;
import io.reflectoring.buckpal.domain.Money;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* Configuration properties for money transfer use cases.
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class MoneyTransferProperties {
private Money maximumTransferThreshold = Money.of(1_000_000L);
}

View File

@@ -1,9 +1,5 @@
package io.reflectoring.buckpal.application.service; package io.reflectoring.buckpal.application.service;
import javax.transaction.Transactional;
import java.time.LocalDateTime;
import io.reflectoring.buckpal.application.port.in.SendMoneyUseCase; import io.reflectoring.buckpal.application.port.in.SendMoneyUseCase;
import io.reflectoring.buckpal.application.port.out.AccountLock; import io.reflectoring.buckpal.application.port.out.AccountLock;
import io.reflectoring.buckpal.application.port.out.LoadAccountPort; import io.reflectoring.buckpal.application.port.out.LoadAccountPort;
@@ -12,6 +8,9 @@ import io.reflectoring.buckpal.domain.Account;
import io.reflectoring.buckpal.testdata.UseCase; import io.reflectoring.buckpal.testdata.UseCase;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import javax.transaction.Transactional;
import java.time.LocalDateTime;
@RequiredArgsConstructor @RequiredArgsConstructor
@UseCase @UseCase
@Transactional @Transactional
@@ -20,9 +19,15 @@ public class SendMoneyService implements SendMoneyUseCase {
private final LoadAccountPort loadAccountPort; private final LoadAccountPort loadAccountPort;
private final AccountLock accountLock; private final AccountLock accountLock;
private final UpdateAccountStatePort updateAccountStatePort; private final UpdateAccountStatePort updateAccountStatePort;
private final MoneyTransferProperties moneyTransferProperties;
@Override @Override
public boolean sendMoney(SendMoneyCommand command) { public boolean sendMoney(SendMoneyCommand command) {
if(command.getMoney().isGreaterThan(moneyTransferProperties.getMaximumTransferThreshold())){
throw new ThresholdExceededException(moneyTransferProperties.getMaximumTransferThreshold(), command.getMoney());
}
LocalDateTime baselineDate = LocalDateTime.now().minusDays(10); LocalDateTime baselineDate = LocalDateTime.now().minusDays(10);
Account sourceAccount = loadAccountPort.loadAccount( Account sourceAccount = loadAccountPort.loadAccount(

View File

@@ -0,0 +1,11 @@
package io.reflectoring.buckpal.application.service;
import io.reflectoring.buckpal.domain.Money;
public class ThresholdExceededException extends RuntimeException {
public ThresholdExceededException(Money threshold, Money actual) {
super(String.format("Maximum threshold for transferring money exceeded: tried to transfer %s but threshold is %s!", actual, threshold));
}
}

View File

@@ -29,6 +29,10 @@ public class Money {
return this.amount.compareTo(money.amount) >= 0; return this.amount.compareTo(money.amount) >= 0;
} }
public boolean isGreaterThan(Money money){
return this.amount.compareTo(money.amount) >= 1;
}
public static Money of(long value) { public static Money of(long value) {
return new Money(BigInteger.valueOf(value)); return new Money(BigInteger.valueOf(value));
} }

View File

@@ -29,7 +29,7 @@ class SendMoneyServiceTest {
Mockito.mock(UpdateAccountStatePort.class); Mockito.mock(UpdateAccountStatePort.class);
private final SendMoneyService sendMoneyService = private final SendMoneyService sendMoneyService =
new SendMoneyService(loadAccountPort, accountLock, updateAccountStatePort); new SendMoneyService(loadAccountPort, accountLock, updateAccountStatePort, moneyTransferProperties());
@Test @Test
void givenWithdrawalFails_thenOnlySourceAccountIsLockedAndReleased() { void givenWithdrawalFails_thenOnlySourceAccountIsLockedAndReleased() {
@@ -138,4 +138,8 @@ class SendMoneyServiceTest {
return account; return account;
} }
} private MoneyTransferProperties moneyTransferProperties(){
return new MoneyTransferProperties(Money.of(Long.MAX_VALUE));
}
}

View File

@@ -4,6 +4,9 @@ plugins {
dependencies { dependencies {
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
implementation project(':common') implementation project(':common')
implementation project(':buckpal-application') implementation project(':buckpal-application')
implementation project(':adapters:buckpal-persistence') implementation project(':adapters:buckpal-persistence')

View File

@@ -0,0 +1,22 @@
package io.reflectoring.buckpal;
import io.reflectoring.buckpal.application.service.MoneyTransferProperties;
import io.reflectoring.buckpal.domain.Money;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@EnableConfigurationProperties(BuckPalConfigurationProperties.class)
public class BuckPalConfiguration {
/**
* Adds a use-case-specific {@link MoneyTransferProperties} object to the application context. The properties
* are read from the Spring-Boot-specific {@link BuckPalConfigurationProperties} object.
*/
@Bean
public MoneyTransferProperties moneyTransferProperties(BuckPalConfigurationProperties buckPalConfigurationProperties){
return new MoneyTransferProperties(Money.of(buckPalConfigurationProperties.getTransferThreshold()));
}
}

View File

@@ -0,0 +1,12 @@
package io.reflectoring.buckpal;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
@Data
@ConfigurationProperties(prefix = "buckpal")
public class BuckPalConfigurationProperties {
private long transferThreshold = Long.MAX_VALUE;
}