Account aggregate implementation
This commit is contained in:
@@ -2,9 +2,76 @@ package com.mz.reactor.ddd.reactorddd.account.domain;
|
||||
|
||||
import com.mz.reactor.ddd.common.api.valueobject.Id;
|
||||
import com.mz.reactor.ddd.common.api.valueobject.Money;
|
||||
import com.mz.reactor.ddd.reactorddd.account.domain.command.CreateAccount;
|
||||
import com.mz.reactor.ddd.reactorddd.account.domain.command.DepositMoney;
|
||||
import com.mz.reactor.ddd.reactorddd.account.domain.command.WithdrawMoney;
|
||||
import com.mz.reactor.ddd.reactorddd.account.domain.event.AccountCreated;
|
||||
import com.mz.reactor.ddd.reactorddd.account.domain.event.MoneyDeposited;
|
||||
import com.mz.reactor.ddd.reactorddd.account.domain.event.MoneyWithdrawn;
|
||||
import org.apache.commons.lang3.text.translate.AggregateTranslator;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class AccountAggregate {
|
||||
private Id aggregateId;
|
||||
|
||||
private Money amount;
|
||||
|
||||
private List<Id> openedTransactions = new ArrayList<>();
|
||||
|
||||
public AccountAggregate(String aggregateId) {
|
||||
this.aggregateId = new Id(aggregateId);
|
||||
this.amount = new Money(BigDecimal.ZERO);
|
||||
}
|
||||
|
||||
public MoneyWithdrawn validateWithdrawMoney(WithdrawMoney command) {
|
||||
return Money.validateValue
|
||||
.andThen(v -> Money.validateWithdraw.apply(this.amount.getAmount(), command.amount()))
|
||||
.andThen(v -> MoneyWithdrawn.from(command))
|
||||
.apply(command.amount());
|
||||
}
|
||||
|
||||
public MoneyDeposited validateDepositMoney(DepositMoney depositMoney) {
|
||||
return Money.validateDepositMoney
|
||||
.andThen(v ->
|
||||
MoneyDeposited.builder()
|
||||
.correlationId(depositMoney.correlationId())
|
||||
.aggregateId(aggregateId.getValue())
|
||||
.amount(v)
|
||||
.build())
|
||||
.apply(depositMoney.amount());
|
||||
}
|
||||
|
||||
public AccountCreated validateCreateAccount(CreateAccount command) {
|
||||
return Money.validateValue
|
||||
.andThen(v -> AccountCreated.from(command))
|
||||
.apply(command.balance());
|
||||
}
|
||||
|
||||
public AccountAggregate applyMoneyDeposited(MoneyDeposited moneyDeposited) {
|
||||
this.amount = this.amount.depositMoney(moneyDeposited.amount());
|
||||
return this;
|
||||
}
|
||||
|
||||
public AccountAggregate applyAccountCreated(AccountCreated event) {
|
||||
this.amount = this.amount.depositMoney(event.balance());
|
||||
return this;
|
||||
}
|
||||
|
||||
public AccountAggregate applyMoneyWithdrawn(MoneyWithdrawn event) {
|
||||
this.amount = this.amount.withdrawMoney(event.amount());
|
||||
return this;
|
||||
}
|
||||
|
||||
public AccountState getState() {
|
||||
return AccountState.builder()
|
||||
.aggregateId(this.aggregateId.getValue())
|
||||
.addAllOpenedTransactions(this.openedTransactions.stream().map(Id::getValue).collect(Collectors.toList()))
|
||||
.amount(this.amount.getAmount())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.mz.reactor.ddd.reactorddd.account.domain;
|
||||
import org.immutables.value.Value;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
@Value.Immutable
|
||||
public interface AccountState {
|
||||
@@ -10,6 +11,8 @@ public interface AccountState {
|
||||
|
||||
BigDecimal amount();
|
||||
|
||||
List<String> openedTransactions();
|
||||
|
||||
static ImmutableAccountState.Builder builder() {
|
||||
return ImmutableAccountState.builder();
|
||||
}
|
||||
|
||||
@@ -2,7 +2,11 @@ package com.mz.reactor.ddd.reactorddd.account.domain.command;
|
||||
|
||||
import com.mz.reactor.ddd.common.api.command.CommandHandler;
|
||||
import com.mz.reactor.ddd.common.api.command.CommandResult;
|
||||
import com.mz.reactor.ddd.common.api.command.ImmutableCommandResult;
|
||||
import com.mz.reactor.ddd.reactorddd.account.domain.AccountAggregate;
|
||||
import com.mz.reactor.ddd.reactorddd.account.domain.event.DepositMoneyFailed;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public class AccountCommandHandler implements CommandHandler<AccountAggregate, AccountCommand> {
|
||||
|
||||
@@ -15,7 +19,25 @@ public class AccountCommandHandler implements CommandHandler<AccountAggregate, A
|
||||
}
|
||||
|
||||
private CommandResult doDepositMoney(AccountAggregate aggregate, DepositMoney command) {
|
||||
return null;
|
||||
try {
|
||||
return Optional.of(command)
|
||||
.map(aggregate::validateDepositMoney)
|
||||
.map(e -> CommandResult.builder()
|
||||
.statusCode(CommandResult.StatusCode.OK)
|
||||
.addEvents(e)
|
||||
.build())
|
||||
.orElseGet(() -> (ImmutableCommandResult) CommandResult.notModified());
|
||||
} catch (RuntimeException e) {
|
||||
return CommandResult.builder()
|
||||
.addEvents(DepositMoneyFailed.builder()
|
||||
.aggregateId(command.aggregateId())
|
||||
.amount(command.amount())
|
||||
.correlationId(command.correlationId())
|
||||
.build())
|
||||
.statusCode(CommandResult.StatusCode.FAILED)
|
||||
.error(e)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.mz.reactor.ddd.reactorddd.account.domain.event;
|
||||
|
||||
import com.mz.reactor.ddd.reactorddd.account.domain.command.CreateAccount;
|
||||
import org.immutables.value.Value;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
@@ -12,4 +13,12 @@ public interface AccountCreated extends AccountEvent {
|
||||
static ImmutableAccountCreated.Builder builder() {
|
||||
return ImmutableAccountCreated.builder();
|
||||
}
|
||||
|
||||
static AccountCreated from(CreateAccount createAccount) {
|
||||
return builder()
|
||||
.aggregateId(createAccount.aggregateId())
|
||||
.balance(createAccount.balance())
|
||||
.correlationId(createAccount.correlationId())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,14 +3,10 @@ package com.mz.reactor.ddd.reactorddd.account.domain.event;
|
||||
import com.mz.reactor.ddd.common.api.event.EventApplier;
|
||||
import com.mz.reactor.ddd.reactorddd.account.domain.AccountAggregate;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
public class AccountEventApplier implements EventApplier<AccountAggregate, AccountEvent> {
|
||||
|
||||
private AccountAggregate applyAccountCreated(AccountAggregate aggregate, AccountCreated event) {
|
||||
return aggregate;
|
||||
return aggregate.applyAccountCreated(event);
|
||||
}
|
||||
|
||||
private AccountAggregate applyMoneyWithdrawn(AccountAggregate aggregate, MoneyWithdrawn event) {
|
||||
@@ -18,7 +14,7 @@ public class AccountEventApplier implements EventApplier<AccountAggregate, Accou
|
||||
}
|
||||
|
||||
private AccountAggregate applyMoneyDeposited(AccountAggregate aggregate, MoneyDeposited event) {
|
||||
return aggregate;
|
||||
return aggregate.applyMoneyDeposited(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.mz.reactor.ddd.reactorddd.account.domain.event;
|
||||
|
||||
import com.mz.reactor.ddd.reactorddd.account.domain.command.WithdrawMoney;
|
||||
import org.immutables.value.Value;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
@@ -11,4 +12,12 @@ public interface MoneyWithdrawn extends AccountEvent {
|
||||
static ImmutableMoneyWithdrawn.Builder builder() {
|
||||
return ImmutableMoneyWithdrawn.builder();
|
||||
}
|
||||
|
||||
static MoneyWithdrawn from(WithdrawMoney command) {
|
||||
return builder()
|
||||
.aggregateId(command.aggregateId())
|
||||
.correlationId(command.correlationId())
|
||||
.amount(command.amount())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,158 @@
|
||||
package com.mz.reactor.ddd.reactorddd.account.domain;
|
||||
|
||||
import com.mz.reactor.ddd.reactorddd.account.domain.command.CreateAccount;
|
||||
import com.mz.reactor.ddd.reactorddd.account.domain.command.DepositMoney;
|
||||
import com.mz.reactor.ddd.reactorddd.account.domain.command.WithdrawMoney;
|
||||
import com.mz.reactor.ddd.reactorddd.account.domain.event.AccountCreated;
|
||||
import com.mz.reactor.ddd.reactorddd.account.domain.event.MoneyDeposited;
|
||||
import com.mz.reactor.ddd.reactorddd.account.domain.event.MoneyWithdrawn;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class AccountAggregateTest {
|
||||
|
||||
@Test
|
||||
void validateCreateAccount_Ok() {
|
||||
//given
|
||||
var aggregateId = UUID.randomUUID().toString();
|
||||
var correlationId = UUID.randomUUID().toString();
|
||||
var aggregate = new AccountAggregate(aggregateId);
|
||||
var command = CreateAccount.builder()
|
||||
.aggregateId(aggregateId)
|
||||
.correlationId(correlationId)
|
||||
.balance(BigDecimal.TEN)
|
||||
.build();
|
||||
|
||||
//when
|
||||
var event = aggregate.validateCreateAccount(command);
|
||||
var state = aggregate.applyAccountCreated(event).getState();
|
||||
|
||||
//then
|
||||
Assertions.assertNotNull(event);
|
||||
Assertions.assertEquals(event.correlationId().get(), command.correlationId().get());
|
||||
Assertions.assertEquals(event.aggregateId(), command.aggregateId());
|
||||
Assertions.assertEquals(event.balance().compareTo(BigDecimal.TEN), 0);
|
||||
Assertions.assertEquals(state.amount(), BigDecimal.TEN);
|
||||
}
|
||||
|
||||
@Test
|
||||
void validateDepositMoney_MoneyDeposited() {
|
||||
//given
|
||||
var aggregateId = UUID.randomUUID().toString();
|
||||
var correlationId = UUID.randomUUID().toString();
|
||||
var aggregate = new AccountAggregate(aggregateId);
|
||||
var depositMoney = DepositMoney.builder()
|
||||
.aggregateId(aggregateId)
|
||||
.correlationId(correlationId)
|
||||
.amount(BigDecimal.TEN)
|
||||
.build();
|
||||
|
||||
//when
|
||||
var event = aggregate.validateDepositMoney(depositMoney);
|
||||
|
||||
//then
|
||||
Assertions.assertNotNull(event);
|
||||
Assertions.assertEquals(event.correlationId().get(), depositMoney.correlationId().get());
|
||||
Assertions.assertEquals(event.aggregateId(), depositMoney.aggregateId());
|
||||
Assertions.assertEquals(event.amount().compareTo(BigDecimal.TEN), 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void validateDepositMoney_Failed() {
|
||||
//given
|
||||
var aggregateId = UUID.randomUUID().toString();
|
||||
var correlationId = UUID.randomUUID().toString();
|
||||
var aggregate = new AccountAggregate(aggregateId);
|
||||
var depositMoney = DepositMoney.builder()
|
||||
.aggregateId(aggregateId)
|
||||
.correlationId(correlationId)
|
||||
.amount(BigDecimal.ZERO)
|
||||
.build();
|
||||
|
||||
//then
|
||||
Assertions.assertThrows(RuntimeException.class, () -> aggregate.validateDepositMoney(depositMoney));
|
||||
}
|
||||
|
||||
@Test
|
||||
void applyMoneyDeposited() {
|
||||
//given
|
||||
var aggregateId = UUID.randomUUID().toString();
|
||||
var correlationId = UUID.randomUUID().toString();
|
||||
var aggregate = new AccountAggregate(aggregateId);
|
||||
var event = MoneyDeposited.builder()
|
||||
.aggregateId(aggregateId)
|
||||
.correlationId(correlationId)
|
||||
.amount(BigDecimal.TEN)
|
||||
.build();
|
||||
Assertions.assertEquals(aggregate.getState().amount(), BigDecimal.ZERO);
|
||||
|
||||
//when
|
||||
var state = aggregate.applyMoneyDeposited(event).getState();
|
||||
|
||||
//then
|
||||
Assertions.assertEquals(state.aggregateId(), aggregateId);
|
||||
Assertions.assertEquals(state.amount(), event.amount());
|
||||
}
|
||||
|
||||
@Test
|
||||
void validateWithdrawMoney_MoneyWithdrawn() {
|
||||
//given
|
||||
var aggregateId = UUID.randomUUID().toString();
|
||||
var correlationId = UUID.randomUUID().toString();
|
||||
var aggregate = new AccountAggregate(aggregateId);
|
||||
var accountCreated = AccountCreated.builder()
|
||||
.aggregateId(aggregateId)
|
||||
.correlationId(correlationId)
|
||||
.balance(BigDecimal.TEN)
|
||||
.build();
|
||||
|
||||
aggregate.applyAccountCreated(accountCreated).getState();
|
||||
|
||||
var withdrawMoney = WithdrawMoney.builder()
|
||||
.aggregateId(aggregateId)
|
||||
.correlationId(correlationId)
|
||||
.amount(BigDecimal.valueOf(5))
|
||||
.build();
|
||||
|
||||
//when
|
||||
var event = aggregate.validateWithdrawMoney(withdrawMoney);
|
||||
var state = aggregate.applyMoneyWithdrawn(event).getState();
|
||||
|
||||
//then
|
||||
Assertions.assertNotNull(event);
|
||||
Assertions.assertEquals(event.correlationId().get(), correlationId);
|
||||
Assertions.assertEquals(event.aggregateId(), aggregateId);
|
||||
Assertions.assertTrue(event instanceof MoneyWithdrawn);
|
||||
Assertions.assertEquals(state.amount(), BigDecimal.valueOf(5));
|
||||
}
|
||||
|
||||
@Test
|
||||
void validateWithdrawMoney_Failed() {
|
||||
//given
|
||||
var aggregateId = UUID.randomUUID().toString();
|
||||
var correlationId = UUID.randomUUID().toString();
|
||||
var aggregate = new AccountAggregate(aggregateId);
|
||||
var accountCreated = AccountCreated.builder()
|
||||
.aggregateId(aggregateId)
|
||||
.correlationId(correlationId)
|
||||
.balance(BigDecimal.TEN)
|
||||
.build();
|
||||
|
||||
aggregate.applyAccountCreated(accountCreated);
|
||||
|
||||
//when
|
||||
var withdrawMoney = WithdrawMoney.builder()
|
||||
.aggregateId(aggregateId)
|
||||
.correlationId(correlationId)
|
||||
.amount(BigDecimal.valueOf(15))
|
||||
.build();
|
||||
|
||||
//then
|
||||
Assertions.assertThrows(RuntimeException.class, () -> aggregate.validateWithdrawMoney(withdrawMoney));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package com.mz.reactor.ddd.reactorddd.account.domain.command;
|
||||
|
||||
import com.mz.reactor.ddd.common.api.command.CommandResult;
|
||||
import com.mz.reactor.ddd.reactorddd.account.domain.AccountAggregate;
|
||||
import com.mz.reactor.ddd.reactorddd.account.domain.event.DepositMoneyFailed;
|
||||
import com.mz.reactor.ddd.reactorddd.account.domain.event.MoneyDeposited;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class AccountCommandHandlerTest {
|
||||
|
||||
AccountCommandHandler accountCommandHandler = new AccountCommandHandler();
|
||||
|
||||
@Test
|
||||
void executeDepositMoneyOK() {
|
||||
//given
|
||||
var aggregateId = UUID.randomUUID().toString();
|
||||
var correlationId = UUID.randomUUID().toString();
|
||||
var accountAggregate = new AccountAggregate(aggregateId);
|
||||
var command = DepositMoney.builder()
|
||||
.aggregateId(aggregateId)
|
||||
.correlationId(correlationId)
|
||||
.amount(BigDecimal.TEN)
|
||||
.build();
|
||||
|
||||
//when
|
||||
var commandResult = accountCommandHandler.execute(accountAggregate, command);
|
||||
|
||||
//then
|
||||
Assertions.assertEquals(commandResult.statusCode(), CommandResult.StatusCode.OK);
|
||||
Assertions.assertTrue(commandResult.events().stream().allMatch(e -> e instanceof MoneyDeposited));
|
||||
}
|
||||
|
||||
@Test
|
||||
void executeDepositMoneyBadCommand() {
|
||||
//given
|
||||
var accountAggregate = new AccountAggregate(UUID.randomUUID().toString());
|
||||
|
||||
//when
|
||||
var commandResult = accountCommandHandler.execute(accountAggregate, null);
|
||||
|
||||
//then
|
||||
Assertions.assertEquals(commandResult.statusCode(), CommandResult.StatusCode.BAD_COMMAND);
|
||||
}
|
||||
|
||||
@Test
|
||||
void executeDepositMoneyFailed() {
|
||||
//given
|
||||
var aggregateId = UUID.randomUUID().toString();
|
||||
var correlationId = UUID.randomUUID().toString();
|
||||
var accountAggregate = new AccountAggregate(aggregateId);
|
||||
var command = DepositMoney.builder()
|
||||
.aggregateId(aggregateId)
|
||||
.correlationId(correlationId)
|
||||
.amount(BigDecimal.ZERO)
|
||||
.build();
|
||||
|
||||
//when
|
||||
var commandResult = accountCommandHandler.execute(accountAggregate, command);
|
||||
|
||||
//then
|
||||
Assertions.assertEquals(commandResult.statusCode(), CommandResult.StatusCode.FAILED);
|
||||
Assertions.assertTrue(commandResult.events().stream().allMatch(e -> e instanceof DepositMoneyFailed));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.mz.reactor.ddd.reactorddd.account.domain.event;
|
||||
|
||||
import com.mz.reactor.ddd.reactorddd.account.domain.AccountAggregate;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class AccountEventApplierTest {
|
||||
|
||||
AccountEventApplier applier = new AccountEventApplier();
|
||||
|
||||
@Test
|
||||
void applyAccountCreated() {
|
||||
//give
|
||||
var aggregateId = UUID.randomUUID().toString();
|
||||
var correlationId = UUID.randomUUID().toString();
|
||||
var accountAggregate = new AccountAggregate(aggregateId);
|
||||
var event = AccountCreated.builder()
|
||||
.correlationId(correlationId)
|
||||
.aggregateId(aggregateId)
|
||||
.balance(BigDecimal.TEN)
|
||||
.build();
|
||||
assertEquals(accountAggregate.getState().amount().compareTo(BigDecimal.ZERO), 0);
|
||||
|
||||
//when
|
||||
var result = applier.apply(accountAggregate, event).getState();
|
||||
|
||||
//then
|
||||
assertEquals(result.amount().compareTo(BigDecimal.TEN), 0);
|
||||
assertEquals(result.aggregateId(), aggregateId);
|
||||
}
|
||||
|
||||
@Test
|
||||
void applyMoneyDeposited() {
|
||||
//give
|
||||
var aggregateId = UUID.randomUUID().toString();
|
||||
var correlationId = UUID.randomUUID().toString();
|
||||
var accountAggregate = new AccountAggregate(aggregateId);
|
||||
var event = MoneyDeposited.builder()
|
||||
.correlationId(correlationId)
|
||||
.aggregateId(aggregateId)
|
||||
.amount(BigDecimal.TEN)
|
||||
.build();
|
||||
assertEquals(accountAggregate.getState().amount().compareTo(BigDecimal.ZERO), 0);
|
||||
|
||||
//when
|
||||
var result = applier.apply(accountAggregate, event).getState();
|
||||
|
||||
//then
|
||||
assertEquals(result.amount().compareTo(BigDecimal.TEN), 0);
|
||||
assertEquals(result.aggregateId(), aggregateId);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import com.mz.reactor.ddd.common.api.valueobject.Id;
|
||||
import com.mz.reactor.ddd.reactorddd.transaction.domain.command.CreateTransaction;
|
||||
import com.mz.reactor.ddd.reactorddd.transaction.domain.command.FinishTransaction;
|
||||
import com.mz.reactor.ddd.reactorddd.transaction.domain.event.TransactionCreated;
|
||||
import com.mz.reactor.ddd.reactorddd.transaction.domain.event.TransactionFailed;
|
||||
import com.mz.reactor.ddd.reactorddd.transaction.domain.event.TransactionFinished;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
@@ -73,6 +74,16 @@ public class TransactionAggregate {
|
||||
return this;
|
||||
}
|
||||
|
||||
public TransactionAggregate applyTransactionFinished(TransactionFinished event) {
|
||||
this.state = State.FINISHED;
|
||||
return this;
|
||||
}
|
||||
|
||||
public TransactionAggregate applyTransactionFailed(TransactionFailed event) {
|
||||
this.state = State.FAILED;
|
||||
return this;
|
||||
}
|
||||
|
||||
public TransactionState getState() {
|
||||
return TransactionState.builder()
|
||||
.amount(amount)
|
||||
|
||||
@@ -4,6 +4,7 @@ import com.mz.reactor.ddd.common.api.command.CommandHandler;
|
||||
import com.mz.reactor.ddd.common.api.command.CommandResult;
|
||||
import com.mz.reactor.ddd.common.api.command.ImmutableCommandResult;
|
||||
import com.mz.reactor.ddd.reactorddd.transaction.domain.TransactionAggregate;
|
||||
import com.mz.reactor.ddd.reactorddd.transaction.domain.event.FinishTransactionFailed;
|
||||
import com.mz.reactor.ddd.reactorddd.transaction.domain.event.TransactionFailed;
|
||||
|
||||
import java.util.List;
|
||||
@@ -17,9 +18,10 @@ public class TransactionCommandHandler implements CommandHandler<TransactionAggr
|
||||
|
||||
private CommandResult doCreateTransaction(TransactionAggregate aggregate, CreateTransaction command) {
|
||||
try {
|
||||
return Optional.ofNullable(aggregate.validateCreateTransaction(command))
|
||||
return Optional.of(command)
|
||||
.map(aggregate::validateCreateTransaction)
|
||||
.map(e -> CommandResult.builder()
|
||||
.events(List.of(e))
|
||||
.addEvents(e)
|
||||
.statusCode(CommandResult.StatusCode.OK)
|
||||
.build())
|
||||
.orElseGet(() -> (ImmutableCommandResult) CommandResult.notModified());
|
||||
@@ -32,6 +34,26 @@ public class TransactionCommandHandler implements CommandHandler<TransactionAggr
|
||||
}
|
||||
}
|
||||
|
||||
private CommandResult doFinishTransaction(TransactionAggregate aggregate, FinishTransaction commad) {
|
||||
try {
|
||||
return Optional.of(commad)
|
||||
.map(aggregate::validateFinishTransaction)
|
||||
.map(e -> CommandResult.builder().build())
|
||||
.orElseGet(() -> (ImmutableCommandResult) CommandResult.notModified());
|
||||
} catch (RuntimeException e) {
|
||||
return CommandResult.builder()
|
||||
.statusCode(CommandResult.StatusCode.FAILED)
|
||||
.error(e)
|
||||
.addEvents(FinishTransactionFailed.builder()
|
||||
.aggregateId(commad.aggregateId())
|
||||
.correlationId(commad.correlationId())
|
||||
.fromAccountId(commad.fromAccountId())
|
||||
.toAccountId(commad.toAccountId())
|
||||
.build())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandResult execute(TransactionAggregate aggregate, TransactionCommand command) {
|
||||
if (command instanceof CreateTransaction) {
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.mz.reactor.ddd.reactorddd.transaction.domain.event;
|
||||
|
||||
import org.immutables.value.Value;
|
||||
|
||||
@Value.Immutable
|
||||
public interface FinishTransactionFailed extends TransactionEvent {
|
||||
|
||||
String fromAccountId();
|
||||
|
||||
String toAccountId();
|
||||
|
||||
static ImmutableFinishTransactionFailed.Builder builder() {
|
||||
return ImmutableFinishTransactionFailed.builder();
|
||||
}
|
||||
}
|
||||
@@ -10,12 +10,28 @@ public class TransactionEventApplier implements EventApplier<TransactionAggregat
|
||||
public TransactionAggregate apply(TransactionAggregate aggregate, TransactionEvent event) {
|
||||
if (event instanceof TransactionCreated) {
|
||||
return applyTransactionCreated(aggregate, (TransactionCreated) event);
|
||||
}
|
||||
if (event instanceof TransactionFinished) {
|
||||
return applyTransactionFinished(aggregate, (TransactionFinished) event);
|
||||
}
|
||||
if (event instanceof TransactionFailed) {
|
||||
return applyTransactionFailed(aggregate, (TransactionFailed) event);
|
||||
} else {
|
||||
return aggregate;
|
||||
}
|
||||
}
|
||||
|
||||
private TransactionAggregate applyTransactionFailed(TransactionAggregate aggregate, TransactionFailed event) {
|
||||
return aggregate.applyTransactionFailed(event);
|
||||
}
|
||||
|
||||
private TransactionAggregate applyTransactionFinished(TransactionAggregate aggregate, TransactionFinished event) {
|
||||
return aggregate.applyTransactionFinished(event);
|
||||
}
|
||||
|
||||
private TransactionAggregate applyTransactionCreated(TransactionAggregate aggregate, TransactionCreated event) {
|
||||
return aggregate.applyTransactionCreated(event);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -122,4 +122,12 @@ class TransactionAggregateTest {
|
||||
.aggregateId(aggregateId)
|
||||
.build()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void applyTransactionFinished() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void applyTransactionFailed() {
|
||||
}
|
||||
}
|
||||
@@ -2,15 +2,54 @@ package com.mz.reactor.ddd.common.api.valueobject;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Objects;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.BinaryOperator;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class Money {
|
||||
private final BigDecimal amount;
|
||||
|
||||
public Money(BigDecimal amount) {
|
||||
this.amount = Objects.requireNonNull(amount, "Money amount can't be null!");
|
||||
this.amount = validateValue.apply(amount);
|
||||
}
|
||||
|
||||
public Money depositMoney(BigDecimal amount) {
|
||||
return new Money(this.amount.add(amount));
|
||||
}
|
||||
|
||||
public Money withdrawMoney(BigDecimal amount) {
|
||||
return new Money(this.amount.add(amount.negate()));
|
||||
}
|
||||
|
||||
public BigDecimal getAmount() {
|
||||
return amount;
|
||||
}
|
||||
|
||||
public static final Function<BigDecimal, BigDecimal> validateValue = value ->
|
||||
Objects.requireNonNull(value, "Money amount can't be null!");
|
||||
|
||||
public static final Function<BigDecimal, BigDecimal> validateGreaterThenZero = value -> {
|
||||
if (BigDecimal.ZERO.compareTo(value) == 0) {
|
||||
throw new RuntimeException(String.format("Can't deposit %s money", value));
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
public static final Function<BigDecimal, BigDecimal> validateDepositMoney = value ->
|
||||
validateValue.andThen(validateGreaterThenZero).apply(value);
|
||||
|
||||
public static final BinaryOperator<BigDecimal> validateWithdraw = (currentValue, value) ->
|
||||
validateValue
|
||||
.andThen(v -> {
|
||||
if (currentValue.compareTo(v) < 0) {
|
||||
throw new RuntimeException(String.format(
|
||||
"Can not withdraw amount %s, current balance is %s",
|
||||
value,
|
||||
currentValue
|
||||
));
|
||||
}
|
||||
return v;
|
||||
})
|
||||
.apply(value);
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user