From 9f612ecfdac6c14e3580f477458f9c3f1c7498bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Serdar=20Hamzao=C4=9Fullar=C4=B1?= Date: Sat, 5 Aug 2017 23:33:08 +0300 Subject: [PATCH 1/7] first commit --- event-sourcing/README.md | 21 ++++++++++++++++++ event-sourcing/etc/.gitkeep | 0 event-sourcing/pom.xml | 43 +++++++++++++++++++++++++++++++++++++ event-sourcing/src/.gitkeep | 0 4 files changed, 64 insertions(+) create mode 100644 event-sourcing/README.md create mode 100644 event-sourcing/etc/.gitkeep create mode 100644 event-sourcing/pom.xml create mode 100644 event-sourcing/src/.gitkeep diff --git a/event-sourcing/README.md b/event-sourcing/README.md new file mode 100644 index 000000000..c513d2da9 --- /dev/null +++ b/event-sourcing/README.md @@ -0,0 +1,21 @@ +--- +layout: pattern +title: Event Sourcing +folder: event-sourcing +permalink: /patterns/event-sourcing/ +categories: Concurrency +tags: + - Java + - Difficulty Intermediate + - Performance +--- + +## Intent + +## Applicability +Use the Event Sourcing pattern when + +* You have a limited accesibility resource and the asynchronous process is acceptable to reach that + +## Credits + diff --git a/event-sourcing/etc/.gitkeep b/event-sourcing/etc/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/event-sourcing/pom.xml b/event-sourcing/pom.xml new file mode 100644 index 000000000..6dc90063e --- /dev/null +++ b/event-sourcing/pom.xml @@ -0,0 +1,43 @@ + + + + 4.0.0 + + java-design-patterns + com.iluwatar + 1.17.0-SNAPSHOT + + event-sourcing + + + junit + junit + test + + + \ No newline at end of file diff --git a/event-sourcing/src/.gitkeep b/event-sourcing/src/.gitkeep new file mode 100644 index 000000000..e69de29bb From 1474a50e5e612af12372979af2c62f1da5db9bcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Serdar=20Hamzao=C4=9Fullar=C4=B1?= Date: Sun, 6 Aug 2017 22:51:43 +0300 Subject: [PATCH 2/7] Example done with app class --- event-sourcing/pom.xml | 7 ++ .../event/sourcing/api/DomainEvent.java | 41 ++++++++ .../event/sourcing/api/EventProcessor.java | 11 +++ .../sourcing/api/ExternalEventListener.java | 8 ++ .../event/sourcing/api/ProcessorJournal.java | 10 ++ .../com/iluwatar/event/sourcing/app/App.java | 58 +++++++++++ .../event/sourcing/domain/Account.java | 63 ++++++++++++ .../event/sourcing/domain/Transaction.java | 46 +++++++++ .../sourcing/event/AccountCreateEvent.java | 43 ++++++++ .../sourcing/event/MoneyDepositEvent.java | 38 +++++++ .../sourcing/event/MoneyTransferEvent.java | 56 +++++++++++ .../sourcing/event/MoneyWithdrawalEvent.java | 38 +++++++ .../gateway/AccountCreateContractSender.java | 12 +++ .../event/sourcing/gateway/Gateways.java | 17 ++++ .../sourcing/gateway/TransactionLogger.java | 12 +++ .../sourcing/journal/JsonFileJournal.java | 99 +++++++++++++++++++ .../processor/DomainEventProcessor.java | 48 +++++++++ .../sourcing/service/AccountService.java | 22 +++++ .../service/MoneyTransactionService.java | 35 +++++++ .../sourcing/service/SequenceIdGenerator.java | 14 +++ .../sourcing/state/AccountAggregate.java | 29 ++++++ pom.xml | 1 + 22 files changed, 708 insertions(+) create mode 100644 event-sourcing/src/main/java/com/iluwatar/event/sourcing/api/DomainEvent.java create mode 100644 event-sourcing/src/main/java/com/iluwatar/event/sourcing/api/EventProcessor.java create mode 100644 event-sourcing/src/main/java/com/iluwatar/event/sourcing/api/ExternalEventListener.java create mode 100644 event-sourcing/src/main/java/com/iluwatar/event/sourcing/api/ProcessorJournal.java create mode 100644 event-sourcing/src/main/java/com/iluwatar/event/sourcing/app/App.java create mode 100644 event-sourcing/src/main/java/com/iluwatar/event/sourcing/domain/Account.java create mode 100644 event-sourcing/src/main/java/com/iluwatar/event/sourcing/domain/Transaction.java create mode 100644 event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/AccountCreateEvent.java create mode 100644 event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyDepositEvent.java create mode 100644 event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyTransferEvent.java create mode 100644 event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyWithdrawalEvent.java create mode 100644 event-sourcing/src/main/java/com/iluwatar/event/sourcing/gateway/AccountCreateContractSender.java create mode 100644 event-sourcing/src/main/java/com/iluwatar/event/sourcing/gateway/Gateways.java create mode 100644 event-sourcing/src/main/java/com/iluwatar/event/sourcing/gateway/TransactionLogger.java create mode 100644 event-sourcing/src/main/java/com/iluwatar/event/sourcing/journal/JsonFileJournal.java create mode 100644 event-sourcing/src/main/java/com/iluwatar/event/sourcing/processor/DomainEventProcessor.java create mode 100644 event-sourcing/src/main/java/com/iluwatar/event/sourcing/service/AccountService.java create mode 100644 event-sourcing/src/main/java/com/iluwatar/event/sourcing/service/MoneyTransactionService.java create mode 100644 event-sourcing/src/main/java/com/iluwatar/event/sourcing/service/SequenceIdGenerator.java create mode 100644 event-sourcing/src/main/java/com/iluwatar/event/sourcing/state/AccountAggregate.java diff --git a/event-sourcing/pom.xml b/event-sourcing/pom.xml index 6dc90063e..35d462cb1 100644 --- a/event-sourcing/pom.xml +++ b/event-sourcing/pom.xml @@ -39,5 +39,12 @@ junit test + + + com.google.code.gson + gson + 2.8.1 + + \ No newline at end of file diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/api/DomainEvent.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/api/DomainEvent.java new file mode 100644 index 000000000..d77654869 --- /dev/null +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/api/DomainEvent.java @@ -0,0 +1,41 @@ +package com.iluwatar.event.sourcing.api; + +import java.io.Serializable; + +/** + * Created by serdarh on 06.08.2017. + */ +public abstract class DomainEvent implements Serializable { + private final long sequenceId; + private final long createdTime; + private boolean replica = false; + private final String eventClassName; + + public DomainEvent(long sequenceId, long createdTime, String eventClassName) { + this.sequenceId = sequenceId; + this.createdTime = createdTime; + this.eventClassName = eventClassName; + } + + public long getSequenceId() { + return sequenceId; + } + + public long getCreatedTime() { + return createdTime; + } + + public boolean isReplica() { + return replica; + } + + public void setReplica(boolean replica) { + this.replica = replica; + } + + public abstract void process(); + + public String getEventClassName() { + return eventClassName; + } +} diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/api/EventProcessor.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/api/EventProcessor.java new file mode 100644 index 000000000..729efc83c --- /dev/null +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/api/EventProcessor.java @@ -0,0 +1,11 @@ +package com.iluwatar.event.sourcing.api; + +/** + * Created by serdarh on 06.08.2017. + */ +public interface EventProcessor { + void process(DomainEvent domainEvent); + void setPrecessorJournal(ProcessorJournal precessorJournal); + void addExternalEventListener(ExternalEventListener externalEventListener); + void recover(); +} diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/api/ExternalEventListener.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/api/ExternalEventListener.java new file mode 100644 index 000000000..a1cb78108 --- /dev/null +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/api/ExternalEventListener.java @@ -0,0 +1,8 @@ +package com.iluwatar.event.sourcing.api; + +/** + * Created by serdarh on 06.08.2017. + */ +public interface ExternalEventListener { + void notify(DomainEvent domainEvent); +} diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/api/ProcessorJournal.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/api/ProcessorJournal.java new file mode 100644 index 000000000..906c66247 --- /dev/null +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/api/ProcessorJournal.java @@ -0,0 +1,10 @@ +package com.iluwatar.event.sourcing.api; + +/** + * Created by serdarh on 06.08.2017. + */ +public interface ProcessorJournal { + void write(DomainEvent domainEvent); + void reset(); + DomainEvent readNext(); +} diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/app/App.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/app/App.java new file mode 100644 index 000000000..8627736f1 --- /dev/null +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/app/App.java @@ -0,0 +1,58 @@ +package com.iluwatar.event.sourcing.app; + +import com.iluwatar.event.sourcing.journal.JsonFileJournal; +import com.iluwatar.event.sourcing.processor.DomainEventProcessor; +import com.iluwatar.event.sourcing.service.AccountService; +import com.iluwatar.event.sourcing.service.MoneyTransactionService; +import com.iluwatar.event.sourcing.state.AccountAggregate; + +import java.math.BigDecimal; + +/** + * Created by serdarh on 06.08.2017. + */ +public class App { + + public static void main(String[] args) { + System.out.println("Running the system first time............"); + + DomainEventProcessor domainEventProcessor = new DomainEventProcessor(); + JsonFileJournal jsonFileJournal = new JsonFileJournal(); + jsonFileJournal.reset(); + domainEventProcessor.setPrecessorJournal(jsonFileJournal); + + System.out.println("Creating th accounts............"); + + AccountService accountService = new AccountService(domainEventProcessor); + MoneyTransactionService moneyTransactionService = new MoneyTransactionService(domainEventProcessor); + accountService.createAccount(1,"Daenerys Targaryen"); + accountService.createAccount(2,"Jon Snow"); + + System.out.println("Do some money operations............"); + + moneyTransactionService.depositMoney(1,new BigDecimal("100000")); + moneyTransactionService.depositMoney(2,new BigDecimal("10")); + + moneyTransactionService.transferMoney(1,2,new BigDecimal("10000")); + moneyTransactionService.withdrawalMoney(2, new BigDecimal("1000")); + + System.out.println("...............State:............"); + System.out.println(AccountAggregate.getAccount(1)); + System.out.println(AccountAggregate.getAccount(2)); + + System.out.println("At that point system goes down state in memory cleared............"); + + AccountAggregate.resetState(); + + System.out.println("Recover the syste by the events in journal file............"); + + domainEventProcessor = new DomainEventProcessor(); + jsonFileJournal = new JsonFileJournal(); + domainEventProcessor.setPrecessorJournal(jsonFileJournal); + domainEventProcessor.recover(); + + System.out.println("...............State Recovered:............"); + System.out.println(AccountAggregate.getAccount(1)); + System.out.println(AccountAggregate.getAccount(2)); + } +} diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/domain/Account.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/domain/Account.java new file mode 100644 index 000000000..5a7a77fcf --- /dev/null +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/domain/Account.java @@ -0,0 +1,63 @@ +package com.iluwatar.event.sourcing.domain; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; + +/** + * Created by serdarh on 06.08.2017. + */ +public class Account { + private final int accountNo; + private final String owner; + private BigDecimal money; + private List transactions; + + public Account(int accountNo, String owner) { + this.accountNo = accountNo; + this.owner = owner; + money = BigDecimal.ZERO; + transactions = new ArrayList<>(); + } + + public int getAccountNo() { + return accountNo; + } + + public String getOwner() { + return owner; + } + + public BigDecimal getMoney() { + return money; + } + + public List getTransactions() { + return transactions; + } + + public void setMoney(BigDecimal money) { + this.money = money; + } + + public void setTransactions(List transactions) { + this.transactions = transactions; + } + + public Account copy() { + Account account = new Account(accountNo, owner); + account.setMoney(money); + account.setTransactions(transactions); + return account; + } + + @Override + public String toString() { + return "Account{" + + "accountNo=" + accountNo + + ", owner='" + owner + '\'' + + ", money=" + money + + ", transactions=" + transactions + + '}'; + } +} diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/domain/Transaction.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/domain/Transaction.java new file mode 100644 index 000000000..557efbedc --- /dev/null +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/domain/Transaction.java @@ -0,0 +1,46 @@ +package com.iluwatar.event.sourcing.domain; + +import java.math.BigDecimal; + +/** + * Created by serdarh on 06.08.2017. + */ +public class Transaction { + private final int accountNo; + private final BigDecimal moneyIn; + private final BigDecimal moneyOut; + private final BigDecimal lastBalance; + + public Transaction(int accountNo, BigDecimal moneyIn, BigDecimal moneyOut, BigDecimal lastBalance) { + this.accountNo = accountNo; + this.moneyIn = moneyIn; + this.moneyOut = moneyOut; + this.lastBalance = lastBalance; + } + + public int getAccountNo() { + return accountNo; + } + + public BigDecimal getMoneyIn() { + return moneyIn; + } + + public BigDecimal getMoneyOut() { + return moneyOut; + } + + public BigDecimal getLastBalance() { + return lastBalance; + } + + @Override + public String toString() { + return "Transaction{" + + "accountNo=" + accountNo + + ", moneyIn=" + moneyIn + + ", moneyOut=" + moneyOut + + ", lastBalance=" + lastBalance + + '}'; + } +} diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/AccountCreateEvent.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/AccountCreateEvent.java new file mode 100644 index 000000000..3957a4fe7 --- /dev/null +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/AccountCreateEvent.java @@ -0,0 +1,43 @@ +package com.iluwatar.event.sourcing.event; + +import com.iluwatar.event.sourcing.api.DomainEvent; +import com.iluwatar.event.sourcing.domain.Account; +import com.iluwatar.event.sourcing.gateway.Gateways; +import com.iluwatar.event.sourcing.state.AccountAggregate; + +/** + * Created by serdarh on 06.08.2017. + */ +public class AccountCreateEvent extends DomainEvent { + private final int accountNo; + private final String owner; + + public AccountCreateEvent(long sequenceId, long createdTime, int accountNo, String owner) { + super(sequenceId, createdTime, "AccountCreateEvent"); + this.accountNo = accountNo; + this.owner = owner; + } + + public int getAccountNo() { + return accountNo; + } + + public String getOwner() { + return owner; + } + + @Override + public void process() { + Account account = AccountAggregate.getAccount(accountNo); + if(account!=null){ + throw new RuntimeException("Account already exists"); + } + account = new Account(accountNo,owner); + AccountAggregate.putAccount(account); + + // check if this event is replicated from journal before calling an external gateway function + if(!isReplica()) { + Gateways.getAccountCreateContractSender().sendContractInfo(account); + } + } +} diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyDepositEvent.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyDepositEvent.java new file mode 100644 index 000000000..ffa9d0763 --- /dev/null +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyDepositEvent.java @@ -0,0 +1,38 @@ +package com.iluwatar.event.sourcing.event; + +import com.iluwatar.event.sourcing.api.DomainEvent; +import com.iluwatar.event.sourcing.domain.Account; +import com.iluwatar.event.sourcing.domain.Transaction; +import com.iluwatar.event.sourcing.gateway.Gateways; +import com.iluwatar.event.sourcing.state.AccountAggregate; + +import java.math.BigDecimal; + +/** + * Created by serdarh on 06.08.2017. + */ +public class MoneyDepositEvent extends DomainEvent { + private BigDecimal money; + private int accountNo; + + public MoneyDepositEvent(long sequenceId, long createdTime, int accountNo,BigDecimal money) { + super(sequenceId, createdTime, "MoneyDepositEvent"); + this.money = money; + this.accountNo = accountNo; + } + + @Override + public void process() { + Account account = AccountAggregate.getAccount(accountNo); + if(account==null){ + throw new RuntimeException("Account not found"); + } + account.setMoney(account.getMoney().add(money)); + Transaction transaction = new Transaction(accountNo,money,BigDecimal.ZERO,account.getMoney()); + account.getTransactions().add(transaction); + AccountAggregate.putAccount(account); + if(!isReplica()) { + Gateways.getTransactionLogger().log(transaction); + } + } +} diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyTransferEvent.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyTransferEvent.java new file mode 100644 index 000000000..4e0fb9829 --- /dev/null +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyTransferEvent.java @@ -0,0 +1,56 @@ +package com.iluwatar.event.sourcing.event; + +import com.iluwatar.event.sourcing.api.DomainEvent; +import com.iluwatar.event.sourcing.domain.Account; +import com.iluwatar.event.sourcing.domain.Transaction; +import com.iluwatar.event.sourcing.gateway.Gateways; +import com.iluwatar.event.sourcing.state.AccountAggregate; + +import java.math.BigDecimal; + +/** + * Created by serdarh on 06.08.2017. + */ +public class MoneyTransferEvent extends DomainEvent { + private BigDecimal money; + private int accountNoFrom; + private int accountNoTo; + + public MoneyTransferEvent(long sequenceId, long createdTime, BigDecimal money, int accountNoFrom, int accountNoTo) { + super(sequenceId, createdTime, "MoneyTransferEvent"); + this.money = money; + this.accountNoFrom = accountNoFrom; + this.accountNoTo = accountNoTo; + } + + @Override + public void process() { + Account accountFrom = AccountAggregate.getAccount(accountNoFrom); + if(accountFrom==null){ + throw new RuntimeException("Account not found "+accountNoFrom); + } + Account accountTo = AccountAggregate.getAccount(accountNoTo); + if(accountTo==null){ + throw new RuntimeException("Account not found"+ accountTo); + } + if(accountFrom.getMoney().compareTo(money)==-1){ + throw new RuntimeException("Insufficient Account Balance"); + } + accountFrom.setMoney(accountFrom.getMoney().subtract(money)); + accountTo.setMoney(accountTo.getMoney().add(money)); + + Transaction transactionFrom = new Transaction(accountNoFrom,BigDecimal.ZERO,money,accountFrom.getMoney()); + accountFrom.getTransactions().add(transactionFrom); + + Transaction transactionTo = new Transaction(accountNoTo,money,BigDecimal.ZERO,accountTo.getMoney()); + accountTo.getTransactions().add(transactionTo); + + AccountAggregate.putAccount(accountFrom); + AccountAggregate.putAccount(accountTo); + + if(!isReplica()) { + Gateways.getTransactionLogger().log(transactionFrom); + Gateways.getTransactionLogger().log(transactionTo); + } + } +} diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyWithdrawalEvent.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyWithdrawalEvent.java new file mode 100644 index 000000000..27a63d13d --- /dev/null +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyWithdrawalEvent.java @@ -0,0 +1,38 @@ +package com.iluwatar.event.sourcing.event; + +import com.iluwatar.event.sourcing.api.DomainEvent; +import com.iluwatar.event.sourcing.domain.Account; +import com.iluwatar.event.sourcing.domain.Transaction; +import com.iluwatar.event.sourcing.gateway.Gateways; +import com.iluwatar.event.sourcing.state.AccountAggregate; + +import java.math.BigDecimal; + +/** + * Created by serdarh on 06.08.2017. + */ +public class MoneyWithdrawalEvent extends DomainEvent { + private BigDecimal money; + private int accountNo; + + public MoneyWithdrawalEvent(long sequenceId, long createdTime, int accountNo,BigDecimal money) { + super(sequenceId, createdTime, "MoneyWithdrawalEvent"); + this.money = money; + this.accountNo = accountNo; + } + + @Override + public void process() { + Account account = AccountAggregate.getAccount(accountNo); + if(account==null){ + throw new RuntimeException("Account not found"); + } + account.setMoney(account.getMoney().subtract(money)); + Transaction transaction = new Transaction(accountNo,BigDecimal.ZERO,money,account.getMoney()); + account.getTransactions().add(transaction); + AccountAggregate.putAccount(account); + if(!isReplica()) { + Gateways.getTransactionLogger().log(transaction); + } + } +} diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/gateway/AccountCreateContractSender.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/gateway/AccountCreateContractSender.java new file mode 100644 index 000000000..42dfccf7b --- /dev/null +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/gateway/AccountCreateContractSender.java @@ -0,0 +1,12 @@ +package com.iluwatar.event.sourcing.gateway; + +import com.iluwatar.event.sourcing.domain.Account; + +/** + * Created by serdarh on 06.08.2017. + */ +public class AccountCreateContractSender { + public void sendContractInfo(Account account){ + // an example imaginary funciton which sends account info to some external end point + } +} diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/gateway/Gateways.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/gateway/Gateways.java new file mode 100644 index 000000000..d167393bf --- /dev/null +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/gateway/Gateways.java @@ -0,0 +1,17 @@ +package com.iluwatar.event.sourcing.gateway; + +/** + * Created by serdarh on 06.08.2017. + */ +public class Gateways { + private static AccountCreateContractSender accountCreateContractSender = new AccountCreateContractSender(); + private static TransactionLogger transactionLogger = new TransactionLogger(); + + public static AccountCreateContractSender getAccountCreateContractSender() { + return accountCreateContractSender; + } + + public static TransactionLogger getTransactionLogger() { + return transactionLogger; + } +} diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/gateway/TransactionLogger.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/gateway/TransactionLogger.java new file mode 100644 index 000000000..6d961d96e --- /dev/null +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/gateway/TransactionLogger.java @@ -0,0 +1,12 @@ +package com.iluwatar.event.sourcing.gateway; + +import com.iluwatar.event.sourcing.domain.Transaction; + +/** + * Created by serdarh on 06.08.2017. + */ +public class TransactionLogger { + public void log(Transaction transaction) { + // example imaginary function that logs the transaction to somewhere + } +} diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/journal/JsonFileJournal.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/journal/JsonFileJournal.java new file mode 100644 index 000000000..45a982c19 --- /dev/null +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/journal/JsonFileJournal.java @@ -0,0 +1,99 @@ +package com.iluwatar.event.sourcing.journal; + +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; +import com.iluwatar.event.sourcing.api.DomainEvent; +import com.iluwatar.event.sourcing.api.ProcessorJournal; +import com.iluwatar.event.sourcing.event.AccountCreateEvent; +import com.iluwatar.event.sourcing.event.MoneyDepositEvent; +import com.iluwatar.event.sourcing.event.MoneyTransferEvent; +import com.iluwatar.event.sourcing.event.MoneyWithdrawalEvent; + +import java.io.*; +import java.util.ArrayList; +import java.util.List; + +/** + * Created by serdarh on 06.08.2017. + */ +public class JsonFileJournal implements ProcessorJournal{ + + private File aFile; + private List events = new ArrayList<>(); + private int index = 0; + public JsonFileJournal() { + aFile = new File("Journal.json"); + if(aFile.exists()) { + try (BufferedReader input = new BufferedReader(new InputStreamReader(new FileInputStream(aFile), "UTF-8"))) { + String line; + while ((line = input.readLine()) != null) { + events.add(line); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + }else{ + reset(); + } + } + + @Override + public void write(DomainEvent domainEvent) { + Gson gson = new Gson(); + JsonElement jsonElement; + if(domainEvent instanceof AccountCreateEvent) { + jsonElement = gson.toJsonTree(domainEvent, AccountCreateEvent.class); + }else if(domainEvent instanceof MoneyDepositEvent) { + jsonElement = gson.toJsonTree(domainEvent, MoneyDepositEvent.class); + }else if(domainEvent instanceof MoneyWithdrawalEvent) { + jsonElement = gson.toJsonTree(domainEvent, MoneyWithdrawalEvent.class); + }else if(domainEvent instanceof MoneyTransferEvent) { + jsonElement = gson.toJsonTree(domainEvent, MoneyTransferEvent.class); + }else{ + throw new RuntimeException("Journal Event not recegnized"); + } + + try (Writer output = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(aFile, true), "UTF-8"))) { + String eventString = jsonElement.toString(); + output.write(eventString +"\r\n"); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public void reset() { + aFile.delete(); + } + + + @Override + public DomainEvent readNext() { + if(index>=events.size()){ + return null; + } + String event = events.get(index); + index++; + + JsonParser parser = new JsonParser(); + JsonElement jsonElement = parser.parse(event); + String eventClassName = jsonElement.getAsJsonObject().get("eventClassName").getAsString(); + Gson gson = new Gson(); + DomainEvent domainEvent; + if(eventClassName.equals("AccountCreateEvent")) { + domainEvent = gson.fromJson(jsonElement, AccountCreateEvent.class); + }else if(eventClassName.equals("MoneyDepositEvent")) { + domainEvent = gson.fromJson(jsonElement, MoneyDepositEvent.class); + }else if(eventClassName.equals("MoneyTransferEvent")) { + domainEvent = gson.fromJson(jsonElement, MoneyTransferEvent.class); + }else if(eventClassName.equals("MoneyWithdrawalEvent")) { + domainEvent = gson.fromJson(jsonElement, MoneyWithdrawalEvent.class); + }else{ + throw new RuntimeException("Journal Event not recegnized"); + } + + domainEvent.setReplica(true); + return domainEvent; + } +} diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/processor/DomainEventProcessor.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/processor/DomainEventProcessor.java new file mode 100644 index 000000000..a23c41905 --- /dev/null +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/processor/DomainEventProcessor.java @@ -0,0 +1,48 @@ +package com.iluwatar.event.sourcing.processor; + +import com.iluwatar.event.sourcing.api.DomainEvent; +import com.iluwatar.event.sourcing.api.EventProcessor; +import com.iluwatar.event.sourcing.api.ExternalEventListener; +import com.iluwatar.event.sourcing.api.ProcessorJournal; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by serdarh on 06.08.2017. + */ +public class DomainEventProcessor implements EventProcessor { + + private ProcessorJournal precessorJournal; + private List externalEventListeners = new ArrayList<>(); + + @Override + public void process(DomainEvent domainEvent) { + externalEventListeners.forEach(externalEventListener -> externalEventListener.notify(domainEvent)); + domainEvent.process(); + precessorJournal.write(domainEvent); + } + + @Override + public void setPrecessorJournal(ProcessorJournal precessorJournal) { + this.precessorJournal = precessorJournal; + } + + @Override + public void addExternalEventListener(ExternalEventListener externalEventListener) { + externalEventListeners.add(externalEventListener); + } + + @Override + public void recover() { + DomainEvent domainEvent; + while(true) { + domainEvent = precessorJournal.readNext(); + if(domainEvent==null){ + break; + }else{ + domainEvent.process(); + } + } + } +} diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/service/AccountService.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/service/AccountService.java new file mode 100644 index 000000000..b0a707788 --- /dev/null +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/service/AccountService.java @@ -0,0 +1,22 @@ +package com.iluwatar.event.sourcing.service; + +import com.iluwatar.event.sourcing.api.EventProcessor; +import com.iluwatar.event.sourcing.event.AccountCreateEvent; + +import java.util.Date; + +/** + * Created by serdarh on 06.08.2017. + */ +public class AccountService { + private EventProcessor eventProcessor; + + public AccountService(EventProcessor eventProcessor) { + this.eventProcessor = eventProcessor; + } + + public void createAccount(int accountNo, String owner){ + AccountCreateEvent accountCreateEvent = new AccountCreateEvent(SequenceIdGenerator.nextSequenceId(), new Date().getTime(),accountNo,owner); + eventProcessor.process(accountCreateEvent); + } +} diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/service/MoneyTransactionService.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/service/MoneyTransactionService.java new file mode 100644 index 000000000..80c2a5a4f --- /dev/null +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/service/MoneyTransactionService.java @@ -0,0 +1,35 @@ +package com.iluwatar.event.sourcing.service; + +import com.iluwatar.event.sourcing.api.EventProcessor; +import com.iluwatar.event.sourcing.event.MoneyDepositEvent; +import com.iluwatar.event.sourcing.event.MoneyTransferEvent; +import com.iluwatar.event.sourcing.event.MoneyWithdrawalEvent; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * Created by serdarh on 06.08.2017. + */ +public class MoneyTransactionService { + private EventProcessor eventProcessor; + + public MoneyTransactionService(EventProcessor eventProcessor) { + this.eventProcessor = eventProcessor; + } + + public void depositMoney(int accountNo, BigDecimal money){ + MoneyDepositEvent moneyDepositEvent = new MoneyDepositEvent(SequenceIdGenerator.nextSequenceId(), new Date().getTime(), accountNo, money); + eventProcessor.process(moneyDepositEvent); + } + + public void withdrawalMoney(int accountNo, BigDecimal money){ + MoneyWithdrawalEvent moneyWithdrawalEvent = new MoneyWithdrawalEvent(SequenceIdGenerator.nextSequenceId(), new Date().getTime(), accountNo, money); + eventProcessor.process(moneyWithdrawalEvent); + } + + public void transferMoney(int accountNoFrom, int accountNoTo, BigDecimal money){ + MoneyTransferEvent moneyTransferEvent = new MoneyTransferEvent(SequenceIdGenerator.nextSequenceId(), new Date().getTime(), money, accountNoFrom, accountNoTo); + eventProcessor.process(moneyTransferEvent); + } +} diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/service/SequenceIdGenerator.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/service/SequenceIdGenerator.java new file mode 100644 index 000000000..2eec6a70d --- /dev/null +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/service/SequenceIdGenerator.java @@ -0,0 +1,14 @@ +package com.iluwatar.event.sourcing.service; + +/** + * Created by serdarh on 06.08.2017. + */ +public class SequenceIdGenerator { + private static long sequenceId = 0L; + + public static long nextSequenceId(){ + sequenceId++; + return sequenceId; + } + +} diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/state/AccountAggregate.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/state/AccountAggregate.java new file mode 100644 index 000000000..b927af0e1 --- /dev/null +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/state/AccountAggregate.java @@ -0,0 +1,29 @@ +package com.iluwatar.event.sourcing.state; + +import com.iluwatar.event.sourcing.domain.Account; + +import java.util.HashMap; +import java.util.Map; + +/** + * Created by serdarh on 06.08.2017. + */ +public class AccountAggregate { + private static Map accounts = new HashMap<>(); + + public static void putAccount(Account account){ + accounts.put(account.getAccountNo(), account); + } + + public static Account getAccount(int accountNo){ + Account account = accounts.get(accountNo); + if(account == null){ + return null; + } + return account.copy(); + } + + public static void resetState(){ + accounts = new HashMap<>(); + } +} diff --git a/pom.xml b/pom.xml index 4f4b5dee6..5f46c9401 100644 --- a/pom.xml +++ b/pom.xml @@ -143,6 +143,7 @@ extension-objects marker cqrs + event-sourcing From 64824d65aa9df0928d67e31bc7e2e0290c989ad9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Serdar=20Hamzao=C4=9Fullar=C4=B1?= Date: Sat, 12 Aug 2017 16:21:35 +0300 Subject: [PATCH 3/7] Some Object Orianted refactor --- .../event/sourcing/api/DomainEvent.java | 10 +-- .../event/sourcing/api/EventProcessor.java | 1 - .../sourcing/api/ExternalEventListener.java | 8 --- .../event/sourcing/domain/Account.java | 67 +++++++++++++++++++ .../sourcing/event/AccountCreateEvent.java | 8 +-- .../sourcing/event/MoneyDepositEvent.java | 18 ++--- .../sourcing/event/MoneyTransferEvent.java | 34 ++++------ .../sourcing/event/MoneyWithdrawalEvent.java | 18 ++--- .../sourcing/journal/JsonFileJournal.java | 2 +- .../processor/DomainEventProcessor.java | 11 --- 10 files changed, 106 insertions(+), 71 deletions(-) delete mode 100644 event-sourcing/src/main/java/com/iluwatar/event/sourcing/api/ExternalEventListener.java diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/api/DomainEvent.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/api/DomainEvent.java index d77654869..e20d03232 100644 --- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/api/DomainEvent.java +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/api/DomainEvent.java @@ -8,7 +8,7 @@ import java.io.Serializable; public abstract class DomainEvent implements Serializable { private final long sequenceId; private final long createdTime; - private boolean replica = false; + private boolean realTime = true; private final String eventClassName; public DomainEvent(long sequenceId, long createdTime, String eventClassName) { @@ -25,12 +25,12 @@ public abstract class DomainEvent implements Serializable { return createdTime; } - public boolean isReplica() { - return replica; + public boolean isRealTime() { + return realTime; } - public void setReplica(boolean replica) { - this.replica = replica; + public void setRealTime(boolean realTime) { + this.realTime = realTime; } public abstract void process(); diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/api/EventProcessor.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/api/EventProcessor.java index 729efc83c..0fc673bf4 100644 --- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/api/EventProcessor.java +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/api/EventProcessor.java @@ -6,6 +6,5 @@ package com.iluwatar.event.sourcing.api; public interface EventProcessor { void process(DomainEvent domainEvent); void setPrecessorJournal(ProcessorJournal precessorJournal); - void addExternalEventListener(ExternalEventListener externalEventListener); void recover(); } diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/api/ExternalEventListener.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/api/ExternalEventListener.java deleted file mode 100644 index a1cb78108..000000000 --- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/api/ExternalEventListener.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.iluwatar.event.sourcing.api; - -/** - * Created by serdarh on 06.08.2017. - */ -public interface ExternalEventListener { - void notify(DomainEvent domainEvent); -} diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/domain/Account.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/domain/Account.java index 5a7a77fcf..48cda1eca 100644 --- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/domain/Account.java +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/domain/Account.java @@ -1,5 +1,12 @@ package com.iluwatar.event.sourcing.domain; +import com.iluwatar.event.sourcing.event.AccountCreateEvent; +import com.iluwatar.event.sourcing.event.MoneyDepositEvent; +import com.iluwatar.event.sourcing.event.MoneyTransferEvent; +import com.iluwatar.event.sourcing.event.MoneyWithdrawalEvent; +import com.iluwatar.event.sourcing.gateway.Gateways; +import com.iluwatar.event.sourcing.state.AccountAggregate; + import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; @@ -60,4 +67,64 @@ public class Account { ", transactions=" + transactions + '}'; } + + private Transaction depositMoney(BigDecimal money) { + this.money = this.money.add(money); + Transaction transaction = new Transaction(accountNo,money,BigDecimal.ZERO,this.money); + transactions.add(transaction); + return transaction; + } + + private Transaction withdrawMoney(BigDecimal money) { + this.money = this.money.subtract(money); + Transaction transaction = new Transaction(accountNo,BigDecimal.ZERO,money,this.money); + transactions.add(transaction); + return transaction; + } + + private void handleDeposit(BigDecimal money,boolean realTime) { + Transaction transaction = depositMoney(money); + AccountAggregate.putAccount(this); + if(realTime) { + Gateways.getTransactionLogger().log(transaction); + } + } + + private void handleWithdrawal(BigDecimal money, boolean realTime) { + if(this.money.compareTo(money)==-1){ + throw new RuntimeException("Insufficient Account Balance"); + } + + Transaction transaction = withdrawMoney(money); + AccountAggregate.putAccount(this); + if(realTime) { + Gateways.getTransactionLogger().log(transaction); + } + } + + public void handleEvent(MoneyDepositEvent moneyDepositEvent) { + handleDeposit(moneyDepositEvent.getMoney(),moneyDepositEvent.isRealTime()); + } + + + public void handleEvent(MoneyWithdrawalEvent moneyWithdrawalEvent) { + handleWithdrawal(moneyWithdrawalEvent.getMoney(),moneyWithdrawalEvent.isRealTime()); + } + + + public void handleTransferFromEvent(MoneyTransferEvent moneyTransferEvent) { + handleWithdrawal(moneyTransferEvent.getMoney(),moneyTransferEvent.isRealTime()); + } + + public void handleTransferToEvent(MoneyTransferEvent moneyTransferEvent) { + handleDeposit(moneyTransferEvent.getMoney(),moneyTransferEvent.isRealTime()); + } + + public void handleEvent(AccountCreateEvent accountCreateEvent) { + AccountAggregate.putAccount(this); + // check if this event is replicated from journal before calling an external gateway function + if(accountCreateEvent.isRealTime()) { + Gateways.getAccountCreateContractSender().sendContractInfo(this); + } + } } diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/AccountCreateEvent.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/AccountCreateEvent.java index 3957a4fe7..1ea089e2f 100644 --- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/AccountCreateEvent.java +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/AccountCreateEvent.java @@ -2,7 +2,6 @@ package com.iluwatar.event.sourcing.event; import com.iluwatar.event.sourcing.api.DomainEvent; import com.iluwatar.event.sourcing.domain.Account; -import com.iluwatar.event.sourcing.gateway.Gateways; import com.iluwatar.event.sourcing.state.AccountAggregate; /** @@ -33,11 +32,6 @@ public class AccountCreateEvent extends DomainEvent { throw new RuntimeException("Account already exists"); } account = new Account(accountNo,owner); - AccountAggregate.putAccount(account); - - // check if this event is replicated from journal before calling an external gateway function - if(!isReplica()) { - Gateways.getAccountCreateContractSender().sendContractInfo(account); - } + account.handleEvent(this); } } diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyDepositEvent.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyDepositEvent.java index ffa9d0763..384a9e198 100644 --- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyDepositEvent.java +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyDepositEvent.java @@ -2,8 +2,6 @@ package com.iluwatar.event.sourcing.event; import com.iluwatar.event.sourcing.api.DomainEvent; import com.iluwatar.event.sourcing.domain.Account; -import com.iluwatar.event.sourcing.domain.Transaction; -import com.iluwatar.event.sourcing.gateway.Gateways; import com.iluwatar.event.sourcing.state.AccountAggregate; import java.math.BigDecimal; @@ -21,18 +19,20 @@ public class MoneyDepositEvent extends DomainEvent { this.accountNo = accountNo; } + public BigDecimal getMoney() { + return money; + } + + public int getAccountNo() { + return accountNo; + } + @Override public void process() { Account account = AccountAggregate.getAccount(accountNo); if(account==null){ throw new RuntimeException("Account not found"); } - account.setMoney(account.getMoney().add(money)); - Transaction transaction = new Transaction(accountNo,money,BigDecimal.ZERO,account.getMoney()); - account.getTransactions().add(transaction); - AccountAggregate.putAccount(account); - if(!isReplica()) { - Gateways.getTransactionLogger().log(transaction); - } + account.handleEvent(this); } } diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyTransferEvent.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyTransferEvent.java index 4e0fb9829..6c873d4db 100644 --- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyTransferEvent.java +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyTransferEvent.java @@ -2,8 +2,6 @@ package com.iluwatar.event.sourcing.event; import com.iluwatar.event.sourcing.api.DomainEvent; import com.iluwatar.event.sourcing.domain.Account; -import com.iluwatar.event.sourcing.domain.Transaction; -import com.iluwatar.event.sourcing.gateway.Gateways; import com.iluwatar.event.sourcing.state.AccountAggregate; import java.math.BigDecimal; @@ -23,6 +21,18 @@ public class MoneyTransferEvent extends DomainEvent { this.accountNoTo = accountNoTo; } + public BigDecimal getMoney() { + return money; + } + + public int getAccountNoFrom() { + return accountNoFrom; + } + + public int getAccountNoTo() { + return accountNoTo; + } + @Override public void process() { Account accountFrom = AccountAggregate.getAccount(accountNoFrom); @@ -33,24 +43,8 @@ public class MoneyTransferEvent extends DomainEvent { if(accountTo==null){ throw new RuntimeException("Account not found"+ accountTo); } - if(accountFrom.getMoney().compareTo(money)==-1){ - throw new RuntimeException("Insufficient Account Balance"); - } - accountFrom.setMoney(accountFrom.getMoney().subtract(money)); - accountTo.setMoney(accountTo.getMoney().add(money)); - Transaction transactionFrom = new Transaction(accountNoFrom,BigDecimal.ZERO,money,accountFrom.getMoney()); - accountFrom.getTransactions().add(transactionFrom); - - Transaction transactionTo = new Transaction(accountNoTo,money,BigDecimal.ZERO,accountTo.getMoney()); - accountTo.getTransactions().add(transactionTo); - - AccountAggregate.putAccount(accountFrom); - AccountAggregate.putAccount(accountTo); - - if(!isReplica()) { - Gateways.getTransactionLogger().log(transactionFrom); - Gateways.getTransactionLogger().log(transactionTo); - } + accountFrom.handleTransferFromEvent(this); + accountTo.handleTransferToEvent(this); } } diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyWithdrawalEvent.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyWithdrawalEvent.java index 27a63d13d..8ed617008 100644 --- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyWithdrawalEvent.java +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyWithdrawalEvent.java @@ -2,8 +2,6 @@ package com.iluwatar.event.sourcing.event; import com.iluwatar.event.sourcing.api.DomainEvent; import com.iluwatar.event.sourcing.domain.Account; -import com.iluwatar.event.sourcing.domain.Transaction; -import com.iluwatar.event.sourcing.gateway.Gateways; import com.iluwatar.event.sourcing.state.AccountAggregate; import java.math.BigDecimal; @@ -21,18 +19,20 @@ public class MoneyWithdrawalEvent extends DomainEvent { this.accountNo = accountNo; } + public BigDecimal getMoney() { + return money; + } + + public int getAccountNo() { + return accountNo; + } + @Override public void process() { Account account = AccountAggregate.getAccount(accountNo); if(account==null){ throw new RuntimeException("Account not found"); } - account.setMoney(account.getMoney().subtract(money)); - Transaction transaction = new Transaction(accountNo,BigDecimal.ZERO,money,account.getMoney()); - account.getTransactions().add(transaction); - AccountAggregate.putAccount(account); - if(!isReplica()) { - Gateways.getTransactionLogger().log(transaction); - } + account.handleEvent(this); } } diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/journal/JsonFileJournal.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/journal/JsonFileJournal.java index 45a982c19..2d50db55f 100644 --- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/journal/JsonFileJournal.java +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/journal/JsonFileJournal.java @@ -93,7 +93,7 @@ public class JsonFileJournal implements ProcessorJournal{ throw new RuntimeException("Journal Event not recegnized"); } - domainEvent.setReplica(true); + domainEvent.setRealTime(false); return domainEvent; } } diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/processor/DomainEventProcessor.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/processor/DomainEventProcessor.java index a23c41905..55453b1c6 100644 --- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/processor/DomainEventProcessor.java +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/processor/DomainEventProcessor.java @@ -2,23 +2,17 @@ package com.iluwatar.event.sourcing.processor; import com.iluwatar.event.sourcing.api.DomainEvent; import com.iluwatar.event.sourcing.api.EventProcessor; -import com.iluwatar.event.sourcing.api.ExternalEventListener; import com.iluwatar.event.sourcing.api.ProcessorJournal; -import java.util.ArrayList; -import java.util.List; - /** * Created by serdarh on 06.08.2017. */ public class DomainEventProcessor implements EventProcessor { private ProcessorJournal precessorJournal; - private List externalEventListeners = new ArrayList<>(); @Override public void process(DomainEvent domainEvent) { - externalEventListeners.forEach(externalEventListener -> externalEventListener.notify(domainEvent)); domainEvent.process(); precessorJournal.write(domainEvent); } @@ -28,11 +22,6 @@ public class DomainEventProcessor implements EventProcessor { this.precessorJournal = precessorJournal; } - @Override - public void addExternalEventListener(ExternalEventListener externalEventListener) { - externalEventListeners.add(externalEventListener); - } - @Override public void recover() { DomainEvent domainEvent; From 4b3435c550def35d2b55cdb72ffa44410efca7ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Serdar=20Hamzao=C4=9Fullar=C4=B1?= Date: Sun, 13 Aug 2017 00:08:35 +0300 Subject: [PATCH 4/7] Code formating --- event-sourcing/etc/.gitkeep | 0 event-sourcing/etc/event-sourcing.urm.puml | 184 +++++++++++ event-sourcing/src/.gitkeep | 0 .../event/sourcing/api/DomainEvent.java | 108 +++++-- .../event/sourcing/api/EventProcessor.java | 44 ++- .../event/sourcing/api/ProcessorJournal.java | 44 ++- .../com/iluwatar/event/sourcing/app/App.java | 89 ++++-- .../event/sourcing/domain/Account.java | 295 ++++++++++++------ .../event/sourcing/domain/Transaction.java | 114 +++++-- .../sourcing/event/AccountCreateEvent.java | 83 +++-- .../sourcing/event/MoneyDepositEvent.java | 82 +++-- .../sourcing/event/MoneyTransferEvent.java | 117 ++++--- .../sourcing/event/MoneyWithdrawalEvent.java | 82 +++-- .../gateway/AccountCreateContractSender.java | 34 +- .../event/sourcing/gateway/Gateways.java | 49 ++- .../sourcing/gateway/TransactionLogger.java | 34 +- .../sourcing/journal/JsonFileJournal.java | 183 ++++++----- .../processor/DomainEventProcessor.java | 64 ++-- .../sourcing/service/AccountService.java | 52 ++- .../service/MoneyTransactionService.java | 84 ++++- .../sourcing/service/SequenceIdGenerator.java | 38 ++- .../sourcing/state/AccountAggregate.java | 64 +++- 22 files changed, 1396 insertions(+), 448 deletions(-) delete mode 100644 event-sourcing/etc/.gitkeep create mode 100644 event-sourcing/etc/event-sourcing.urm.puml delete mode 100644 event-sourcing/src/.gitkeep diff --git a/event-sourcing/etc/.gitkeep b/event-sourcing/etc/.gitkeep deleted file mode 100644 index e69de29bb..000000000 diff --git a/event-sourcing/etc/event-sourcing.urm.puml b/event-sourcing/etc/event-sourcing.urm.puml new file mode 100644 index 000000000..207d238e6 --- /dev/null +++ b/event-sourcing/etc/event-sourcing.urm.puml @@ -0,0 +1,184 @@ +@startuml +package com.iluwatar.event.sourcing.journal { + class JsonFileJournal { + - aFile : File + - events : List + - index : int + + JsonFileJournal() + + readNext() : DomainEvent + + reset() + + write(domainEvent : DomainEvent) + } +} +package com.iluwatar.event.sourcing.processor { + class DomainEventProcessor { + - precessorJournal : ProcessorJournal + + DomainEventProcessor() + + process(domainEvent : DomainEvent) + + recover() + + setPrecessorJournal(precessorJournal : ProcessorJournal) + } +} +package com.iluwatar.event.sourcing.service { + class AccountService { + - eventProcessor : EventProcessor + + AccountService(eventProcessor : EventProcessor) + + createAccount(accountNo : int, owner : String) + } + class MoneyTransactionService { + - eventProcessor : EventProcessor + + MoneyTransactionService(eventProcessor : EventProcessor) + + depositMoney(accountNo : int, money : BigDecimal) + + transferMoney(accountNoFrom : int, accountNoTo : int, money : BigDecimal) + + withdrawalMoney(accountNo : int, money : BigDecimal) + } + class SequenceIdGenerator { + - sequenceId : long {static} + + SequenceIdGenerator() + + nextSequenceId() : long {static} + } +} +package com.iluwatar.event.sourcing.event { + class AccountCreateEvent { + - accountNo : int + - owner : String + + AccountCreateEvent(sequenceId : long, createdTime : long, accountNo : int, owner : String) + + getAccountNo() : int + + getOwner() : String + + process() + } + class MoneyDepositEvent { + - accountNo : int + - money : BigDecimal + + MoneyDepositEvent(sequenceId : long, createdTime : long, accountNo : int, money : BigDecimal) + + getAccountNo() : int + + getMoney() : BigDecimal + + process() + } + class MoneyTransferEvent { + - accountNoFrom : int + - accountNoTo : int + - money : BigDecimal + + MoneyTransferEvent(sequenceId : long, createdTime : long, money : BigDecimal, accountNoFrom : int, accountNoTo : int) + + getAccountNoFrom() : int + + getAccountNoTo() : int + + getMoney() : BigDecimal + + process() + } + class MoneyWithdrawalEvent { + - accountNo : int + - money : BigDecimal + + MoneyWithdrawalEvent(sequenceId : long, createdTime : long, accountNo : int, money : BigDecimal) + + getAccountNo() : int + + getMoney() : BigDecimal + + process() + } +} +package com.iluwatar.event.sourcing.gateway { + class AccountCreateContractSender { + + AccountCreateContractSender() + + sendContractInfo(account : Account) + } + class Gateways { + - accountCreateContractSender : AccountCreateContractSender {static} + - transactionLogger : TransactionLogger {static} + + Gateways() + + getAccountCreateContractSender() : AccountCreateContractSender {static} + + getTransactionLogger() : TransactionLogger {static} + } + class TransactionLogger { + + TransactionLogger() + + log(transaction : Transaction) + } +} +package com.iluwatar.event.sourcing.app { + class App { + + App() + + main(args : String[]) {static} + } +} +package com.iluwatar.event.sourcing.state { + class AccountAggregate { + - accounts : Map {static} + + AccountAggregate() + + getAccount(accountNo : int) : Account {static} + + putAccount(account : Account) {static} + + resetState() {static} + } +} +package com.iluwatar.event.sourcing.domain { + class Account { + - accountNo : int + - money : BigDecimal + - owner : String + - transactions : List + + Account(accountNo : int, owner : String) + + copy() : Account + - depositMoney(money : BigDecimal) : Transaction + + getAccountNo() : int + + getMoney() : BigDecimal + + getOwner() : String + + getTransactions() : List + - handleDeposit(money : BigDecimal, realTime : boolean) + + handleEvent(accountCreateEvent : AccountCreateEvent) + + handleEvent(moneyDepositEvent : MoneyDepositEvent) + + handleEvent(moneyWithdrawalEvent : MoneyWithdrawalEvent) + + handleTransferFromEvent(moneyTransferEvent : MoneyTransferEvent) + + handleTransferToEvent(moneyTransferEvent : MoneyTransferEvent) + - handleWithdrawal(money : BigDecimal, realTime : boolean) + + setMoney(money : BigDecimal) + + setTransactions(transactions : List) + + toString() : String + - withdrawMoney(money : BigDecimal) : Transaction + } + class Transaction { + - accountNo : int + - lastBalance : BigDecimal + - moneyIn : BigDecimal + - moneyOut : BigDecimal + + Transaction(accountNo : int, moneyIn : BigDecimal, moneyOut : BigDecimal, lastBalance : BigDecimal) + + getAccountNo() : int + + getLastBalance() : BigDecimal + + getMoneyIn() : BigDecimal + + getMoneyOut() : BigDecimal + + toString() : String + } +} +package com.iluwatar.event.sourcing.api { + abstract class DomainEvent { + - createdTime : long + - eventClassName : String + - realTime : boolean + - sequenceId : long + + DomainEvent(sequenceId : long, createdTime : long, eventClassName : String) + + getCreatedTime() : long + + getEventClassName() : String + + getSequenceId() : long + + isRealTime() : boolean + + process() {abstract} + + setRealTime(realTime : boolean) + } + interface EventProcessor { + + process(DomainEvent) {abstract} + + recover() {abstract} + + setPrecessorJournal(ProcessorJournal) {abstract} + } + interface ProcessorJournal { + + readNext() : DomainEvent {abstract} + + reset() {abstract} + + write(DomainEvent) {abstract} + } +} +Gateways --> "-accountCreateContractSender" AccountCreateContractSender +DomainEventProcessor --> "-precessorJournal" ProcessorJournal +Account --> "-transactions" Transaction +Gateways --> "-transactionLogger" TransactionLogger +AccountService --> "-eventProcessor" EventProcessor +MoneyTransactionService --> "-eventProcessor" EventProcessor +AccountCreateEvent --|> DomainEvent +MoneyDepositEvent --|> DomainEvent +MoneyTransferEvent --|> DomainEvent +MoneyWithdrawalEvent --|> DomainEvent +JsonFileJournal ..|> ProcessorJournal +DomainEventProcessor ..|> EventProcessor +@enduml \ No newline at end of file diff --git a/event-sourcing/src/.gitkeep b/event-sourcing/src/.gitkeep deleted file mode 100644 index e69de29bb..000000000 diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/api/DomainEvent.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/api/DomainEvent.java index e20d03232..693ea1755 100644 --- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/api/DomainEvent.java +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/api/DomainEvent.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.event.sourcing.api; import java.io.Serializable; @@ -6,36 +28,72 @@ import java.io.Serializable; * Created by serdarh on 06.08.2017. */ public abstract class DomainEvent implements Serializable { - private final long sequenceId; - private final long createdTime; - private boolean realTime = true; - private final String eventClassName; - public DomainEvent(long sequenceId, long createdTime, String eventClassName) { - this.sequenceId = sequenceId; - this.createdTime = createdTime; - this.eventClassName = eventClassName; - } + private final long sequenceId; + private final long createdTime; + private final String eventClassName; + private boolean realTime = true; - public long getSequenceId() { - return sequenceId; - } + /** + * Instantiates a new Domain event. + * + * @param sequenceId the sequence id + * @param createdTime the created time + * @param eventClassName the event class name + */ + public DomainEvent(long sequenceId, long createdTime, String eventClassName) { + this.sequenceId = sequenceId; + this.createdTime = createdTime; + this.eventClassName = eventClassName; + } - public long getCreatedTime() { - return createdTime; - } + /** + * Gets sequence id. + * + * @return the sequence id + */ + public long getSequenceId() { + return sequenceId; + } - public boolean isRealTime() { - return realTime; - } + /** + * Gets created time. + * + * @return the created time + */ + public long getCreatedTime() { + return createdTime; + } - public void setRealTime(boolean realTime) { - this.realTime = realTime; - } + /** + * Is real time boolean. + * + * @return the boolean + */ + public boolean isRealTime() { + return realTime; + } - public abstract void process(); + /** + * Sets real time. + * + * @param realTime the real time + */ + public void setRealTime(boolean realTime) { + this.realTime = realTime; + } - public String getEventClassName() { - return eventClassName; - } + /** + * Process. + */ + public abstract void process(); + + /** + * Gets event class name. + * + * @return the event class name + */ + public String getEventClassName() { + return eventClassName; + } } diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/api/EventProcessor.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/api/EventProcessor.java index 0fc673bf4..afa218939 100644 --- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/api/EventProcessor.java +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/api/EventProcessor.java @@ -1,10 +1,48 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.event.sourcing.api; /** * Created by serdarh on 06.08.2017. */ public interface EventProcessor { - void process(DomainEvent domainEvent); - void setPrecessorJournal(ProcessorJournal precessorJournal); - void recover(); + + /** + * Process. + * + * @param domainEvent the domain event + */ + void process(DomainEvent domainEvent); + + /** + * Sets precessor journal. + * + * @param precessorJournal the precessor journal + */ + void setPrecessorJournal(ProcessorJournal precessorJournal); + + /** + * Recover. + */ + void recover(); } diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/api/ProcessorJournal.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/api/ProcessorJournal.java index 906c66247..8814b82d9 100644 --- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/api/ProcessorJournal.java +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/api/ProcessorJournal.java @@ -1,10 +1,48 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.event.sourcing.api; /** * Created by serdarh on 06.08.2017. */ public interface ProcessorJournal { - void write(DomainEvent domainEvent); - void reset(); - DomainEvent readNext(); + + /** + * Write. + * + * @param domainEvent the domain event + */ + void write(DomainEvent domainEvent); + + /** + * Reset. + */ + void reset(); + + /** + * Read next domain event. + * + * @return the domain event + */ + DomainEvent readNext(); } diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/app/App.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/app/App.java index 8627736f1..f62cebb62 100644 --- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/app/App.java +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/app/App.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.event.sourcing.app; import com.iluwatar.event.sourcing.journal.JsonFileJournal; @@ -5,7 +27,6 @@ import com.iluwatar.event.sourcing.processor.DomainEventProcessor; import com.iluwatar.event.sourcing.service.AccountService; import com.iluwatar.event.sourcing.service.MoneyTransactionService; import com.iluwatar.event.sourcing.state.AccountAggregate; - import java.math.BigDecimal; /** @@ -13,46 +34,52 @@ import java.math.BigDecimal; */ public class App { - public static void main(String[] args) { - System.out.println("Running the system first time............"); + /** + * The entry point of application. + * + * @param args the input arguments + */ + public static void main(String[] args) { + System.out.println("Running the system first time............"); - DomainEventProcessor domainEventProcessor = new DomainEventProcessor(); - JsonFileJournal jsonFileJournal = new JsonFileJournal(); - jsonFileJournal.reset(); - domainEventProcessor.setPrecessorJournal(jsonFileJournal); + DomainEventProcessor domainEventProcessor = new DomainEventProcessor(); + JsonFileJournal jsonFileJournal = new JsonFileJournal(); + jsonFileJournal.reset(); + domainEventProcessor.setPrecessorJournal(jsonFileJournal); - System.out.println("Creating th accounts............"); + System.out.println("Creating th accounts............"); - AccountService accountService = new AccountService(domainEventProcessor); - MoneyTransactionService moneyTransactionService = new MoneyTransactionService(domainEventProcessor); - accountService.createAccount(1,"Daenerys Targaryen"); - accountService.createAccount(2,"Jon Snow"); + AccountService accountService = new AccountService(domainEventProcessor); + MoneyTransactionService moneyTransactionService = new MoneyTransactionService( + domainEventProcessor); + accountService.createAccount(1, "Daenerys Targaryen"); + accountService.createAccount(2, "Jon Snow"); - System.out.println("Do some money operations............"); + System.out.println("Do some money operations............"); - moneyTransactionService.depositMoney(1,new BigDecimal("100000")); - moneyTransactionService.depositMoney(2,new BigDecimal("10")); + moneyTransactionService.depositMoney(1, new BigDecimal("100000")); + moneyTransactionService.depositMoney(2, new BigDecimal("100")); - moneyTransactionService.transferMoney(1,2,new BigDecimal("10000")); - moneyTransactionService.withdrawalMoney(2, new BigDecimal("1000")); + moneyTransactionService.transferMoney(1, 2, new BigDecimal("10000")); + moneyTransactionService.withdrawalMoney(2, new BigDecimal("1000")); - System.out.println("...............State:............"); - System.out.println(AccountAggregate.getAccount(1)); - System.out.println(AccountAggregate.getAccount(2)); + System.out.println("...............State:............"); + System.out.println(AccountAggregate.getAccount(1)); + System.out.println(AccountAggregate.getAccount(2)); - System.out.println("At that point system goes down state in memory cleared............"); + System.out.println("At that point system goes down state in memory cleared............"); - AccountAggregate.resetState(); + AccountAggregate.resetState(); - System.out.println("Recover the syste by the events in journal file............"); + System.out.println("Recover the syste by the events in journal file............"); - domainEventProcessor = new DomainEventProcessor(); - jsonFileJournal = new JsonFileJournal(); - domainEventProcessor.setPrecessorJournal(jsonFileJournal); - domainEventProcessor.recover(); + domainEventProcessor = new DomainEventProcessor(); + jsonFileJournal = new JsonFileJournal(); + domainEventProcessor.setPrecessorJournal(jsonFileJournal); + domainEventProcessor.recover(); - System.out.println("...............State Recovered:............"); - System.out.println(AccountAggregate.getAccount(1)); - System.out.println(AccountAggregate.getAccount(2)); - } + System.out.println("...............State Recovered:............"); + System.out.println(AccountAggregate.getAccount(1)); + System.out.println(AccountAggregate.getAccount(2)); + } } diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/domain/Account.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/domain/Account.java index 48cda1eca..9e1bc560e 100644 --- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/domain/Account.java +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/domain/Account.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.event.sourcing.domain; import com.iluwatar.event.sourcing.event.AccountCreateEvent; @@ -6,7 +28,6 @@ import com.iluwatar.event.sourcing.event.MoneyTransferEvent; import com.iluwatar.event.sourcing.event.MoneyWithdrawalEvent; import com.iluwatar.event.sourcing.gateway.Gateways; import com.iluwatar.event.sourcing.state.AccountAggregate; - import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; @@ -15,116 +36,184 @@ import java.util.List; * Created by serdarh on 06.08.2017. */ public class Account { - private final int accountNo; - private final String owner; - private BigDecimal money; - private List transactions; - public Account(int accountNo, String owner) { - this.accountNo = accountNo; - this.owner = owner; - money = BigDecimal.ZERO; - transactions = new ArrayList<>(); + private final int accountNo; + private final String owner; + private BigDecimal money; + private List transactions; + + /** + * Instantiates a new Account. + * + * @param accountNo the account no + * @param owner the owner + */ + public Account(int accountNo, String owner) { + this.accountNo = accountNo; + this.owner = owner; + money = BigDecimal.ZERO; + transactions = new ArrayList<>(); + } + + /** + * Gets account no. + * + * @return the account no + */ + public int getAccountNo() { + return accountNo; + } + + /** + * Gets owner. + * + * @return the owner + */ + public String getOwner() { + return owner; + } + + /** + * Gets money. + * + * @return the money + */ + public BigDecimal getMoney() { + return money; + } + + /** + * Sets money. + * + * @param money the money + */ + public void setMoney(BigDecimal money) { + this.money = money; + } + + /** + * Gets transactions. + * + * @return the transactions + */ + public List getTransactions() { + return transactions; + } + + /** + * Sets transactions. + * + * @param transactions the transactions + */ + public void setTransactions(List transactions) { + this.transactions = transactions; + } + + /** + * Copy account. + * + * @return the account + */ + public Account copy() { + Account account = new Account(accountNo, owner); + account.setMoney(money); + account.setTransactions(transactions); + return account; + } + + @Override + public String toString() { + return "Account{" + + "accountNo=" + accountNo + + ", owner='" + owner + '\'' + + ", money=" + money + + ", transactions=" + transactions + + '}'; + } + + private Transaction depositMoney(BigDecimal money) { + this.money = this.money.add(money); + Transaction transaction = new Transaction(accountNo, money, BigDecimal.ZERO, this.money); + transactions.add(transaction); + return transaction; + } + + private Transaction withdrawMoney(BigDecimal money) { + this.money = this.money.subtract(money); + Transaction transaction = new Transaction(accountNo, BigDecimal.ZERO, money, this.money); + transactions.add(transaction); + return transaction; + } + + private void handleDeposit(BigDecimal money, boolean realTime) { + Transaction transaction = depositMoney(money); + AccountAggregate.putAccount(this); + if (realTime) { + Gateways.getTransactionLogger().log(transaction); + } + } + + private void handleWithdrawal(BigDecimal money, boolean realTime) { + if (this.money.compareTo(money) == -1) { + throw new RuntimeException("Insufficient Account Balance"); } - public int getAccountNo() { - return accountNo; + Transaction transaction = withdrawMoney(money); + AccountAggregate.putAccount(this); + if (realTime) { + Gateways.getTransactionLogger().log(transaction); } + } - public String getOwner() { - return owner; + /** + * Handle event. + * + * @param moneyDepositEvent the money deposit event + */ + public void handleEvent(MoneyDepositEvent moneyDepositEvent) { + handleDeposit(moneyDepositEvent.getMoney(), moneyDepositEvent.isRealTime()); + } + + + /** + * Handle event. + * + * @param moneyWithdrawalEvent the money withdrawal event + */ + public void handleEvent(MoneyWithdrawalEvent moneyWithdrawalEvent) { + handleWithdrawal(moneyWithdrawalEvent.getMoney(), moneyWithdrawalEvent.isRealTime()); + } + + /** + * Handle event. + * + * @param accountCreateEvent the account create event + */ + public void handleEvent(AccountCreateEvent accountCreateEvent) { + AccountAggregate.putAccount(this); + // check if this event is replicated from journal before calling an external gateway function + if (accountCreateEvent.isRealTime()) { + Gateways.getAccountCreateContractSender().sendContractInfo(this); } + } - public BigDecimal getMoney() { - return money; - } + /** + * Handle transfer from event. + * + * @param moneyTransferEvent the money transfer event + */ + public void handleTransferFromEvent(MoneyTransferEvent moneyTransferEvent) { + handleWithdrawal(moneyTransferEvent.getMoney(), moneyTransferEvent.isRealTime()); + } - public List getTransactions() { - return transactions; - } - - public void setMoney(BigDecimal money) { - this.money = money; - } - - public void setTransactions(List transactions) { - this.transactions = transactions; - } - - public Account copy() { - Account account = new Account(accountNo, owner); - account.setMoney(money); - account.setTransactions(transactions); - return account; - } - - @Override - public String toString() { - return "Account{" + - "accountNo=" + accountNo + - ", owner='" + owner + '\'' + - ", money=" + money + - ", transactions=" + transactions + - '}'; - } - - private Transaction depositMoney(BigDecimal money) { - this.money = this.money.add(money); - Transaction transaction = new Transaction(accountNo,money,BigDecimal.ZERO,this.money); - transactions.add(transaction); - return transaction; - } - - private Transaction withdrawMoney(BigDecimal money) { - this.money = this.money.subtract(money); - Transaction transaction = new Transaction(accountNo,BigDecimal.ZERO,money,this.money); - transactions.add(transaction); - return transaction; - } - - private void handleDeposit(BigDecimal money,boolean realTime) { - Transaction transaction = depositMoney(money); - AccountAggregate.putAccount(this); - if(realTime) { - Gateways.getTransactionLogger().log(transaction); - } - } - - private void handleWithdrawal(BigDecimal money, boolean realTime) { - if(this.money.compareTo(money)==-1){ - throw new RuntimeException("Insufficient Account Balance"); - } - - Transaction transaction = withdrawMoney(money); - AccountAggregate.putAccount(this); - if(realTime) { - Gateways.getTransactionLogger().log(transaction); - } - } - - public void handleEvent(MoneyDepositEvent moneyDepositEvent) { - handleDeposit(moneyDepositEvent.getMoney(),moneyDepositEvent.isRealTime()); - } + /** + * Handle transfer to event. + * + * @param moneyTransferEvent the money transfer event + */ + public void handleTransferToEvent(MoneyTransferEvent moneyTransferEvent) { + handleDeposit(moneyTransferEvent.getMoney(), moneyTransferEvent.isRealTime()); + } - public void handleEvent(MoneyWithdrawalEvent moneyWithdrawalEvent) { - handleWithdrawal(moneyWithdrawalEvent.getMoney(),moneyWithdrawalEvent.isRealTime()); - } - - - public void handleTransferFromEvent(MoneyTransferEvent moneyTransferEvent) { - handleWithdrawal(moneyTransferEvent.getMoney(),moneyTransferEvent.isRealTime()); - } - - public void handleTransferToEvent(MoneyTransferEvent moneyTransferEvent) { - handleDeposit(moneyTransferEvent.getMoney(),moneyTransferEvent.isRealTime()); - } - - public void handleEvent(AccountCreateEvent accountCreateEvent) { - AccountAggregate.putAccount(this); - // check if this event is replicated from journal before calling an external gateway function - if(accountCreateEvent.isRealTime()) { - Gateways.getAccountCreateContractSender().sendContractInfo(this); - } - } } diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/domain/Transaction.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/domain/Transaction.java index 557efbedc..29cdc4d15 100644 --- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/domain/Transaction.java +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/domain/Transaction.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.event.sourcing.domain; import java.math.BigDecimal; @@ -6,41 +28,71 @@ import java.math.BigDecimal; * Created by serdarh on 06.08.2017. */ public class Transaction { - private final int accountNo; - private final BigDecimal moneyIn; - private final BigDecimal moneyOut; - private final BigDecimal lastBalance; - public Transaction(int accountNo, BigDecimal moneyIn, BigDecimal moneyOut, BigDecimal lastBalance) { - this.accountNo = accountNo; - this.moneyIn = moneyIn; - this.moneyOut = moneyOut; - this.lastBalance = lastBalance; - } + private final int accountNo; + private final BigDecimal moneyIn; + private final BigDecimal moneyOut; + private final BigDecimal lastBalance; - public int getAccountNo() { - return accountNo; - } + /** + * Instantiates a new Transaction. + * + * @param accountNo the account no + * @param moneyIn the money in + * @param moneyOut the money out + * @param lastBalance the last balance + */ + public Transaction(int accountNo, BigDecimal moneyIn, BigDecimal moneyOut, + BigDecimal lastBalance) { + this.accountNo = accountNo; + this.moneyIn = moneyIn; + this.moneyOut = moneyOut; + this.lastBalance = lastBalance; + } - public BigDecimal getMoneyIn() { - return moneyIn; - } + /** + * Gets account no. + * + * @return the account no + */ + public int getAccountNo() { + return accountNo; + } - public BigDecimal getMoneyOut() { - return moneyOut; - } + /** + * Gets money in. + * + * @return the money in + */ + public BigDecimal getMoneyIn() { + return moneyIn; + } - public BigDecimal getLastBalance() { - return lastBalance; - } + /** + * Gets money out. + * + * @return the money out + */ + public BigDecimal getMoneyOut() { + return moneyOut; + } - @Override - public String toString() { - return "Transaction{" + - "accountNo=" + accountNo + - ", moneyIn=" + moneyIn + - ", moneyOut=" + moneyOut + - ", lastBalance=" + lastBalance + - '}'; - } + /** + * Gets last balance. + * + * @return the last balance + */ + public BigDecimal getLastBalance() { + return lastBalance; + } + + @Override + public String toString() { + return "Transaction{" + + "accountNo=" + accountNo + + ", moneyIn=" + moneyIn + + ", moneyOut=" + moneyOut + + ", lastBalance=" + lastBalance + + '}'; + } } diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/AccountCreateEvent.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/AccountCreateEvent.java index 1ea089e2f..8356cd3e8 100644 --- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/AccountCreateEvent.java +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/AccountCreateEvent.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.event.sourcing.event; import com.iluwatar.event.sourcing.api.DomainEvent; @@ -8,30 +30,49 @@ import com.iluwatar.event.sourcing.state.AccountAggregate; * Created by serdarh on 06.08.2017. */ public class AccountCreateEvent extends DomainEvent { - private final int accountNo; - private final String owner; - public AccountCreateEvent(long sequenceId, long createdTime, int accountNo, String owner) { - super(sequenceId, createdTime, "AccountCreateEvent"); - this.accountNo = accountNo; - this.owner = owner; - } + private final int accountNo; + private final String owner; - public int getAccountNo() { - return accountNo; - } + /** + * Instantiates a new Account create event. + * + * @param sequenceId the sequence id + * @param createdTime the created time + * @param accountNo the account no + * @param owner the owner + */ + public AccountCreateEvent(long sequenceId, long createdTime, int accountNo, String owner) { + super(sequenceId, createdTime, "AccountCreateEvent"); + this.accountNo = accountNo; + this.owner = owner; + } - public String getOwner() { - return owner; - } + /** + * Gets account no. + * + * @return the account no + */ + public int getAccountNo() { + return accountNo; + } - @Override - public void process() { - Account account = AccountAggregate.getAccount(accountNo); - if(account!=null){ - throw new RuntimeException("Account already exists"); - } - account = new Account(accountNo,owner); - account.handleEvent(this); + /** + * Gets owner. + * + * @return the owner + */ + public String getOwner() { + return owner; + } + + @Override + public void process() { + Account account = AccountAggregate.getAccount(accountNo); + if (account != null) { + throw new RuntimeException("Account already exists"); } + account = new Account(accountNo, owner); + account.handleEvent(this); + } } diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyDepositEvent.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyDepositEvent.java index 384a9e198..9d73639eb 100644 --- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyDepositEvent.java +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyDepositEvent.java @@ -1,38 +1,78 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.event.sourcing.event; import com.iluwatar.event.sourcing.api.DomainEvent; import com.iluwatar.event.sourcing.domain.Account; import com.iluwatar.event.sourcing.state.AccountAggregate; - import java.math.BigDecimal; /** * Created by serdarh on 06.08.2017. */ public class MoneyDepositEvent extends DomainEvent { - private BigDecimal money; - private int accountNo; - public MoneyDepositEvent(long sequenceId, long createdTime, int accountNo,BigDecimal money) { - super(sequenceId, createdTime, "MoneyDepositEvent"); - this.money = money; - this.accountNo = accountNo; - } + private BigDecimal money; + private int accountNo; - public BigDecimal getMoney() { - return money; - } + /** + * Instantiates a new Money deposit event. + * + * @param sequenceId the sequence id + * @param createdTime the created time + * @param accountNo the account no + * @param money the money + */ + public MoneyDepositEvent(long sequenceId, long createdTime, int accountNo, BigDecimal money) { + super(sequenceId, createdTime, "MoneyDepositEvent"); + this.money = money; + this.accountNo = accountNo; + } - public int getAccountNo() { - return accountNo; - } + /** + * Gets money. + * + * @return the money + */ + public BigDecimal getMoney() { + return money; + } - @Override - public void process() { - Account account = AccountAggregate.getAccount(accountNo); - if(account==null){ - throw new RuntimeException("Account not found"); - } - account.handleEvent(this); + /** + * Gets account no. + * + * @return the account no + */ + public int getAccountNo() { + return accountNo; + } + + @Override + public void process() { + Account account = AccountAggregate.getAccount(accountNo); + if (account == null) { + throw new RuntimeException("Account not found"); } + account.handleEvent(this); + } } diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyTransferEvent.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyTransferEvent.java index 6c873d4db..995fb9326 100644 --- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyTransferEvent.java +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyTransferEvent.java @@ -1,50 +1,97 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.event.sourcing.event; import com.iluwatar.event.sourcing.api.DomainEvent; import com.iluwatar.event.sourcing.domain.Account; import com.iluwatar.event.sourcing.state.AccountAggregate; - import java.math.BigDecimal; /** * Created by serdarh on 06.08.2017. */ public class MoneyTransferEvent extends DomainEvent { - private BigDecimal money; - private int accountNoFrom; - private int accountNoTo; - public MoneyTransferEvent(long sequenceId, long createdTime, BigDecimal money, int accountNoFrom, int accountNoTo) { - super(sequenceId, createdTime, "MoneyTransferEvent"); - this.money = money; - this.accountNoFrom = accountNoFrom; - this.accountNoTo = accountNoTo; + private BigDecimal money; + private int accountNoFrom; + private int accountNoTo; + + /** + * Instantiates a new Money transfer event. + * + * @param sequenceId the sequence id + * @param createdTime the created time + * @param money the money + * @param accountNoFrom the account no from + * @param accountNoTo the account no to + */ + public MoneyTransferEvent(long sequenceId, long createdTime, BigDecimal money, int accountNoFrom, + int accountNoTo) { + super(sequenceId, createdTime, "MoneyTransferEvent"); + this.money = money; + this.accountNoFrom = accountNoFrom; + this.accountNoTo = accountNoTo; + } + + /** + * Gets money. + * + * @return the money + */ + public BigDecimal getMoney() { + return money; + } + + /** + * Gets account no from. + * + * @return the account no from + */ + public int getAccountNoFrom() { + return accountNoFrom; + } + + /** + * Gets account no to. + * + * @return the account no to + */ + public int getAccountNoTo() { + return accountNoTo; + } + + @Override + public void process() { + Account accountFrom = AccountAggregate.getAccount(accountNoFrom); + if (accountFrom == null) { + throw new RuntimeException("Account not found " + accountNoFrom); + } + Account accountTo = AccountAggregate.getAccount(accountNoTo); + if (accountTo == null) { + throw new RuntimeException("Account not found" + accountTo); } - public BigDecimal getMoney() { - return money; - } - - public int getAccountNoFrom() { - return accountNoFrom; - } - - public int getAccountNoTo() { - return accountNoTo; - } - - @Override - public void process() { - Account accountFrom = AccountAggregate.getAccount(accountNoFrom); - if(accountFrom==null){ - throw new RuntimeException("Account not found "+accountNoFrom); - } - Account accountTo = AccountAggregate.getAccount(accountNoTo); - if(accountTo==null){ - throw new RuntimeException("Account not found"+ accountTo); - } - - accountFrom.handleTransferFromEvent(this); - accountTo.handleTransferToEvent(this); - } + accountFrom.handleTransferFromEvent(this); + accountTo.handleTransferToEvent(this); + } } diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyWithdrawalEvent.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyWithdrawalEvent.java index 8ed617008..64fddc16e 100644 --- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyWithdrawalEvent.java +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyWithdrawalEvent.java @@ -1,38 +1,78 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.event.sourcing.event; import com.iluwatar.event.sourcing.api.DomainEvent; import com.iluwatar.event.sourcing.domain.Account; import com.iluwatar.event.sourcing.state.AccountAggregate; - import java.math.BigDecimal; /** * Created by serdarh on 06.08.2017. */ public class MoneyWithdrawalEvent extends DomainEvent { - private BigDecimal money; - private int accountNo; - public MoneyWithdrawalEvent(long sequenceId, long createdTime, int accountNo,BigDecimal money) { - super(sequenceId, createdTime, "MoneyWithdrawalEvent"); - this.money = money; - this.accountNo = accountNo; - } + private BigDecimal money; + private int accountNo; - public BigDecimal getMoney() { - return money; - } + /** + * Instantiates a new Money withdrawal event. + * + * @param sequenceId the sequence id + * @param createdTime the created time + * @param accountNo the account no + * @param money the money + */ + public MoneyWithdrawalEvent(long sequenceId, long createdTime, int accountNo, BigDecimal money) { + super(sequenceId, createdTime, "MoneyWithdrawalEvent"); + this.money = money; + this.accountNo = accountNo; + } - public int getAccountNo() { - return accountNo; - } + /** + * Gets money. + * + * @return the money + */ + public BigDecimal getMoney() { + return money; + } - @Override - public void process() { - Account account = AccountAggregate.getAccount(accountNo); - if(account==null){ - throw new RuntimeException("Account not found"); - } - account.handleEvent(this); + /** + * Gets account no. + * + * @return the account no + */ + public int getAccountNo() { + return accountNo; + } + + @Override + public void process() { + Account account = AccountAggregate.getAccount(accountNo); + if (account == null) { + throw new RuntimeException("Account not found"); } + account.handleEvent(this); + } } diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/gateway/AccountCreateContractSender.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/gateway/AccountCreateContractSender.java index 42dfccf7b..aa15f746f 100644 --- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/gateway/AccountCreateContractSender.java +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/gateway/AccountCreateContractSender.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.event.sourcing.gateway; import com.iluwatar.event.sourcing.domain.Account; @@ -6,7 +28,13 @@ import com.iluwatar.event.sourcing.domain.Account; * Created by serdarh on 06.08.2017. */ public class AccountCreateContractSender { - public void sendContractInfo(Account account){ - // an example imaginary funciton which sends account info to some external end point - } + + /** + * Send contract info. + * + * @param account the account + */ + public void sendContractInfo(Account account) { + // an example imaginary funciton which sends account info to some external end point + } } diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/gateway/Gateways.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/gateway/Gateways.java index d167393bf..932338c32 100644 --- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/gateway/Gateways.java +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/gateway/Gateways.java @@ -1,17 +1,50 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.event.sourcing.gateway; /** * Created by serdarh on 06.08.2017. */ public class Gateways { - private static AccountCreateContractSender accountCreateContractSender = new AccountCreateContractSender(); - private static TransactionLogger transactionLogger = new TransactionLogger(); - public static AccountCreateContractSender getAccountCreateContractSender() { - return accountCreateContractSender; - } + private static AccountCreateContractSender accountCreateContractSender = new AccountCreateContractSender(); + private static TransactionLogger transactionLogger = new TransactionLogger(); - public static TransactionLogger getTransactionLogger() { - return transactionLogger; - } + /** + * Gets account create contract sender. + * + * @return the account create contract sender + */ + public static AccountCreateContractSender getAccountCreateContractSender() { + return accountCreateContractSender; + } + + /** + * Gets transaction logger. + * + * @return the transaction logger + */ + public static TransactionLogger getTransactionLogger() { + return transactionLogger; + } } diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/gateway/TransactionLogger.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/gateway/TransactionLogger.java index 6d961d96e..3cfea0751 100644 --- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/gateway/TransactionLogger.java +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/gateway/TransactionLogger.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.event.sourcing.gateway; import com.iluwatar.event.sourcing.domain.Transaction; @@ -6,7 +28,13 @@ import com.iluwatar.event.sourcing.domain.Transaction; * Created by serdarh on 06.08.2017. */ public class TransactionLogger { - public void log(Transaction transaction) { - // example imaginary function that logs the transaction to somewhere - } + + /** + * Log. + * + * @param transaction the transaction + */ + public void log(Transaction transaction) { + // example imaginary function that logs the transaction to somewhere + } } diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/journal/JsonFileJournal.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/journal/JsonFileJournal.java index 2d50db55f..a5d82e5be 100644 --- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/journal/JsonFileJournal.java +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/journal/JsonFileJournal.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.event.sourcing.journal; import com.google.gson.Gson; @@ -9,91 +31,104 @@ import com.iluwatar.event.sourcing.event.AccountCreateEvent; import com.iluwatar.event.sourcing.event.MoneyDepositEvent; import com.iluwatar.event.sourcing.event.MoneyTransferEvent; import com.iluwatar.event.sourcing.event.MoneyWithdrawalEvent; - -import java.io.*; +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.Writer; import java.util.ArrayList; import java.util.List; /** * Created by serdarh on 06.08.2017. */ -public class JsonFileJournal implements ProcessorJournal{ +public class JsonFileJournal implements ProcessorJournal { - private File aFile; - private List events = new ArrayList<>(); - private int index = 0; - public JsonFileJournal() { - aFile = new File("Journal.json"); - if(aFile.exists()) { - try (BufferedReader input = new BufferedReader(new InputStreamReader(new FileInputStream(aFile), "UTF-8"))) { - String line; - while ((line = input.readLine()) != null) { - events.add(line); - } - } catch (IOException e) { - throw new RuntimeException(e); - } - }else{ - reset(); + private File aFile; + private List events = new ArrayList<>(); + private int index = 0; + + /** + * Instantiates a new Json file journal. + */ + public JsonFileJournal() { + aFile = new File("Journal.json"); + if (aFile.exists()) { + try (BufferedReader input = new BufferedReader( + new InputStreamReader(new FileInputStream(aFile), "UTF-8"))) { + String line; + while ((line = input.readLine()) != null) { + events.add(line); } + } catch (IOException e) { + throw new RuntimeException(e); + } + } else { + reset(); + } + } + + @Override + public void write(DomainEvent domainEvent) { + Gson gson = new Gson(); + JsonElement jsonElement; + if (domainEvent instanceof AccountCreateEvent) { + jsonElement = gson.toJsonTree(domainEvent, AccountCreateEvent.class); + } else if (domainEvent instanceof MoneyDepositEvent) { + jsonElement = gson.toJsonTree(domainEvent, MoneyDepositEvent.class); + } else if (domainEvent instanceof MoneyWithdrawalEvent) { + jsonElement = gson.toJsonTree(domainEvent, MoneyWithdrawalEvent.class); + } else if (domainEvent instanceof MoneyTransferEvent) { + jsonElement = gson.toJsonTree(domainEvent, MoneyTransferEvent.class); + } else { + throw new RuntimeException("Journal Event not recegnized"); } - @Override - public void write(DomainEvent domainEvent) { - Gson gson = new Gson(); - JsonElement jsonElement; - if(domainEvent instanceof AccountCreateEvent) { - jsonElement = gson.toJsonTree(domainEvent, AccountCreateEvent.class); - }else if(domainEvent instanceof MoneyDepositEvent) { - jsonElement = gson.toJsonTree(domainEvent, MoneyDepositEvent.class); - }else if(domainEvent instanceof MoneyWithdrawalEvent) { - jsonElement = gson.toJsonTree(domainEvent, MoneyWithdrawalEvent.class); - }else if(domainEvent instanceof MoneyTransferEvent) { - jsonElement = gson.toJsonTree(domainEvent, MoneyTransferEvent.class); - }else{ - throw new RuntimeException("Journal Event not recegnized"); - } + try (Writer output = new BufferedWriter( + new OutputStreamWriter(new FileOutputStream(aFile, true), "UTF-8"))) { + String eventString = jsonElement.toString(); + output.write(eventString + "\r\n"); + } catch (IOException e) { + throw new RuntimeException(e); + } + } - try (Writer output = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(aFile, true), "UTF-8"))) { - String eventString = jsonElement.toString(); - output.write(eventString +"\r\n"); - } catch (IOException e) { - throw new RuntimeException(e); - } + @Override + public void reset() { + aFile.delete(); + } + + + @Override + public DomainEvent readNext() { + if (index >= events.size()) { + return null; + } + String event = events.get(index); + index++; + + JsonParser parser = new JsonParser(); + JsonElement jsonElement = parser.parse(event); + String eventClassName = jsonElement.getAsJsonObject().get("eventClassName").getAsString(); + Gson gson = new Gson(); + DomainEvent domainEvent; + if (eventClassName.equals("AccountCreateEvent")) { + domainEvent = gson.fromJson(jsonElement, AccountCreateEvent.class); + } else if (eventClassName.equals("MoneyDepositEvent")) { + domainEvent = gson.fromJson(jsonElement, MoneyDepositEvent.class); + } else if (eventClassName.equals("MoneyTransferEvent")) { + domainEvent = gson.fromJson(jsonElement, MoneyTransferEvent.class); + } else if (eventClassName.equals("MoneyWithdrawalEvent")) { + domainEvent = gson.fromJson(jsonElement, MoneyWithdrawalEvent.class); + } else { + throw new RuntimeException("Journal Event not recegnized"); } - @Override - public void reset() { - aFile.delete(); - } - - - @Override - public DomainEvent readNext() { - if(index>=events.size()){ - return null; - } - String event = events.get(index); - index++; - - JsonParser parser = new JsonParser(); - JsonElement jsonElement = parser.parse(event); - String eventClassName = jsonElement.getAsJsonObject().get("eventClassName").getAsString(); - Gson gson = new Gson(); - DomainEvent domainEvent; - if(eventClassName.equals("AccountCreateEvent")) { - domainEvent = gson.fromJson(jsonElement, AccountCreateEvent.class); - }else if(eventClassName.equals("MoneyDepositEvent")) { - domainEvent = gson.fromJson(jsonElement, MoneyDepositEvent.class); - }else if(eventClassName.equals("MoneyTransferEvent")) { - domainEvent = gson.fromJson(jsonElement, MoneyTransferEvent.class); - }else if(eventClassName.equals("MoneyWithdrawalEvent")) { - domainEvent = gson.fromJson(jsonElement, MoneyWithdrawalEvent.class); - }else{ - throw new RuntimeException("Journal Event not recegnized"); - } - - domainEvent.setRealTime(false); - return domainEvent; - } + domainEvent.setRealTime(false); + return domainEvent; + } } diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/processor/DomainEventProcessor.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/processor/DomainEventProcessor.java index 55453b1c6..81ec2e761 100644 --- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/processor/DomainEventProcessor.java +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/processor/DomainEventProcessor.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.event.sourcing.processor; import com.iluwatar.event.sourcing.api.DomainEvent; @@ -9,29 +31,29 @@ import com.iluwatar.event.sourcing.api.ProcessorJournal; */ public class DomainEventProcessor implements EventProcessor { - private ProcessorJournal precessorJournal; + private ProcessorJournal precessorJournal; - @Override - public void process(DomainEvent domainEvent) { - domainEvent.process(); - precessorJournal.write(domainEvent); - } + @Override + public void process(DomainEvent domainEvent) { + domainEvent.process(); + precessorJournal.write(domainEvent); + } - @Override - public void setPrecessorJournal(ProcessorJournal precessorJournal) { - this.precessorJournal = precessorJournal; - } + @Override + public void setPrecessorJournal(ProcessorJournal precessorJournal) { + this.precessorJournal = precessorJournal; + } - @Override - public void recover() { - DomainEvent domainEvent; - while(true) { - domainEvent = precessorJournal.readNext(); - if(domainEvent==null){ - break; - }else{ - domainEvent.process(); - } - } + @Override + public void recover() { + DomainEvent domainEvent; + while (true) { + domainEvent = precessorJournal.readNext(); + if (domainEvent == null) { + break; + } else { + domainEvent.process(); + } } + } } diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/service/AccountService.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/service/AccountService.java index b0a707788..59cefaaee 100644 --- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/service/AccountService.java +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/service/AccountService.java @@ -1,22 +1,56 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.event.sourcing.service; import com.iluwatar.event.sourcing.api.EventProcessor; import com.iluwatar.event.sourcing.event.AccountCreateEvent; - import java.util.Date; /** * Created by serdarh on 06.08.2017. */ public class AccountService { - private EventProcessor eventProcessor; - public AccountService(EventProcessor eventProcessor) { - this.eventProcessor = eventProcessor; - } + private EventProcessor eventProcessor; - public void createAccount(int accountNo, String owner){ - AccountCreateEvent accountCreateEvent = new AccountCreateEvent(SequenceIdGenerator.nextSequenceId(), new Date().getTime(),accountNo,owner); - eventProcessor.process(accountCreateEvent); - } + /** + * Instantiates a new Account service. + * + * @param eventProcessor the event processor + */ + public AccountService(EventProcessor eventProcessor) { + this.eventProcessor = eventProcessor; + } + + /** + * Create account. + * + * @param accountNo the account no + * @param owner the owner + */ + public void createAccount(int accountNo, String owner) { + AccountCreateEvent accountCreateEvent = new AccountCreateEvent( + SequenceIdGenerator.nextSequenceId(), new Date().getTime(), accountNo, owner); + eventProcessor.process(accountCreateEvent); + } } diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/service/MoneyTransactionService.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/service/MoneyTransactionService.java index 80c2a5a4f..5f7c0e9c7 100644 --- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/service/MoneyTransactionService.java +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/service/MoneyTransactionService.java @@ -1,10 +1,31 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.event.sourcing.service; import com.iluwatar.event.sourcing.api.EventProcessor; import com.iluwatar.event.sourcing.event.MoneyDepositEvent; import com.iluwatar.event.sourcing.event.MoneyTransferEvent; import com.iluwatar.event.sourcing.event.MoneyWithdrawalEvent; - import java.math.BigDecimal; import java.util.Date; @@ -12,24 +33,53 @@ import java.util.Date; * Created by serdarh on 06.08.2017. */ public class MoneyTransactionService { - private EventProcessor eventProcessor; - public MoneyTransactionService(EventProcessor eventProcessor) { - this.eventProcessor = eventProcessor; - } + private EventProcessor eventProcessor; - public void depositMoney(int accountNo, BigDecimal money){ - MoneyDepositEvent moneyDepositEvent = new MoneyDepositEvent(SequenceIdGenerator.nextSequenceId(), new Date().getTime(), accountNo, money); - eventProcessor.process(moneyDepositEvent); - } + /** + * Instantiates a new Money transaction service. + * + * @param eventProcessor the event processor + */ + public MoneyTransactionService(EventProcessor eventProcessor) { + this.eventProcessor = eventProcessor; + } - public void withdrawalMoney(int accountNo, BigDecimal money){ - MoneyWithdrawalEvent moneyWithdrawalEvent = new MoneyWithdrawalEvent(SequenceIdGenerator.nextSequenceId(), new Date().getTime(), accountNo, money); - eventProcessor.process(moneyWithdrawalEvent); - } + /** + * Deposit money. + * + * @param accountNo the account no + * @param money the money + */ + public void depositMoney(int accountNo, BigDecimal money) { + MoneyDepositEvent moneyDepositEvent = new MoneyDepositEvent( + SequenceIdGenerator.nextSequenceId(), new Date().getTime(), accountNo, money); + eventProcessor.process(moneyDepositEvent); + } - public void transferMoney(int accountNoFrom, int accountNoTo, BigDecimal money){ - MoneyTransferEvent moneyTransferEvent = new MoneyTransferEvent(SequenceIdGenerator.nextSequenceId(), new Date().getTime(), money, accountNoFrom, accountNoTo); - eventProcessor.process(moneyTransferEvent); - } + /** + * Withdrawal money. + * + * @param accountNo the account no + * @param money the money + */ + public void withdrawalMoney(int accountNo, BigDecimal money) { + MoneyWithdrawalEvent moneyWithdrawalEvent = new MoneyWithdrawalEvent( + SequenceIdGenerator.nextSequenceId(), new Date().getTime(), accountNo, money); + eventProcessor.process(moneyWithdrawalEvent); + } + + /** + * Transfer money. + * + * @param accountNoFrom the account no from + * @param accountNoTo the account no to + * @param money the money + */ + public void transferMoney(int accountNoFrom, int accountNoTo, BigDecimal money) { + MoneyTransferEvent moneyTransferEvent = new MoneyTransferEvent( + SequenceIdGenerator.nextSequenceId(), new Date().getTime(), money, accountNoFrom, + accountNoTo); + eventProcessor.process(moneyTransferEvent); + } } diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/service/SequenceIdGenerator.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/service/SequenceIdGenerator.java index 2eec6a70d..6e1730679 100644 --- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/service/SequenceIdGenerator.java +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/service/SequenceIdGenerator.java @@ -1,14 +1,42 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.event.sourcing.service; /** * Created by serdarh on 06.08.2017. */ public class SequenceIdGenerator { - private static long sequenceId = 0L; - public static long nextSequenceId(){ - sequenceId++; - return sequenceId; - } + private static long sequenceId = 0L; + + /** + * Next sequence id long. + * + * @return the long + */ + public static long nextSequenceId() { + sequenceId++; + return sequenceId; + } } diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/state/AccountAggregate.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/state/AccountAggregate.java index b927af0e1..a42826160 100644 --- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/state/AccountAggregate.java +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/state/AccountAggregate.java @@ -1,7 +1,28 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.event.sourcing.state; import com.iluwatar.event.sourcing.domain.Account; - import java.util.HashMap; import java.util.Map; @@ -9,21 +30,36 @@ import java.util.Map; * Created by serdarh on 06.08.2017. */ public class AccountAggregate { - private static Map accounts = new HashMap<>(); - public static void putAccount(Account account){ - accounts.put(account.getAccountNo(), account); - } + private static Map accounts = new HashMap<>(); - public static Account getAccount(int accountNo){ - Account account = accounts.get(accountNo); - if(account == null){ - return null; - } - return account.copy(); - } + /** + * Put account. + * + * @param account the account + */ + public static void putAccount(Account account) { + accounts.put(account.getAccountNo(), account); + } - public static void resetState(){ - accounts = new HashMap<>(); + /** + * Gets account. + * + * @param accountNo the account no + * @return the account + */ + public static Account getAccount(int accountNo) { + Account account = accounts.get(accountNo); + if (account == null) { + return null; } + return account.copy(); + } + + /** + * Reset state. + */ + public static void resetState() { + accounts = new HashMap<>(); + } } From eb2a232382f2de3eee5d9a288e67b2ab1c90af44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Serdar=20Hamzao=C4=9Fullar=C4=B1?= Date: Sun, 13 Aug 2017 01:36:52 +0300 Subject: [PATCH 5/7] README edited --- event-sourcing/README.md | 16 +- event-sourcing/etc/event-sourcing.png | Bin 0 -> 105812 bytes event-sourcing/etc/event-sourcing.ucls | 263 +++++++++++++++++++++++++ 3 files changed, 277 insertions(+), 2 deletions(-) create mode 100644 event-sourcing/etc/event-sourcing.png create mode 100644 event-sourcing/etc/event-sourcing.ucls diff --git a/event-sourcing/README.md b/event-sourcing/README.md index c513d2da9..1079405fa 100644 --- a/event-sourcing/README.md +++ b/event-sourcing/README.md @@ -3,7 +3,7 @@ layout: pattern title: Event Sourcing folder: event-sourcing permalink: /patterns/event-sourcing/ -categories: Concurrency +categories: Architectural tags: - Java - Difficulty Intermediate @@ -11,11 +11,23 @@ tags: --- ## Intent +Instead of storing just the current state of the data in a domain, use an append-only store to record the full series of actions taken on that data. The store acts as the system of record and can be used to materialize the domain objects. This can simplify tasks in complex domains, by avoiding the need to synchronize the data model and the business domain, while improving performance, scalability, and responsiveness. It can also provide consistency for transactional data, and maintain full audit trails and history that can enable compensating actions. + +![alt text](./etc/event-sourcing.png "Event Sourcing") ## Applicability Use the Event Sourcing pattern when -* You have a limited accesibility resource and the asynchronous process is acceptable to reach that +* You need very high performance on persisting your application state even your application state have a complex relational data structure +* You need log of changes of your application state and ability to restore a state of any moment in time. +* You need to debug production problems by replaying the past events. + +## Real world examples + +* [The Lmax Architecture] (https://martinfowler.com/articles/lmax.html) ## Credits +* [Martin Fowler - Event Sourcing] (https://martinfowler.com/eaaDev/EventSourcing.html) +* [Event Sourcing | Microsoft Docs] (https://docs.microsoft.com/en-us/azure/architecture/patterns/event-sourcing) +* [Reference 3: Introducing Event Sourcing] (https://msdn.microsoft.com/en-us/library/jj591559.aspx) diff --git a/event-sourcing/etc/event-sourcing.png b/event-sourcing/etc/event-sourcing.png new file mode 100644 index 0000000000000000000000000000000000000000..ac7192b195fb67b1d67c1b5c0e5fcc3a5cfd6fb0 GIT binary patch literal 105812 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 M|6O9HyGJ!3|Bxmyy?LP zdd*-~!N2`v4K#81!%=lBjSg(59Qpz{{nDPg>>&nlnm=VE2yJFf8nelT9MqtM{Dpey z8M~QxGjEW`)A-S;SaM*)EfVa7gJy7AkYakJya-8hJj5pY*%E~Emk9L9Cm;DZh2v)nH``U=?&oS zK_@xj(75?Ys6W&iGhWi$e90DCD|FDz1sOj_GhA|A`m^SBF!O%^;w!S$F?jRdm4rzh#L3tDpY=i5{LHK$)qcid+iLL8+6yF`?E@4(97m zvlK44(v}rnr}_&%AILvtH0^d9`xZS-z9{v7sGbaz1n~A>2OAz|^uD;8?g81OZ9ox9 zrqWwQcUDce!`dB`+y>UqJyyeB4=W*oPp2+bc>x(My zDrb4Nn_PA~dkVBD6QFa^X2kGWP=u9S&4#gzYrn2`znomr0q%}3xhey<08*sSi$2HF zud3&|bl}3ZJ8|)QVSc0+oG23sctyABF`bygMY(!F?8##kQZczNZi!tWm5TU3@h|?X zTK`Ysr_~(DOzMO8JM-9eAO;=v%cQ1R?KXyWs>jh;GYTP;FKFga516nPD4TYZo&=@Z z+WvUKwK`SseHQ?)|2Uli3>s}1b2_gUb&yKPSWUm;NH8XIo02MNQ$(bF_WY!V*2~aN ziX>|{`Ca)`!Hwsn5|ea}0p z=KKLt0s1!lVXE52;0GmD!G6Y520)i&U82FjflL!tb{o%uUHTwaKBc)U0bFiCDB1np zR-%k4-*VSjEpvo;1YM{)$&p9ivJclb90w>B3V5Fl1{Oi9%ZO-B#RZh|2XBDZ4fNK=89y2q zA;x%v9V&3xorFP#i4Jpb<&=R>r)IL}#xe}AancBI>Mm5%j161R8O(s<1y=0|-DDHG zAr(zv-~lY{;&;W6%wkwi$U+J+_)U_L&$%G|vn{}wIBFtJOFMg^Lu-a;? z4kBD*JZ!qM-CBI=?jk3V>*9zLFp~%RI931=xs~qMbJ8`1ybRR%f`iJWW!6CR1*5Jr zt!*o*R#ErUqeERb-J_DEK}Ll1CBv-jav@ub%?A!rshvaUm-_poXufC)H3jUtaz+#-fx{p=)}POnhm5^@!#FWl7El? z26W%xk^@$uD*%)LPkw@F`9#5KZUQ`?c>3)k_ZsB3^UnU3!pp ziQ5#jix2tvTE4-*ef>KCBE~1K%g-pPrz{I0v%p$xwgZv%m z*}>hVoFRH}EeWSp6C=Jpmr_i@518EDckK~?LnrTMx*k)Naf65GV`Wyput{2|9jmuB zt9cxi`lH=-28h;Pb2H`4y?^<0cYh@Aj_DIOCE7BDVyzwq`?P*~o|qu+M2BIWDbQ-_ zjMPzSLECsnD4!R!<_5{bG>p zq@UhK&LM-nn-lJI7^E}eAcLI4A_ed$oxcm|UG+4G^GOk9Y(5P~_`}nY1Ht}eI$NO5(#XE?hVm`~3F%AbS2hWrWd6+gpsY4E_~rWhq{s+X zH9=*%Fa?p|hv^%yNV98n$1FM%+haUAn3j1hbslxyN~^h%|L_mh^3fCmVkAHl?q7W{ zgZGDUiaT@1kA>WZPse*jj!)Q5diPF&CT48D_plVINrwRbM2ONK${@R=S;Lfg_Iinv z>U441Fm=z6IQe^H?n}TG$2@rY#|)X5L6PD|Y<~B!WdHtvb36BsKi~ogD-9BrEukEP zlh(y*^55&|Z8O}lq`-ty6S(#r6SxR~)~s~}a4dv>Y1M3@E5HX_TCEy>zFX#PXtV~U zktQx#Sa`}hH6?vvr+wTyZE?w_eMYi^9fddYdn=&fSoJyYal+A8V=z4Jfn8jIAgiVwRPq4e_qGAy)VS(M09RT8US@thE7v+l)*c z)g<5kCER`eouI$dF(U2A)7Mlu?F`LKuNlda9C2AG^>%(fYh45da5bxM3fVV-UGa$8 ziKsL?ljUi31?0N51zWZBh{w2)=1fiJ%O`wO?x5C)3gNLEFyT2JvwmiN*j{(?p3-x7n4EPC{@%i z9TgZS59^ogUI90()myIIV|(ag_0lP+AAGn=tY=vgB{MMzYYyFxbL`O;EIbuT$|nF+ z6SzA2A2=4z?gbocG#!NL9Q}T1ll^v9@x=+G-=Qp@U2Kr*9*bO1+$h|qcLJ(CSFm=} z$4o7(%s3&OQG9KHUz$&QZYCX;@Pa8%2c{EG=5h^`>eAP1cIqeUj(?Qcc?=_tp$c7lrlTzp_iQ~= zI(wVSR*xe4m(_4Dbp6mhUbWuYMQ4B#a`D_d-KsJ)nppHU%sjA59eg<-CBMi+mg%<| zQ`RyXt^oqEIzYPFioGi)F`As0iT6T;XXB3N8)p3qoLW-Nmv92 zukMH3uFp;BL@V;oa|JUU9n%6rE`Ps1!}Lax!DX=OA9z_J~fON?Sh%l5|H(&)u5ZmRToGzBom!f&wY?n+}zHCe%Kq8;Zt{_wb^|Y=bS(mE$`+nKKh`J>-q0iwOeo?Pmg_Y$b7QKIl%5)(|>{%KaaqO0*7B(tF?Jsy; z?|;GTSRu%Wd#W2Dt^w&C>kI+7NWy272}3BBRglSLlMH+2RtSH_g`ZE*DkNm@jC%7thcVJm zW@d)Sns3+~&Fihw*CC&sB=mmLp4v-Gbf8j1njZa9n%9HkSqx}{Cl;bsShKMN?7gYf zOpzVHkrFwc4W1p|*_t?>_hvh32n;626=8`FA98}W-)#?rbXl^Xq~$Blf4qm42dGW6LGTueK{s&6n$m zY-giTDoa$VqEeb=gHyyRI2Ef;ZzB^j=Zl;x6YhaC(XI{z@6WL!HZ^Mm40v~T$E;Y7 z5TZpYCDmNyvHQdn@wWuDbrkeV#4Z3^({F`jzXR6p?>rK5DP8QVL$s%3;e=;3^Ncw;y{ zcZ?5`tA}JF!Sd;zou*(=A5xl4$1b_nKtZ_!XgeOkDq-5$#1U2>gHoN*AO(e*^Cx$pDkIV;TUcE38D#?oSC?}q~6xn?Rwx3;+DVZQ#<-aq~ZN(0cBfP=-E7?NVS~D< zCVr;uuI5p~s!g+P6ZbE#YRYY}YWv$2xS+>=Yeg0A7{e*SIZLr0+-@6eN_x=#uq(?H zpqoV6Sa|+UT_@uPz?=M!fG?02j*RFEli3d0c!0bs5Y8L2*ZU=Qt{S3tEPxwo%ugbr zLjE$PJOQGl(MLlUYM-!plpi9XcAYydy7fBdjCWtx)dP}F>`}V7^)6GQ)l3k|)Jf#f zdA3@^b8bXF#~`9Qh1^AD(G6BhGTfgZ^J~ulr}%%$-qYFdq^Vn;aATX*60~iEOVo`X zbJwHkExH3{n!r8v%N;M2%jm$p7*H&@pDfy^7-|KR2Bj0v#gpiI&*l!+JY6|Zn$31M z$P2aZ-CH>{b>AzY{bdbg^>+$lQBJF=iF_WDGaKvU)aM+iYHGDdfiWOC<g^$t`++}2=1>2K=IfLU57G| z{LwvkFtmGp^rQ>$`@gHyW#n^1)F?5<~X^b6s4&ZvwnHz!X{AVU6v4&B-<3tfYTV;!SGDpC_X-a~Te zA!|^ymdXI){pi<$dLf`-iDLI)<@lFx?!R~cCHFs%r~gwc{Hl#+{XzuZI+bz=v{d%4 zEfL}J-dA_}Nq24b7CKL#b_rtdf+_l2h~eFI;2&mT^pqIq5(hpI6n@sYBKG@4FyIvu z4?p=nIU=znC$P$1Rq%D`cjAJqr?Q;MC$M(v1oYpdDSUi0<$2Br?|FdPwJGv4m>c){iW zX-;_+cacO=#XWidJ;1SkT;DfaSLQlKrfYNofwo+zHr$R>f1o3V zKo=`}$ZbxgC@WKvdW$)VNMpq8rW$~*q28Pfc$m@PfBa~{owW=)I&-8RfT7>^OE=In zzWa64PuPl{6Q0{4@aA8mlgIx~=&P+e=-g)ze>mM--==>J9&@Ke-nAx!CH&y4Tq-pr ztab*sAWk)|eSkMCLCq1`oV`N!C9!fn@BL-;nsK}K(TX`Yb3|SXd&z74BbC&L)cY$atM2-7xfsm}|L;aV7wkAtJo(I&2--n$x zYAvV+lvVu>V^D55K_ZNIDeD(tQyt3xDGo^J91M55f7C+iVI>Fn&=VuR4b?I1q!PFH zvOWu~)371{2uqxpC$RnM5J^tCf@|NyV6?<%K}Y*1mM5$E_vQlQNFL~+Lv+kOaE3(3 z69$zPA^UmkT%o>B2qn8g5QPGd{ZSK-$OT%+``-MtN8) zKcjBv+j82Rj1pod>EYYdaS+?sb&Z?6U*5fex76|pede1SeZ=7#uRC9g?Y!4$)E;wm zdPOqX5A8L6t5BGw8L6TR-$;m!1Z(p})LhE7;?%O>h%8q)qCe;r&bM;$v*3E{N`P>~ zOx2Ze@vZ!wxs|#NAANewOO4UsQo4-K_5eKwdeQ!ZiC>ng$zO2|!5^}v|FUbY=0&no zXGw+Cb8PHmLGIQERLj{+u_HjCQs=Vk1~==#z$FVgnGyVcAL1-mMH0f=w11`vcu28W zSEhmS1oNVjn?pyXiQjLYF0lmqVr^%utUt^ce36WdV4#55ujRbuwyUX;%I@RN>~`7~ zDc+qSlc6a#60C6Mb@oaAfq5PliiSWq!vRa}oc>{ioDrL|ae06C#1duU-=9Nq?5{1K zm%3Sr-2eI;?4v-RTzAXodj??Sy#2w@I$oFdNzf|k$azScStG3GfDiXWockPNPEgO; z)Yb?;eZjm;#Aznt{1 zU`Qxt>Vqe~Vp$Bpaz4TQ-A(`9hTRtjkVZiJf8kfJpt_Ih%`rog0cXFl6hH^>Zl0?Z zP**fO{(2{Tf_HPWkn;FzbYb1@fC3PIEDFV1=K~{rKg}KI4ePj0&-sLQi)RTB1bSeL zFz!t{PRa3iVTg30_OQA$(?Lq}dWiFtYKo=ok3I@Bf~H6#RF3k5H0zCNO|< zLh{;Qh~5UJFzK=G$GUc)ghMZ0YXrDCAb(yCp(r(~Q??u5xLN2IO{Jd~q??yE{O%Q$ z?9qQcQNBkZ@uN09T7*lFR*33OnmH%VdbcCJj^6I?ZD>*n?XDC7ybzy;R~cRRJ!-Dy zuyxf;$RE_?-v?wBVVw1EaRvfI3Mhje(xKPAHeH7|$=I*HQmj5l8@}E1np{GPPTYRA zs*|eO%P2LA*Utm-+P1o0ey$sU9IxB4iFnm6#RHbr2N8Hf1-N&YZ@8afRE>33lR2Bm z-*-_wkgj2Bai+o43|QR|VD+@x!B~meRAXms6v?}2V9Q$rHHW5vyk#>uJ1B9kJ8oDf zF>_lqFgS+VO97;*i3L@%x_h^sedTtNtmNCodfK6oLY|Tz=4YwfuE#laoV@VcJ6vWRKu|~g0i2UFte+#(HB(o zZcb;3)8dWVqL77SiJ8#HHBiaz%3Ux$IL%g=;Da<=TQHzl%iZ>R<_*bZ7GO=5D!Wd- z#xToR&{0e26i8%aVNyxxbkwHyaA*70gxj)Do!0@-Ht`rXa=KV(x))!kdA!}Pgvuw! zt*bL-!F3`_MPQb^HiO&?rw!%$#-$o7+SqN*#eBO5`Od#tey?kWcl(~}@C`&Iel zm)06v(4uwlx6$LhIevnd4mH3r{!hMdr;`EVRmJ!f|}+2ew$2IKNgFGt=e8Z&L{C($fgmmO2K$AQ?RwfmbYgmYB}>X5p6 zHXNvoJU{E0AX`fxY${*6_w(owT5Rt8(2$UHgUbfA8&ONgxzOQdVXv8Xa4A7GwV>9{=Qa?#=JYLVUT`^ijosy zlpP;D+rX;Vo7t1!D7XjQZ2Q%+wFwOI|4qG|#$eh)ZX7pBEJe|_-Mk45FyQ{zSz)z` z@e(DE!~LHNpy4NGZ5d>S#RwOCv6^HDck{`-;_ztc+z_e-af8qz0=L&B&BNgKO9u*I zW&Ts`6}sRTy1G#&w5E{aP>@su^d|5{mMTFrH8V&#-1-Q;S>|0#2N0mapuBV;i&+YO zhsHMuR1Hr8P^}085`#E$_ALJuK4|o>1B*>hcb&-!`9X?{jDO zT#@M*VR%R~=nsSNH$2VO_rG%FcrtK9RcfPQ3O!w1{jkN#$t5%U1pmjCP7!8V$R44P zf;SI=UKvn-mKDVz3A1#jqyYgsu}BF(=YJ(ACmLTZ-IUFkwlrR2Zy6O_4Ure~)wzq# zk|1KkcjKM)HhTLL506L3s7^OqHlK-{b$JQz_lcnPTOVww@zLe?KF@Y{xUKE5DlbRT z5XM4mp*^+moTp@^T%jiAS@zYPap=Jh4P?gAv-iD@sgk~`5%2+)iQ<}^K1mF#$7bWN zV&N?B&+eO_Z$vRTgIeX7sY~>Z?J>EOTen%{ z9eg%RRBJyqzI6v6>hHEt@@=2i)hHfwDP?yLg-aI&>CC35rUuxXscSqHE zOBsn_Ahb=OlitjJy>y6K#hl{Exi0+R z^rWQi-soGWt~8u`z0DtE1*{SSA6HWR00ERf;MC8Tn6ctTqS6t|1~wI zF)9#%7`gIrd3(t{{{EOTtqyO z+GNz$!%x#gwS_i&<_7q|7U9M2(~(<%EZcJ;_qqr}Y(i|?XLU!mRC7!HrWGP?};#a7R~D4{a& zko!R{Qu5xKk49ZO4I~l>k7-CO&j1JZ*kKz54UTu2jR0ym^SO#9+2?fzqCR!t%~&-m z&@Lvn@cIWS+QAt#n>!S7q0K7K;u?iln=H}{*pb>Tz5djme*93Q66oaGq;+a5fCE&xu>HaqQ zGbEXr*Rh1X;W%dY>PDi*!L-t;M&!=WPC{wrXt+ZBwDl#2F)*QrZ6FgPIBoQBy$?(X zkGC;rsuxIj*a6*ymPQ9Vd$Nqq&g}UjZ^;1~Ki~*GIjg*9sf#|{w8p{hrfoBkDqzCa zya;O?X?^;xXu(1x{#kQj)6^`l{}fL(z2~K|{t0{&s9&vtm&+h)tieO8_iX~FF*i3k zKNA%z@lHUySP@EKpkmZ>d%fKy|G3kXPH5VH+^lC087xnXgZD2PJ~cU7h4`_{3}mwo zK$TX|W@n^k8T~;MS5}|vXlA5WO7p>N;OK7zjP6B{9OY9F_nr6MIAxSThj+=^$A<6L ztzsB&5O#f`zxCUS%DeqFs{c}ok>@U*)+?)7|$OQQipLh_bj_v&WaKWxt*%HFme`oO01X6A5oy+1s8ai ze@PbN&L^~M4;s2W9{u11;2UQIiv;qX@a#mVP^80JX^-{_P--<&SV>B^_0?pk{*mUu z;l>nbSOHj$s}nHo2;16X=1?zuneX9Ij=S6`{Alm#8x|OPnESc69Ug&X=I_RFP8q=q z>RXgg!&Ade>z17W$f+F_=`BGIO;EAZ{~)LOTym;wHC+_yfxyAIk`2uO9>1_sbJUG; z-)tHSpLbptsbd1|#tYLsk$R2fP`A^0vmBOIAt?7~Rroc(>uW3NNWoQ4N?lw{avs#s z?>Ti4FXE(0+%^fr=Pe{f@in5=5aZacO_I=4hd2B~m`c^q8VruHqa zdj;*IY4;U1s7R-Kybh`hnVQ|40h(3*&Ww#?sho>ec9l399n>wOjCo9YpQg}NQvp)7 z+uIXY`)YIFdHo&$kHu%0H%76s&du<*8N8cn-)s*Ra*Mj3J0#)^4eFn}Vc8WXOYJ3N zBqsb`AdCbx`uR&EvnY^2bs>~C6)Z+r@4U?p`uyWWXY-`qVj9$dw(&F%q!;q<=+kj4 z0T|Ma*_(`=y=8qUqYRpomKT}q?Vn4Yz&|D{>G;+YppSknn-Vh?2v8>h93u2R%6~w) z+ovrSG8-w`Gf6G3ADlBxHJ*rmLkH=9<%LFc_71k9j7)=F)8Rg2XDK>L2n;$F@o(q( zvhuF+ve&Gm89-$H*0D?3LZNP!g<|@Fhu7q(#$FV`k9U$;B)=WJ?GX^T_IR*IQ$JdYdppa(7^O=H-ckj_+kcv{(t}bIh%Sco)U_=N2%_Jk2G%H?K z=h|5--fg8AMe1!NMBDhCrP4FrZV&$Gr4-AzXtQ8@D znNCnVq#rJ&Z+kP7L>!H1IQGK$J&GSo&yvdb7#hsmF`MEH*Po*CbH@k0H}F0cEe@HEadA5~2ZmWHXX#T!L7wGdr`BU9OP0wL z%3w@>Wfs+KYz7t)M10FwWcV1&iY(oYD5Y#2x>bi{4jN*GeHLBJ9gk;=b3Iz)j`mnh z8p+buU%H;51*+lXo=s|Wnc05OA9{d4SXfLyOX?+|TPT`Z=&M5!)enwRw_d!hn*Tv3 zhd-|pS<3b-aPw}O)jvIr?9q_R7!0N8W#RcWXXDpaPHQFK4|Hdn3DBuc)5Sk&oV+c)6pZ_HW(?McZc66m59CH3c77uiQPm64hz0!3g3@HNL7-tsnQCs!&TrNSm}^ zB$e5nPRWpxnX1o%#@=dAZShxds7IkXzwJl_Fm2%7)z zu7NAZWSM_tS0F({W~dR4ZNF?=cZvi@Qyq?j6dk$ZT(osCo#wM>ctl|zB+srO9wehZ zD>mf6%DGrD-+Z#{*1o1)DV*Q3;_5EZ)ODwH{bkjlseH$BWNF6JT*HpR8S_DL_3Yc6 zY-U3~>XphgzmN+3@LxdO8#eKp)sdh3KGSsi!l4$fV&^6BFn9z|AZ(I{0_WV$H-FVZ zp}5=cK4u^DMDK2oTZChH#!1P^mou>;i>}jx+T}wf0s$e-tslTN+9zei3FWRequodb zc+75^LS^sn*|KC780|eLX~X%vddAD@oVV|IoymD^*PI_kTtF`H zY;uIxk0&`GhEMt~vyt8*clV4?li$2=)E#u$Ae@DNnq}qE!u5_8%oGiHU5=~vKH_Zj zhB5=i&S7hEP?`lp%cmy*`FUM;QH9zH)4Vq!s8S2C@gT*(#IVN~pP>HDTH?KQFw7Y&v>WgfM|dn}fDf zQ^=tjwMS%jg>1%Hv4Cin^I8o~ZVwuw!(wPr7j%AD6_-U;o7n*NeS}8i90mXyp#@84 zB+4yTrJ*Fry`b1+Sr_kRshf3=wvc$mS%I-Qg0Q?$zxbvOH86CR=42$uBIEVg9EzJD zYHUGs9Kf+-G=T9wy?LP&WhxT81(L^GpuCfKEk#1Dm_V~A(PN`O`Tlv(t3H-dOI%|X zl~f)$Wj=ndz+_ZS*2!rx4EeRP`*`%r(gF*o7$BybVXK*aTdq`{O^XZ?|qt0s)h>nqPW=dtK( z8Oj+Iy43BX?jM2jwBJ2!;cu2*{rMYw6eUQ3C3Xd{VLC~*$h=bG8sP~ecK304`5PLOtchlcULxws8 zSwD5sR%1PIBF|BnoiWq&rUiwt!d z`Vh^kt3*78Sx~(x+hCS)o^5}a5h?CNBqnzrWixf*mYTD>4j=RMeP#ie3S35`ts$?m z62lIz^j$rbBctY85t7_W^@>W$BKFU|)2vJsamOMm>Go+_68SmHsj(C{>!+Bx_PMSM z5-2mWNm3=P+#~G>8qE@D_9jq<5&!|{8fHWp0K3uQNB&n($r z%+=6lom~P;Ob!!a^tjyozT32}^(;p-0m9xvtM2PU77Z%hosb-R@=|}GPPp+rl@!R- zf6wyV(vFD1D{3BoVihkok9!=eG9|+57|hjw^D?|bBhLyD&vFjV9R>`cx98dO==-p+MY-oiv-0YP zv({^(z_C}3mc2_ET z!#)_(WjiGszAk}-`+3{lom@;83x}qWt+a|IUONax{ZdIQxTS3{fVL+9`5X_C^Rx22 zlokz zZ|<{INN4=KU?nSHPF-Hf{OIMG&#j4t>i1_^Ba0P>g#|6}Wbyw$V$*NZwFC1eJl z11BHynO@sXcm=z|p9AAmDNJ(qr)~zG8r=v3w5?%ERBh+K;O`3Ij@|OG5{<$X0dwIe zMU_bAs6@vjrk2^2)QH;D(Kii@9#Ta5Sz=0v>@DPDC|5@d*mocNx*5zLIK`_vOsGE+ z4vJACgI7=jlbG%MCtLoU`~c!h!`PP6g>Yt`jl506Ozn87h{-@ktYK+E-=_R8p{=P7 zb6qN|BU?x?oWQjjC|M#4XPXI^SDCglE@3sJ;O3cYQh|JpCCZep+ZQ`z7QJ%K#Xf)4m(7@wT-#$FN^Qv({Bm`Dhw>7dynhx*J&*$u1m5Rq14OM@ zUP;G(($3NFQ&hShf|wAf)+(*?6?n;Ym6%IiPl!2~Bx_01SAhtKu7M!#oGJ7z2cl;1 zsdlJb!OgUFVZm6-fxt?oIqqTev+x>}B-E-LPf{G+nh~A-P4ZOINmX*|;{m$zRcX%> zum%;4nzUQyRZD5_@E&sNjEy;3sJ&y`QfEHEKfPI6(dNptm|sVY##aCmg4QSfN2dqk z!I?r9O_J_EjSS*)kE;h%T2L*#Nb#bbhSJ~#KEYCJ7+6Ha<)Jl}l z8OuXRNuS}i37#qAnjE&w5aa6ZyTxvr0iy2htn>|*?(uc7ai*mOjx&&O{R5_cRwznH z6*=v1JY=nF5pSm<#81DZx{4SKkhr$|L|0Bjz!EYr!{}HHd@T`~>+--OqiB1iI7gqx zWVhE+xhwXEWO(W4A)=(dp@)oapdu%g}}S_B`dYm1JEo zJFc}ZEM$pY)^%?oU)@AXkE<{Y!mL?j<#xw;(n=_3(5>~ch+=RWj_7vY73;X~6)KxP zuh1iwi1VT$1;9694CrMlOORDUq_nH`9J`jQXe5>p$~&_rujMY5vdz!~*J6AZwh_Ns zi-3=HlCE7$M`T~;3mpbG@4o2O!gYCO$(K2co{ez~mWi zRG0#~%owF>zXNUpqcXGhh`L`P=2~61Aj_^i77_7@TVi!!YSAUFg98|I*T3w?)+Ne< zf@6wt$=tu_{fl(cnWrW|e6jblyvu#ao7V%@`Pn0)gjUn>UM-{Mtk_yYsT>@fGsY%& zS3wtHxWXf{39N`H zB+VSmcK2-CBM@b*#^VyI{SKqjSRG#R#A*iQttDGKcCJ2ORDCUH@6@govRb)oor!ps zhNAbg;8m6^QGGTRH9x_?oWjcFJ*+ill(CWFHT*ZXEk?m9A-@@-bT+5qxe4z>$~h~Q z;C^yP4Gg%Q2++bc4M^gSJ&c8{jDl+~cF3}vCUw(&ka#h_Z%PyqXkv zPlzTP8eocHqCKrQur3x{zy!LJ^P6JoU;XkDWv_+V^VdCqN-R3r<{iS^A%8lEjI}z zv~q?e5(ZQY_J#rC{Z)}=J_7J4VkjeRwy6X{@PC^M!qB;3u$d6Jy}*l~dG3hatuK}- zIHtog7yJ4GkMd5`_D(-=VHuj=_N(ZybQWZP`1|NI=Pz^0;1bbYfM6E2kC8vn;+G4t zdcd(L5)Cw&;~%m1sri4{d&{t>x3+KCty@J9QA8RHx}^qb0Ric*Ax1i9=q{C#R62%k z7&@duL_oT82wfS1eUA4zj`zdkr}3C!#ktn{oqe81Es{}s#!-*T z;(>z#9C|+}x-3zH4!(5uHG5Ue>qwDOo~IG{K>jYKCO~%p1AheO0Gzvxjo-A(=luy- zUVx-}aifpsel(NF^hnozu@RqP$bS4ax_t-P4IGbSLCPP&e*feta|@M)*B6E|6Mg*M zp+fGHe7bh--%z;+fKfo`^fB#g;(5KJ6H)P=JKBc(ut4*qKCvI7c9o1it4rw75)Oaj zLHYxdwm#>Y_E#LCSSN5nkKsd)qN}@H^f60kG1=zqE_RlE6OWDMfrKwprfjo9j+O z&S_Fm9o5LZ-dFi@qthsIVWLVypv=zs*+R@(v!mfU0zSjbt5(eq%#r{<=?`9j8ixQj zKuDg$`E=FpW3t;dSZM>CCaB;6Kv%TaQ&Yt&oQu9TC|bu%yA?x~ZL#mHZ`YcrkLi5}MHR81RIw1nF4+vY*+O&%+z%In ziNz@NnE?o==*)U@UFpMN;aKVed1aNPDrQO`lI^GX@QT6gLvSDKYO?%p{E*QtaMeeG zd>?cmYA#SAI+a4^=%6$4sP$BSO{3+mn8>T4+bTO1S+|qhr!%6!LIr#fC+5j+(R;a1 z05RZW#*L~}qjJEUPeP9l@Yw(7;UE_vSjuGlo1ftn-D5y## zo(}-ar}%!9_|0xEAC`j8umr*YYc^M(^tk{xHWnA?duh_>p8BH5-!>i94AxT_hT{DapFW3zm4a=Z2jE446dRT zRsjMS-mv`n*c%2RgJJ}Y~fwnWm^_+hfx^%`&<593ikz!iz?vt0-)Rm--&{QaXEfHLhp=z zFP#%xmjR$v?(b+ZZUg5&Z@oh)3W%g(w^ekDYxF|7TxXtD8Ouu(tZ2A*L(fPVR+xb6 zk`VYrS>g9-_tGcP(bRk*Dmv>0Ds-wvHO+t@>sVkg<@Xtg9pGV$F8%aUuT-?erv!R4 zRH8@O{K^(4F-}7wz571hI#TJW3)ATc?1O&WanEK4An~phhH$nq`U0y|0=GTzaO3j@-5|5_X0{cVng8 zs+(ShCyyQDR*Ox0d-}afTeu3L)CJLS^cnFSN%(GgZ-!H8EmnEwWM<#fN@^!;tw0pzD2!8@SM)kxOdr9Zh(xNl3`z zgPMYIqzT?ieQ zCm^iK<*S7PMUH=3lNRvPl1L^MZj4pC)%T0a=(j*dcy??#d)E>y7~%(XM4vJs49>Ib zYISYGa%^55A+7`A4!_=g2C;!_TWT23Y+eLuh7<>tj+AOQxV++8;bTTwct#(AX36_} z#gx2yd=sM{m*peku%cxkAh*w8yKq1HVG<^!FmNz@AS|%jzGv zC%A{U2Ra$cfp;w&Xc!liRm@s~@AGiQ|9!%71c-sXyxbJ|q}POHpWIl?s$eRm;@4@Qi7ww;D}k zdf)_-TWu?mplpcg0i^rrB!D~)soq&YDOg^K$rm5S29v=8A14g+JwM>tu_5y@I1_`> znRnwVPr!B3xIaLP+S3+J9eq1-|9tQ(U?=Cs7*NMrO*|gD{ws(rqzX64Y?kp{KL0vU z^2X;uyAd*TJhZ%wCLg5ar5y;wKE4x~4+cWHArH7$(OKl`_SYkx;-^7$7VZy2vw=9- z-xQFY+L#wKJKfRYYWsj1MN?`r?i~O z?7By1cymw7xA+QkLvotO@u>$;Ze@f|vNh{j#$3Z?S!NseNV1eYL&T};b3F9I{*?ey zcaXwhL1bD&ZqjxYwh6On-+}FPb!tt@7WiisIMTddeU-vjjsR*yd1;<@^A~8^!kbO! z{tQuyg@o>(BgJFizhc&ZhB&GjH39JId2Gm!+(wfTDBwOgy@B+0nwrw5(&ezaK?U2* zy*&49IC-F=_dZGtp7yz6G=bmLkCjGzW^9j$Z7)=mi8&&Rr$S@dd^$F8KPQ@*yb;v1 z`iS+MVaNS)m3*2ayCKh~ArP(BQ$5B=VM149F!?K&r4y>N-QeAHCI~qa&K*VlXYfyU zSND;A!|8yGnf3h}%H~{$(k6We!p^H1rN+0dH+YS+G!Rgws`x|YRX6y_A;jg{qDl}q zlavR6dn>6P`1LR0{Fi*69k_MgbZI+MJiUHddO3AzJPRpiI>^2?0UKyohmSzpBXSmE zPde2*uog79EE0i8jdi1qnuQ6Yu^gbjn8&3G{&W)*49Lu{*~ot6YZg6&Xxp^$My*I= z&OGsmBs-H3;`YL=I2OEE@;u*|zL@>i2q8U-_%K?3Bshi0=&P2D+JkHNhO26eJ|aN0aj zYT?LVUTmbTi(8#L;9oL>z~}_<-wQItBF{W8AevIq(GEDBj|`5JVoM(B1UK<2bv2*J z6QFAy{AH0rSFr$l4q^2|UGUoxMFxgj=F?0y>JL|y|32!kG}&Gc3`Ny06_+gz3#kzY8m6Kmjwlb_SXk z0lA~Jw}eiUYUdWS(w3&&);lk!r%s!}HWP;lHy`iOzAp55^o{ZDb%W8{P-okF`HhdG zYWg30y}_o4c~%u*^yLHmOUG;WS2*Rdks?!wj-xQ@3=N`c_S-nn{3>;3-z%2Xvq_Kh zX8yosnS+9pvL1S!8UDmiS`uX_+ZsfBM^7Y{QAm$ z0TzPOW47AZlpU6Ij@@bj=dsxsn-mJ$6m7$M)UF!T+{dNE*Q#>)Y?*;>tgwT(SZpfS z?8pLX+64Ob%wSdTLhH>Us=A8?uBF`pw4CLui43oGfZDitQXJTt->`%Z>z)eMx;pLk zDWoif0u!70jj2kyI$CFr<=e1oT94B2#~X^L>h()|fsaK_OIELbR*{>lh7FWJ7qtw# zL(4L6DQ`|Xz~5YDRorI&njWWPdkh)O`A=JXqsGi@lpSzd-eX8rQWCk>br< z#bs>iEdUDqL_aM}=zlo;K7D8-buOX^IasRZxV3*>4;8mHSO|7+sO-`KTzCK2gIxXY zY!#f~y&&JSdfV^)3D=6PH(RKRl>|FE!#sn%P>cB~_nH_WXl^AoZ=tJshqo@MOB{!tMN0hI%FB6a>jyt^1e z@0k-l&i1ol6Z`^hTh6H9>N^a^?Gm-&vj!dW^swA@LB4Y1TP0D1bd#BLB@6zG%VV+b zqijjmvJjT8`S#hKnN?~hu$ZOl$X#L}87`)kIPUZ^V5gPs^x zQ8UNJ8*TRWF^O#3V#&8mvbRLP=c$|q`HKyAwTPMRY;1*?5Py6``ZlrZ+@}6&z$gZO zy&l%7QqYSghUC2{Yz|4_MOMFF!c11<*+T9$q@>e^A=e#P`7^Yi?TCKogR1UE7Ljvc zhijm&B?wE%umWi@d^qiJ!4PIC+GVdS5Hq~>BCXDGF#6XCzsa0>->_4}v_a?|7Z5wU z*Y4;VFobN^wOD8^@mIb5s^cK~TG@`iq7=URd{?+ubl^&k{!~jnMj|J9A>&PFrE*ay zekkb^b?q;F9kthObDC6|w)D(J2a2vITz>QE9Y%#(UFTD~Qip2;e}B;q@i;{0ejz`P zG5a&xO3QWA(W8ue58)WK*T$VIRJ235^n!T5&Au`wadkqh0VA=>EGe$6_PVD6Qj~J* zGxtEI&gT}UY|q=})6JkjV2=RKp~~L;xKg*Oy8Id_Nf+e+-@R52((Op9g>@(lHrwDl zl2k?hTAvcCORTc1&%|d{SSdWSlZ$u4z6&`I+=V>2M8#Y?tM{d; z-zMsE!P_S<9fl3)%;!tkoHJ%-A#?9BzpY3_-qx1ndXrJdNb@OA(+8*Gmib9K$d1In0Ht?1wmNH>$R;>K)lpB=< zjqGY09e6hIDXF%k8_H?wc0F;B4@>xig1){V9e7a(0^k+OHDLEg&x7ZM-V=-01TV+7 zE<)&wTLl>RANN1)WE9kAcE~yju<0h$U0n|?8-8LLjGh3<)7~$anJZ~xc{^ei^$4I( zfC-)Pr#zjJvie7J_*znA!=4113~|0pKdn(%P6bz7sOCYQi%S;RI;RJ;9hZ3#k=x+- zDLA-b+z<3XrV0Pwwk@pG!_=eaf*qAd>UOWzUTs69?**`CRNUCFHTMpR&_)3-B>D8N zLH;}BiJj|NZGKdaxAMi;g_Fm0e7VyfER{X;hwF0gVCu&G`ZHjj04iW@7=Fo!hgz2t zjruqqTVbOn$92yLSjYG5422n|w~N#7e5<(G#?`6fT&>?y!t9L2;sFU)=TFTW zAG?wzKcCK=P>SPPX=UWidaqG#5$REDd)XO72X+v}AAs=Uv-W33cE2CIvF&T~Kr1+h zIC)KWFtXTelG?t>5okhA_z#+W_enn0iFr81&v&w%-MjM=6|U4|k(FK-_iSJyV&JMo zh3YXaBASISY`FgIitKsE8S(KggZc<=g;;hqfVp6{I>51U^EHF%tV&HNe!&;;Zkus| zD{v&-d#zB*X?A#lp(3fnPUTDuJ;CbjNQWTgWb9;6GhNk!mW!NPXTFEe+0*F@JGoxy z2^)kS$X?i-sQ#S8!ivg1@`d3;|8ZJZAs8+@2?RZpwN7UT+c8#A2olCStcyer;%G_-pM)?}T^|Mepmt$TsZg zBBT|!AIOI3aD=)q7t3%2^_XVv?saZN@1k6fMc*G_Rp?_DzV3fnqxr0c`R7${ADuKB zK{Vw^sO4AZtAB=FsTiv+UOS%RrO%#04MF0QoU4|v6`)U}Qje(G?BNfmjh{p*Yd;=* z@8IdS8u`ZzvNXm9Rqg_6MCXH6gC#S-`q?{VWncLU)NT}29F*vx8dXhN{PBU*C}*{~ z`TN3b5@s9|?0Kan#+;At{&CZ%y-eJ3zWT4&EBt$zQ6l_LWc(Kg*&yVu^RDZR^Ni$% zIs|YU$?p~1S&Aq45<}5f5tNuP!#;#RZe+kdfGp84q_`d zSoxBdD7I8f&~jiBU8E$o;j%TZai=>K_PyDZD|_N)AB|S9$N^<_S;Jw9rVR&n>y`fz z^Kb})z9i0LBLL97#B#0h&eFd2P$=*OK#p*I?awfg3R436jh)wK;Ng*U?8hxoVg0|e zK$f%FB=9m~gJ{UM&W zYOuxoPXc(#_+BZvL@Dq|^sBKBS$}N|EmP^ko(xMfeRkvTv)uH!D4jkh15{gwpr#KT zV8eoVQ6ozoUa1dX8hEs<>f_soVmg*34wvJoUb;hVbEelnNsZ=gR$LTx@lR>Ijbp+w zVL4Qb%om6sjm?DoCZl6alg8@IG^NkXn&O_Yf( z8`J7^2)EQAv$Vuv+@e+BQ27Va0f=zSl~m`%;Cwj11@myz1`y%okCmYIp<2k$gtE=K z`oJ7rYlpZ71GN~pdzn1@#tpO0tu+`u) zliy+KxdYCDqxwOSWp>Y7B@6Bk^tM&>?*sPqTtz{5v z{ep$+wJ-cG5MtMU$$CJq4>Y6xMDUi}Y2ovIAmFj;Tq_eX+kjpiP?;u^-a^+C#;%hp za;cK`$1hR&H_hh1(h7JPpN4_lPm77WH3L9U72kttA{ipq0eZ1V)Jn+8Z zVlcR-)o+WR1t?Lo7Gv=#S3pXBp*NSmeYJ97J2)sQzKDiS+pvpKXp%C3n&B53iot9L zV$buZFYI&9Z>&s^Z2E$$qO@t*4TY`YS0w{%zD`IhrZ0EIlrT?QWP)_{32Lm}YAm5? zTRfGtv&8-myZkNp0XF9?(#u`}=~jDwkEF@HG~mH)0CyEqO>%#oWzZOue(ObsgJI~` zscU0jZ9^1ysqiZ3lBpB8o|p4|tzw;2oZ=XWS_v$enKa6C;D*`WB-;KS5dLL7Lt3RJ z>0?7mndEP`HS)RARx8ALG|C}JOF)(xpXO$m%8gF2e8DqnUNu{|ienOA#pp}?EC&5H zUQT-`Q;n7If{SB!9{sLu1VJeO?fSWeZ#+#nM;3)$SJLWRF9)7Jp>Tj>V53r2&~H=n zD^%cw!Si_15`pVlra!-9eK5P&o)}wxrFuesB2%ADY(?6}Rn?9zCbL7mgE^+R)4_L$ zSFQa8*)D%+$Dp(MvHVo7rs6{h1f1V|2L~`G52%^>B#&xa2WaIK+D)<>f}|`3hAf#H zO8P4-I*Vo}=FURm+~yXrQL$SjSu6^3KNt4jVLss!9znk~(ft#G*6ajuoPOVwKBG%T z2}~qIlkt!|H_*1SvD$m^455+F2r)9>C6}@tEO0kJ=zQ3`zvdP<03G*vg`>MaS!q<8 z01>h$IEgWE-N$f!f61GB-cnAY40~~)uw?3tt35tF*DLcp&i5X0@6k@&pIo%&u$Sd> zuZqCK<^thqeE~cipmaiCNU^(*)|Kan;C9x>6*-lKS{i=p`yR?hya=uY^ zx*A2fJ^v@>)u*>1SybR9RMlK_UzirL@U)tIV!c0-qh#b8iJ;N|TepmC+2b#jT`Pcl z6v9EMo>j!$Jc&P8QrbO+fp)GZ@ z+>GV^s)+76Yyb|lS?5J8RP8!*!`R2gCRT@S-pkZ@BvS@5%w7JK~(388@8r0=nY{p=PrPHKSIAZA!-jxvz(}6n~4RHDAE4w1|Lpe9ur-uyerq* zi$nYAj<5=LFGJ~Tidv+k>OsHf0?s80vUO$HI)cedi~Xeyvnsb z$xqPKMVs0P6SMDzd$I(9i&4)Cc)+n%cIZ0vL8Q56e;ISoHI+j}P{X5;7+q)osZDn7 zA1-LAA6;Eq?2ZcO^FlQ0qslvf`kIn^V;Rkt(1pJVxs`{m)S;0VUDun!FPAu34J~M+ z7c`Gr5L%P@ZSt|N(1L=lr4iK)&84isV@8dW!qSN(i35DZ@T~;O_I6wv!^f!>pgLpTMVo8O9WHaJRHa1`OM z<-S2m%RU*svVWq-+h9~}TR19fTf&tDs0J#FO2#Dnvn}rS3i2K2gW;AgGN`>>3Bo{; z=2m6buBCwMw(8DimgU6RQ8ke1s=a6+7;DULmVs>JxfRKuQ-hQUz6W0h45f#-y;d`v?=EKXZ8Oo{{s%{m#H^ zm;;7O!w}yE&JIoaQso4yO@!Cmc7es~Hem*;W6m<-8irYL$ab$l(Zk>N33OVEJcQJJ zA6}>sbj}}?LG@|BBUQ6*v#Uyon_(+G#y%h&IH445*wv%RGmL4OdFO7|r388Sfmo&l z(wJL`!$xIOi}_$6F#X(##;2-ZmL>eVPOog)de;D1GHLzVGgcOnjI5EbuWKU`|3GGl zGh{#MiBquC8DK{6>9?rymEn9r^>FzFT(@Lnpta)P**1{KagfGAxN}{2jj%jWdOWe# zOZG8;3+MB)<@mfCU&f8E2Su@_gQS!VUPZcemoUglD0d{f2=OYoDi;kveQgk=`o^D@ zHOeQACf+LU-mlL@a7*-8{1N#_@xe>wLVq^#{(>*8 z5&~+yrY4m6bNG8NI_4IXMY=WKPRtpZi}h!2J*Ur<1)`HWamyrxj)PL&yS}{~Uu7U4 zzX?Q_UAgk@cEzn;c7Y1Iz2(`JR*2_n%j8_sVIs=X>>Q*|xgo*5)>*$uS+1X(W9ZF= z0IDCSurYNQWX3YI*76o;RRMtzZRE8|u2IQPM3-LcQuM99a3xMLlP~6~00iJNO=jOu zxd~+p?)LKX2SCcC`AK{GS%BZcn{#v}{2z~>>Kn-Cr!A1MmhPeW5U<~MhErepPH7)K zTLN7-ChY*aB>pS(>y{jaZl;qKQossokwrf5$#Rf<5-l-^VstB|A0ryR1#B51|NDS} z7SR5;(F&!qu8zBTK!@t>J*s1R!ZI#-I?iJaEQ`$&dL>e|Jj+MnEV2VHj5Ssly2mU$ ziF%wE?>k!;^CQ?kOj}rY7`6ygX+$a3*0x!EVbRUc9_f}Fahxpv4eve!)lzPb2s(Zr z^_s4s1Q9>;wjcTgrKY)O@!G|`I03>wS*2!?#XSb^(Xa0MJdlaZ`9qVkxjFJ&vgL*y zyidxhip8?14n5%AhyNu8k?R8(M5do%`VS1UF9$G4=Vdui#P?Z;H0wT-hK(t1NBVkW z2d*|_J9*XoxE?dMu7|@f3@TH1{`f!>&M4c=X}hIt@SLeS6)~U`2BuqRO+U7auw6Oq zFHmk4T{>J7sQ1F{7FeWm4vWwzFI=IWU$@&W@B@87pN95Z6xsvtHoR%^|>@j4!!yP2XGdKqFN<73Pivcas16)@bKOM*#29mu+5T(c-&}}~M z6HoBYdPIceC7MSu3!zw=K!x)()^1acpZG(v2Vio4pr*_U2%A9R@( z>o}Gs{#e4W|Kj4$Z>Kt`(N@lLiwO7y0A2uYBy*wIHqgN{$!)3GmWzel)5*$h>01Wx zO})8^U@1N0I%EWy9;_nged?fiKR!@bkH)S?m*NcR!KQZO3$d|J?Fa5K2dWT>ovOF8 zA{3@%70EPU@uI5gXRzGxG1q;))p@O4{t|Y>oc+b#E9_LbxrcN#9}e^v}%pFcxBf`mERlm z=+1RUnnxu^uD|02d?56Sc6SItxcRb!op4yJ!OgV(QXYiSEi)}i;{uxkwnv3&(pvMU zIy|(xKyvsJr@N2E)P66}cO7VXQ6%-3B!tOtd%EBTRE=hJQW6NsRl7MSwVkS@3h414 z6@Nn%-HNJkn_891s-3BZ0=ISi6-ou)L0OsKesSLpj$x1P#`FJ)4D|mJ(k$jdY9oug z=7@v*-2p(CW;`F!*zBQn4OCh4mc?B-K{QXfS%c5l*kW@Y)@-TCG zf^}o3Eslc^6~?dqj`HiELJzBR+newCkLslK(%wX?t6@(V+2A(b$Q~g;u5u2%AHm-} zH^hOxFcRU^rjg(kA*4NHZiiMqYzm)4-MLpUQFjjD7CjBe>&=>NHA(f>yK`%(KE_p{ z=agG%3>#f$#cBs*33#oPr){0~(hdcSAcSny*~<>joXIdWSQW2}c7~}_$CYbRo8I`6 z!GT3tAg@(ymol8V3JT;ik!6#5@&S92wm_8z5ebgbz-^IqXR%mD%6z?i)J!&w$bK(b}&ZQWt-`0&<{*9 zcxWZJVIj@0xLT9P55^S(MFbs}%&fac_^edX)x7FrTPc_baOL86UXF@XUbl2J(R_z$ zs~1E4*QNivdo=Dw+jo+Tv=5fI!L8ZudT~H+2T&(Zt5!kYPoXG2(lDtrUcI&Qkd~-) zA{n}r$1hNkb6bb(^`h)WZ^gso^xBJk*wCin7tEAPM zQ4M?hPlMV$uKe&q0KaD86gsm^fOPZd- zEn7Bp=e5{ecgF$nppPz*Hj0Cn7}r=YN&@MzgRD4E!~S6)aJgfMF$tq}4issT&5s!7 z6Dfd~V5!!D4;PGjaOZ0?H5h?L!9!dEWlU()lD_|g_sXOg!y4iXf!a|wEZbW{i?3pu zWj|v`Ua$N&mTyb5;B-2b; zdg_k(5|N~549ls@QKzh%*U=yy`4>nhdhU(|R)jeUy3Z}Pjx_roEt`GWqH(@dTfr>Z zVm4eYXr3R=7j)Kjxu&X{Yh^`cU$=E>sp-NmkM?D8}ImX2-H+1 z_3NK$bar@_FowH`+aSCB9at8E4K%>ihT@Q@Hb5(@L~CU$UlaF50iEoA29{de!7GT+ zP0?ug&cK%jX_ePc;|f@~_$SW>no;_W#e=_|w%U~E{%6hT>5KVtMK089vx7GuwWQ7z z*gXM21w5y-nIraQ(U@*ic9h)SF5Af7D6TJ8LTWtH=Dka5LaOz-hJ=0XL7@tGta}wQ zgl(P5LpmtUwGkX49>-nW-c>nmvt(18q(iw;&P$gU!RuO&HSTZb?&#%yeLJwAlB|oC zr_eN7=Yq!UYz58tKVts%RR8WTlc_M}7~l*yig`0jI8^=8^;IR?0>1=8UuMNJPfChB z*&%dHJ4Yp;#Y9DXI+OH+hPF}-7E-dx*9$)(eCYM4yey-pp|MRB3tN#Szwwwg!B-u7 zA@MggtstC(2QOH7P95Av>>{W7FaAa!oLZY1f6eH^2@mE0m2C*1p5|G@O$Vlj=mvYC z)jIvV;Dj5dL?PKlH*<$r71ukUi&;{wxa|_xeraj!XQRML?4|Bjc75VR<3Y`zC=&#r z`G#-RI&CgC&*9*NY?0R5Ej_o0ud$X`&vS&Aw9@sCsc7K2Q$kt2*`5Ure0*|I)?cxX zhkRA*OIsrYop$W2sugqtkwrLS*takZBW7i+&{42y`TXgh#BDtkBC&#GT-G=2~ zu#^>u!uC=6zvL?)a&5lzqcyq7N>{g|sa=5CalE&-Gl;zZ$zY(Y5`JNWi9cfo?!;9d z^A$5?>L4;Tk|kM*NzIH!DpQBCo~~I`gFzi} zxi>c#7gzQfmXnu(0(i?{X=f4qe3@IwAszFH*&2r0xB<96ZgO#d~QEv88 z&Aimgw;b9+adzK^&6FUU;_XO@)QJnTVlGJhB2l&ttwXobX@`2f$!N(&`f?eYRv~TK z{f>q<)njzC^dGjZTQyz(=5g;;w49T!g_SDL)PqW%Zt0X1MVM+a2>qA zfj6{9l4DNL6Qbbf6-OZLN->~6A)$UlOy#_*yk^Y6qG*8RB>iD6@(nAphy<)f4EIBOQ6Xim z2?^MzZ$rZDaKPkw7HI?t?!5Kp)smK`m#EadD_0!u{RM_Mb*jF9$?s5u2}ZBv9-(>y z4OeyzKCV0oA+DAgsd3X0AAH2r^y~q%u>A|M0Fs(6w=6rx6iVVs8E#((nTnORn3>ia z49I2CC2RPb9|{&V#(xh&`t-dts@y~<5b9@pHa_6M2!K%ZHlCwyZ7u*pgjR4M_U(?V zTUh$m1s#P{oy2HV8+n*Z^~;}17nMb{flDpxQ12^4lHVYR%O4FvKrRJZ#ruyCwC5PP z`^BKNs*>z&e2!>L5`O8QDx0Ya;0~MFxY#kM`Zzgf^J@TuVnWFbF(XAKl$7%$xNg<; zYL)((Z*W6)L*s4d=8N)?KBcfXRn9I<$U6buiBw+!*?J|`GyI3Lj~2_W!jzU{kt|CL z$?t+4HZ02$bGdHMNj?0d#xRfy9dI2M^jnj|4bn0obN|O6 zsQvuV)8ob!*1K{8y*M0z9O?ZD#kzri&)v|18}RAwL$sk6`1Iu_x+yR4X&Vh4|MzGA zmmt6$_`f&gw8p+z2!3)83Br|GY;CK z%5}WJAVR(VPcRYwo4xk|hth|~sK>Sr`X7Bg0n10bgscl3s$&S0!l*lz3B-|Td&|!e zCSaoM-Z?{z(u-pfY)H*Kpl2<*sjCCYD);NePtXv}+*@`V3b$tfql1G?;{3y`er-jmMQ`ac3+M5Svb9Z>Ey0aSH1^h#n``y&hcOz7r$)8I$;;wEC@k6yIorxDmSZ zB!^@duqI!ZKbnC;ePZv<(kSO;#Q#ha`gV^brN90~9ZBz$(#wr-#yYKqm(dcYYz#D} zWJl_7GNf*QNzr+3E&B4!y|>8tR17h%PZS`S z^E8r?FMSoX>(M+&s+#znGIk4#Y1ekL^=n7edGKcyBV;6Uut$;?+#%3e8x6FcbcJg& z{imTm=1bNkjmyP=zpU?iq}q`)-?z6zoD%Lr5V0PZK=(;=Ivgfc_ep5syO#TYe^CMh zW*hK?5c}$TLgBA>`{_g=*tkMJ>t4Sdr?^y~@Y24}7 z#o5XB!}Dd!i(SZR)|T5`n&+Wk%gABVI%`&Lyt-Do4^PoY2k1FOQ{ z?p`p>o&Bo;Kx|6T-HAm!(Wk_Jk{55UZPj@kuT5Y2HC(27hI$%$9$5+=K?GeEZYEtw z6`w!67+9^?s-?Zye|Xl&6);EUwTsoZA08ldUy>l(7MHE7a`76&j^iKm9*3NfN}bn} zjPzrpZeRb8x4xy2#dFEL!DFX<`t->2{`5%0<$F)JmBWixt;_k#$}^nHZ(KeXo8}OL z*7=*p39%=sl2wW{8Zx2$N{b{6ryzDjtY+it>rSfN0Z7Njth>!x+!)=0nRMw+g2P7p zn|f6a%>QDKfqqL5PmCcK-QuRxiw(zf4+W1jFPm80b{hzANg#Yq>z1Va7vHxFJ+{AD zF5HC6_B+{^y0z7ox%ERO^_`7~o9!qPz!rkB(a2nVjslIuCs5-VWo>f8U`61jiPXKG zMP1Q9E4W2}p3aVA{lWCbL7~T7!Zbcm>FPA;qQREs{G_Sj++5i6`<&y{#r(~N;v*W! zakr(@Dad|0<}$p$(WFu)*v!C!bFBQxvN%7uma`xhJF^SlEzHteQaoT|;_82~Kh)Wf z3tW^JGI{LMTpqK0Q9EezJT1J;swi+jg^dp#FI^tfczSNb8$4`QRxWpwpVgN{pNN;Q z^>c@*t|65p$ny@&jF1O(xgA}HJxd{K9JX@2iH07bO2Gc+5CrNw1m!@F60w&wuy9&# z8(^1zRB(IvzuX^5bUm`1J~|O}U-~wE-YQs7Un6+lc6r&3<9W!XY5)wb1l`z)6DiOxUg!Lt1>+#kBhAmeTl@<@PL=|Z0&l~nP@%=@NlQ0xKyny zZ~GnrMVf?fUap>aO`q*VH=H{%yI(%^+=YbUM$x~-`-H>|druR)`l zG#0ukL!%0{64kob^y^o1y2KDL*Fp`_%z{IN7SL?Gl?8a=#cMvC(PvDavBYM5mhMEl zRT$0@N!7%83bm#I3Q7rdf_!Wv z3NZ{4>5^@I?Pls^b3byuXJO@p?eo;4mcWHxkNT*DRB;z^bK{M4cPr>czFYMOGpM6C zL9VqjOiV*DP)|@{GBd3=?=4mOCw2c7(8&8Ru_BZ2*&}@M`@H5)eBzQaQyS+$BHJ7Z zLn28_O%r2$CB5}y_#n?OcZGSW0Xnm;`jkhV$r!SDMM#=PeN~m|k{#m$nl#0xDAug6 z(g?CcFs<@IwJXHU-FU%TLkrW9J+GqTuQL>;k;raoYifjv(;r(W!s?+yFW+qA4nPaY zsCu+u>}RMYw2xoecq01j&k=FN$*U!tCrlJr8*u0!75;bGQ1}Cod^oJZ*mx0KBWm70 zwz4#yL-a9s=H4>W4kT5Q8R9vbH7sUR2+Et6KMt)k3f6-JY{GLp8fut~4caE*W(l;e zSY>TZ3FeG98OxAruG-9xueV4^S5(0w%JgKs#QDFUeqis+C!y0aC(OH_FWguFdRG?6 z$n*jLRv_3D%^o}R>bn-DE>>BX;}}k=gA9R09yOB%5PXj#amB0KCbwm%+8pmx(QbMW zNU4gX>M2p&{HVcc(M6bJJYoL{swzn36lKx^{XBfP%F;H0HkR%iD50;Fj71A8t|Aam zwxUX`4D&j#=iM>dlCryiXaPj)JQn$N4G^_N>E)ij%Rr`&=*kw-&QKqlVNHTnaypY& z3i}k4Pg=b@U0_+ttc~JxF)-UY+7%3ZY;yY~llgblb1Cg5b53++aQ3O|%<@F=4nqJCQu#8_te@%m5ljqVH2QF-QH z2Sxb$4R4s$!>PQ~w0+p#&hmat@0}&9EY!WMchO}4V2%P0 z1*mQn&SDXJ60RwbH5N`s2S)T?ttkhK%TKk%MhZ4!kZ3U4YX%8Dg&x5bRWxcP}mC2yJ=MrBD!yXl|PY>LuZ)H>7eU48eO27Dr&4;%~YUd$ASi~ZIxiwK- zsf*$)h@ArC@r;0Ij4%T^PYvzqEe6aDF|&@R=f%<_?D+b4EqK4PZ60g5$-Eg!P*j^+ zaln4o2w&BZ-qh2{Me#d-yjKs-g|&4eB3d%-o&hbZKlO($Es*rZ`R`QAY)_vL-eEcj zr`&%j!Nb*J%31tk+re>jGw3|ZJj_2zf-!t2--1Fd^4j15oZqiqJH9~maJ>fTZ@KSV z1y=1YYiG#1p~ysQ+mJ;YL}IG!+yC@%uW9U#iGd7|;$1k8r0wV`G#c1E$wxe1c(G7d zty=N1Juv|kTZ9soIZHlV{eY5{!8g(p>$C#rt>8$;CVqV8%{&g9fQJ6tK=OJ6vQr?D z5uc`ma)ThhR&{KmHp@|g{xsjZYIP4McN@W`PyT9gm^=%-4_)N<-mFoDrEgAkc;+)N zFsKhXjEl}xhguP3X5l4d*ja#vE8~3GymNi&*D7H5)J6WN+Su2;dA$09bHT2rdBF;9 zQcJA%@{g~dpt1`2ZxxPt%d+DJ%y=aho3}nM;tbB(hEa}hwd{Pz)pI<{%imxNlafPR zXoLKMytT0J?dFxP`fV6IW-hwDrBKLdSw+UD$C;a#P{gonE+xk@4ThC|7tHY*mnyka zear{(Qe);#*JN}D8eDe0=-IJ746lmc>j*`BKn{lN?L*^^`#Q`PHv1+Aq?7Hjjy)g> z>W*MdU!R@`Um{NVa1$*8rb$=QKL$qGM6xMX@kex+)EF3U6ASfszO~_Wd{JHic{op^ zCSnAut)MQpHBmKKP${v~AHqZFHgVo~uM-=AJu-QP9w4nWv^#(XAGV1(f~Qfg?U&}Q zgvNA&mIMUy;T>>{mQV*_2lmebQpoI`tSyBCC9UcOxo^aLdgb(*u*5^qB&>ALGH#BZ z=k}8Aq;+JIHA0OvoKmn}tD?g1gWJ`vOZcf}N_A!pZ60qvLld8x>#nzi@36c(2Mm`fVSC_C%tz#&k33P?e*Cj1 z4Y-sW)t_8QQjfT{_DOCYTXEuL%cqMd+k6W-R_(W;0Lusg@*QTyEwp?mY6UlQH`~*y zMsgS}YzlUS$M^FT6LU!lw}=WGA0L`{VU(UV6Q*@g_?{>?)puq4$8Eh!1#$1zM!2@TU?bH-=~gM zYcAR$mclZMT-md6pm+Fid%jfn1#|<1d=OgA8_|@EoT$tstVAhStV>0aR`937)z?KfZR4;!$`}KW0D)%|}}BhdEG-n-hhUaiRJ2Pgq@+mLPT{1I4AS3(G6;Evb<&T2@~Xbn)) zhRJRCFczayCtEWhHwwZkB+kY+0KJvi`*iE_?3elFb{nJWG<64FsWdA#A0dOiJ20Ad z5TIcSjh3mrxjW7c$obui`@+Yr9OxeboFc$6>e1}TomLje)06Y*%X6t{<{VdY=lM_~ z+ILq?FNnstZFpbDzXGgYc@!;0yAU4N*@L@c;qx< zB#n{${H4zZcMgHP7D})G2Fl9jBb;G(^#v--eR{3&mipZ)w=qYZyfR=yc`5)HJ(P#P zRC$hqhB=7N5aJlYlBMfp4S*5qcH1{kZ?7ooiGkzO{O?s&F9gPkCcp_YsFAuh+&leDHCe{HZ)scDR5|P zO!8U6bN;xImddWXrOC+Jlg9lXGQNXvhs^%L?5&JKIYXVK+74$no**QIho!t4v^BQl zj~5`wsgL*?KF;yvN_)CH&o` zpB*1fMzNl;`8ZmluMUt%9yB~jk1AZh?8K3kG2NGoyCTXZcufaxpNkDEnhFFDbuVO1?riCYZzHP6WOvG=OzKj*N5dwp~LvgxYp z;dJu6854z=9=d>H- zhA3|^^4s2?*eBNa{s1Hc>l?p-h1%MgN@aFd=k!&*lh$QneBj*hJ?WSn!0uSZR`qAneqKW?D&&Rn%)5^u zn1)f0yuksPGx)uRR4)gx&?eTu42!}?_gohbI*u`E_4L0t+!eKH0T-$R&9*jqe9i`;=s&B0>Skn${pg}{)WDpOi6k`C9hTO^Q5OhsB{avU@o-B&-D zab>6?!c|z!KY6gRgcs>7m{jnT)z-83q>#3E5+Wotug}(T(((F%`y~%b>;em$tRZY_ z+O>YdfTg7WD-?9wc~KGb&Cii*+%K@m}UkpO}SkrqUh-pfm9 z2|^4?z#v6I2p}L&LKRdL2sPBuMg<*Of)FAQn$o2A7DV3(PH`OP$IGvLU-IRgdvoqy zYp=8RK5|-I_fKqQIQup4f!p)eOiJD&bgkJk+(Pn|M;RKg7M=nua~~CN`+b)1=*mCu zCa|SGirkT&^4?B@`sVCPg)Qrn9wJb6+efW8 zcEe`gSW7^L_$IWeC+C`K5n)0H^uLS9QTetm#T;mbm~xMZTksIv&ml&aa4Iu5d{pg3NX7iKIIyGK5Wn^$4by_~S~UM76*| z`worV#6HUS@Dti11cz2S>~HIdaiK0H;lE(WFtmcJS>SI`bT2h>^tbdGXhY#sa2<7H_S_xZd2LN2hyd z$SRrVSh-gX-6VZ_xl&o(?|B0JRq11QzZY|bKZn_D3}Dau1AkJ9pJ#h2JEVf%&V{-? z9#-@+x5}k=-mSFTR{cf`T&$6&Dsng~F{hBvRaQ1==B8h*oi<>N7k`Lv1N&8e=m6n0 zn^wcz@E6VP{EhNoPY$W<5~~uW&?Z6Q+w;!o^!J#|sDhI?BU>D$FVjafSBYuHiWD7n zIN3D9o=5Tb^b8y9`*migK7C|TR71T_$vqK6@`SC>pHA|-=}L4pwJp^yokb7(PFN-R z;Www1A#<%6kd+tTk(Hl;F6f>kQ529OIA+kC(n#}lRq5<*zwS6nm=4(1l$kQ7rk(C# zW#(Qq-%4<|9Mjp2PefgcczvDtI z1_hAv$Eu?#X+)*Yf~dl0=R&up+bFWWH`qcCVcx@0A_CZGc` z(yF)W!K$YYTCq#6O?{o7JUG>XUBi{FuAkry-uBPIeg?}hY+!LJzX0N5PlXt*$PRr0 zi8$?3I&9ocU%I%`DNV)bZLE@%3DG&?zcK4W7GltfCM}YJS0%_}aBnCuSKrm8& z?%IEjOK=xO7Cui-s$+ziJ*t)rUAXVm7M!aVcqPIlT%p>b+kg@kr6R&GL0$E~#1#SJK)D6H58q0NiM=sx+SN6b#dA+3u^mI?sdr$RtrLbPT+BEzW~6Ftm; zu&kn?rs%owms}H^W!1MA@)&1QS;ehT9nrF*;XJ8FVkRHOJHIkt7vlES zAzwwc(~$I#s*>YTf+CY+hy5ZCwkXsjv{?eYuwUKsO~u_Y&gLWlpL+*^`smzygL|LH zm7^5C(|nX3(=fivbSkJ3Z=HBT5Wex@qcl=iv;<~_TsQVQ)1j{uX!A6NLFDv(6F122 z@2!g!fx4jN_zU>wegh{z0s}aYaZbVcyaY|@Zb{aTs!JkkyU(t=)kOfV?2zEJwNJq` zOyGJ7qY=q#rYME7ew(0&e7qD&4c8|eR=UA<;?R18(9>U|jAs=2?y6p@l0Lab^~y-7 zG>_!_9=MQw7uEk1t#l7MbwIXpa|}hVq-fQ7eYq{2-gUtS@;l?Z?oCLK2ADUx_l@^6 zcqQsMN+`$`F!vLJR|PVH=Y3nL!4%)V%)2ly_pM388~`HBz(v|jg z_L4eNqE<*$v52(mOVR~)jPN&bh0z@(cZFp#>hVPHJDm`49pw}xciB-vU6mo`YwZTB zNOGK(O*qkqmh|~JyLdqoxB{p?^%B1>AG>*8+NWkChGeq>sl+Kl9BlGB{t272{d+ZE z>|sIEV_TOZjs@9wDvOlvJT4U)qzu-R=@&oHc#X62OM&u}(5kTW0+}RH8@nCCY(5VF z7IuWHE;L*b8m2!rUMem&YVc9n*IA zcyrFNj;SR3`!Y6fLYflgVingV;?>p;MLrsjT1q%KntVNbEru^=uL_-Ha6kP6>tQK} zeM@g`Dq-?ami+y9@FV$TRONNNVqY3bp$Q*K_4?)Ek#W2KAK~&fq_Qz~C0#;ox@_@0 z2~$cLD{;Pau3;IXr#j&&y)@@nmt(YmVtaU!-7~_7M79;g=W9 zuxF8=!No<2cs{>V^0Ve;>t>E#=Td}-@aQ(}$jnld5wEuf(do2@Uy-DqM_)geP`7eZmHpAYDu3?zb@AxpU4iu&uKdX;@Ha zWXB>#n@W?A;72L9PwH-4xGkvr=QFnh@&p$wTl?3H(ON_p(J@a4`yttV;$=s(1iby8 zJ0>H1s-(rnFq5Q*Ug=|on7h-zmbY5Y(6H7nFXWqJikrOG>`WdWL6HK0mH-O1+Ks57 z9X}QMiyHYBbMAc_*=uEWwtAFR1aOX+JbWT6Iaa1gzh>Y8(6TzP!-y%X{Mm+7@-yqI z7uYo;^@-+$1!%(^h0#dWKBX-t6+i)RtzOr^xMG+K-<^2lemag-c6z7x9+u;=*!Si| z`-N-A$jWECR{F~2O_xA*M~31l8Ct~VoEWS*X>}s$V_M6}uzGQ}>1vqkLz_op4bv+F z_2M$L;J?0B2M38;aqz!ep9=3Y{D)%f61Oe}tvd1b1j8=LZTh0*S-vQlC!XBGPdAO* zmHU?XH7L|5BTD^Jrt=zkY|L~j59 literal 0 HcmV?d00001 diff --git a/event-sourcing/etc/event-sourcing.ucls b/event-sourcing/etc/event-sourcing.ucls new file mode 100644 index 000000000..d187b1bb0 --- /dev/null +++ b/event-sourcing/etc/event-sourcing.ucls @@ -0,0 +1,263 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 82d7e57e8eb8a425f5af87bc33ed850ec72f382b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Serdar=20Hamzao=C4=9Fullar=C4=B1?= Date: Sat, 19 Aug 2017 22:06:35 +0300 Subject: [PATCH 6/7] Integration Test --- event-sourcing/etc/event-sourcing.ucls | 4 +- .../{service => }/AccountService.java | 4 +- .../MoneyTransactionService.java | 4 +- .../{service => }/SequenceIdGenerator.java | 4 +- .../event/sourcing/api/DomainEvent.java | 2 +- .../event/sourcing/api/EventProcessor.java | 2 +- .../event/sourcing/api/ProcessorJournal.java | 2 +- .../com/iluwatar/event/sourcing/app/App.java | 66 ++++++---- .../event/sourcing/domain/Account.java | 2 +- .../event/sourcing/domain/Transaction.java | 2 +- .../sourcing/event/AccountCreateEvent.java | 2 +- .../sourcing/event/MoneyDepositEvent.java | 2 +- .../sourcing/event/MoneyTransferEvent.java | 2 +- .../sourcing/event/MoneyWithdrawalEvent.java | 2 +- .../gateway/AccountCreateContractSender.java | 2 +- .../event/sourcing/gateway/Gateways.java | 2 +- .../sourcing/gateway/TransactionLogger.java | 2 +- .../sourcing/journal/JsonFileJournal.java | 2 +- .../processor/DomainEventProcessor.java | 2 +- .../sourcing/state/AccountAggregate.java | 2 +- .../event/sourcing/IntegrationTest.java | 115 ++++++++++++++++++ 21 files changed, 181 insertions(+), 46 deletions(-) rename event-sourcing/src/main/java/com/iluwatar/event/sourcing/{service => }/AccountService.java (95%) rename event-sourcing/src/main/java/com/iluwatar/event/sourcing/{service => }/MoneyTransactionService.java (97%) rename event-sourcing/src/main/java/com/iluwatar/event/sourcing/{service => }/SequenceIdGenerator.java (94%) create mode 100644 event-sourcing/src/main/test/java/com/iluwatar/event/sourcing/IntegrationTest.java diff --git a/event-sourcing/etc/event-sourcing.ucls b/event-sourcing/etc/event-sourcing.ucls index d187b1bb0..e6944ef70 100644 --- a/event-sourcing/etc/event-sourcing.ucls +++ b/event-sourcing/etc/event-sourcing.ucls @@ -122,7 +122,7 @@ - @@ -132,7 +132,7 @@ - diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/service/AccountService.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/AccountService.java similarity index 95% rename from event-sourcing/src/main/java/com/iluwatar/event/sourcing/service/AccountService.java rename to event-sourcing/src/main/java/com/iluwatar/event/sourcing/AccountService.java index 59cefaaee..2d6f585a5 100644 --- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/service/AccountService.java +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/AccountService.java @@ -20,14 +20,14 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package com.iluwatar.event.sourcing.service; +package com.iluwatar.event.sourcing; import com.iluwatar.event.sourcing.api.EventProcessor; import com.iluwatar.event.sourcing.event.AccountCreateEvent; import java.util.Date; /** - * Created by serdarh on 06.08.2017. + * Created by Serdar Hamzaogullari on 06.08.2017. */ public class AccountService { diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/service/MoneyTransactionService.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/MoneyTransactionService.java similarity index 97% rename from event-sourcing/src/main/java/com/iluwatar/event/sourcing/service/MoneyTransactionService.java rename to event-sourcing/src/main/java/com/iluwatar/event/sourcing/MoneyTransactionService.java index 5f7c0e9c7..0e3cb67bd 100644 --- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/service/MoneyTransactionService.java +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/MoneyTransactionService.java @@ -20,7 +20,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package com.iluwatar.event.sourcing.service; +package com.iluwatar.event.sourcing; import com.iluwatar.event.sourcing.api.EventProcessor; import com.iluwatar.event.sourcing.event.MoneyDepositEvent; @@ -30,7 +30,7 @@ import java.math.BigDecimal; import java.util.Date; /** - * Created by serdarh on 06.08.2017. + * Created by Serdar Hamzaogullari on 06.08.2017. */ public class MoneyTransactionService { diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/service/SequenceIdGenerator.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/SequenceIdGenerator.java similarity index 94% rename from event-sourcing/src/main/java/com/iluwatar/event/sourcing/service/SequenceIdGenerator.java rename to event-sourcing/src/main/java/com/iluwatar/event/sourcing/SequenceIdGenerator.java index 6e1730679..c928a8737 100644 --- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/service/SequenceIdGenerator.java +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/SequenceIdGenerator.java @@ -20,10 +20,10 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package com.iluwatar.event.sourcing.service; +package com.iluwatar.event.sourcing; /** - * Created by serdarh on 06.08.2017. + * Created by Serdar Hamzaogullari on 06.08.2017. */ public class SequenceIdGenerator { diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/api/DomainEvent.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/api/DomainEvent.java index 693ea1755..6eb5141a2 100644 --- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/api/DomainEvent.java +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/api/DomainEvent.java @@ -25,7 +25,7 @@ package com.iluwatar.event.sourcing.api; import java.io.Serializable; /** - * Created by serdarh on 06.08.2017. + * Created by Serdar Hamzaogullari on 06.08.2017. */ public abstract class DomainEvent implements Serializable { diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/api/EventProcessor.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/api/EventProcessor.java index afa218939..512f9d7f8 100644 --- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/api/EventProcessor.java +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/api/EventProcessor.java @@ -23,7 +23,7 @@ package com.iluwatar.event.sourcing.api; /** - * Created by serdarh on 06.08.2017. + * Created by Serdar Hamzaogullari on 06.08.2017. */ public interface EventProcessor { diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/api/ProcessorJournal.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/api/ProcessorJournal.java index 8814b82d9..2212580dc 100644 --- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/api/ProcessorJournal.java +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/api/ProcessorJournal.java @@ -23,7 +23,7 @@ package com.iluwatar.event.sourcing.api; /** - * Created by serdarh on 06.08.2017. + * Created by Serdar Hamzaogullari on 06.08.2017. */ public interface ProcessorJournal { diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/app/App.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/app/App.java index f62cebb62..77be00cf6 100644 --- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/app/App.java +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/app/App.java @@ -24,62 +24,82 @@ package com.iluwatar.event.sourcing.app; import com.iluwatar.event.sourcing.journal.JsonFileJournal; import com.iluwatar.event.sourcing.processor.DomainEventProcessor; -import com.iluwatar.event.sourcing.service.AccountService; -import com.iluwatar.event.sourcing.service.MoneyTransactionService; +import com.iluwatar.event.sourcing.AccountService; +import com.iluwatar.event.sourcing.MoneyTransactionService; import com.iluwatar.event.sourcing.state.AccountAggregate; import java.math.BigDecimal; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** - * Created by serdarh on 06.08.2017. + * Event Sourcing : Instead of storing just the current state of the data in a domain, use an + * append-only store to record the full series of actions taken on that data. The store acts as the + * system of record and can be used to materialize the domain objects. This can simplify tasks in + * complex domains, by avoiding the need to synchronize the data model and the business domain, + * while improving performance, scalability, and responsiveness. It can also provide consistency for + * transactional data, and maintain full audit trails and history that can enable compensating + * actions. + * + * This App class is an example usage of Event Sourcing pattern. As an example, two bank account is + * created, then some money deposit and transfer actions are taken so a new state of accounts is + * created. At that point, state is cleared in order to represent a system shot down. After the shot + * down, system state is recovered by re-creating the past events from event journal. Then state is + * printed so a user can view the last state is same with the state before system shot down. + * + * Created by Serdar Hamzaogullari on 06.08.2017. */ public class App { + private static final Logger LOGGER = LoggerFactory.getLogger(App.class); + public static final int ACCOUNT_OF_DAENERYS = 1; + public static final int ACCOUNT_OF_JON = 2; + /** * The entry point of application. * * @param args the input arguments */ public static void main(String[] args) { - System.out.println("Running the system first time............"); DomainEventProcessor domainEventProcessor = new DomainEventProcessor(); JsonFileJournal jsonFileJournal = new JsonFileJournal(); - jsonFileJournal.reset(); domainEventProcessor.setPrecessorJournal(jsonFileJournal); - - System.out.println("Creating th accounts............"); - AccountService accountService = new AccountService(domainEventProcessor); MoneyTransactionService moneyTransactionService = new MoneyTransactionService( domainEventProcessor); - accountService.createAccount(1, "Daenerys Targaryen"); - accountService.createAccount(2, "Jon Snow"); - System.out.println("Do some money operations............"); + LOGGER.info("Running the system first time............"); + jsonFileJournal.reset(); - moneyTransactionService.depositMoney(1, new BigDecimal("100000")); - moneyTransactionService.depositMoney(2, new BigDecimal("100")); + LOGGER.info("Creating th accounts............"); - moneyTransactionService.transferMoney(1, 2, new BigDecimal("10000")); - moneyTransactionService.withdrawalMoney(2, new BigDecimal("1000")); + accountService.createAccount(ACCOUNT_OF_DAENERYS, "Daenerys Targaryen"); + accountService.createAccount(ACCOUNT_OF_JON, "Jon Snow"); - System.out.println("...............State:............"); - System.out.println(AccountAggregate.getAccount(1)); - System.out.println(AccountAggregate.getAccount(2)); + LOGGER.info("Do some money operations............"); - System.out.println("At that point system goes down state in memory cleared............"); + moneyTransactionService.depositMoney(ACCOUNT_OF_DAENERYS, new BigDecimal("100000")); + moneyTransactionService.depositMoney(ACCOUNT_OF_JON, new BigDecimal("100")); + moneyTransactionService.transferMoney(ACCOUNT_OF_DAENERYS, ACCOUNT_OF_JON, new BigDecimal("10000")); + moneyTransactionService.withdrawalMoney(ACCOUNT_OF_JON, new BigDecimal("1000")); + + LOGGER.info("...............State:............"); + LOGGER.info(AccountAggregate.getAccount(ACCOUNT_OF_DAENERYS).toString()); + LOGGER.info(AccountAggregate.getAccount(ACCOUNT_OF_JON).toString()); + + LOGGER.info("At that point system had a shot down, state in memory is cleared............"); AccountAggregate.resetState(); - System.out.println("Recover the syste by the events in journal file............"); + LOGGER.info("Recover the system by the events in journal file............"); domainEventProcessor = new DomainEventProcessor(); jsonFileJournal = new JsonFileJournal(); domainEventProcessor.setPrecessorJournal(jsonFileJournal); domainEventProcessor.recover(); - System.out.println("...............State Recovered:............"); - System.out.println(AccountAggregate.getAccount(1)); - System.out.println(AccountAggregate.getAccount(2)); + LOGGER.info("...............Recovered State:............"); + LOGGER.info(AccountAggregate.getAccount(ACCOUNT_OF_DAENERYS).toString()); + LOGGER.info(AccountAggregate.getAccount(ACCOUNT_OF_JON).toString()); } } diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/domain/Account.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/domain/Account.java index 9e1bc560e..b6aab7791 100644 --- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/domain/Account.java +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/domain/Account.java @@ -33,7 +33,7 @@ import java.util.ArrayList; import java.util.List; /** - * Created by serdarh on 06.08.2017. + * Created by Serdar Hamzaogullari on 06.08.2017. */ public class Account { diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/domain/Transaction.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/domain/Transaction.java index 29cdc4d15..a0d921f5f 100644 --- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/domain/Transaction.java +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/domain/Transaction.java @@ -25,7 +25,7 @@ package com.iluwatar.event.sourcing.domain; import java.math.BigDecimal; /** - * Created by serdarh on 06.08.2017. + * Created by Serdar Hamzaogullari on 06.08.2017. */ public class Transaction { diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/AccountCreateEvent.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/AccountCreateEvent.java index 8356cd3e8..350104267 100644 --- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/AccountCreateEvent.java +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/AccountCreateEvent.java @@ -27,7 +27,7 @@ import com.iluwatar.event.sourcing.domain.Account; import com.iluwatar.event.sourcing.state.AccountAggregate; /** - * Created by serdarh on 06.08.2017. + * Created by Serdar Hamzaogullari on 06.08.2017. */ public class AccountCreateEvent extends DomainEvent { diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyDepositEvent.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyDepositEvent.java index 9d73639eb..3fb61bd08 100644 --- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyDepositEvent.java +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyDepositEvent.java @@ -28,7 +28,7 @@ import com.iluwatar.event.sourcing.state.AccountAggregate; import java.math.BigDecimal; /** - * Created by serdarh on 06.08.2017. + * Created by Serdar Hamzaogullari on 06.08.2017. */ public class MoneyDepositEvent extends DomainEvent { diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyTransferEvent.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyTransferEvent.java index 995fb9326..bbba89988 100644 --- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyTransferEvent.java +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyTransferEvent.java @@ -28,7 +28,7 @@ import com.iluwatar.event.sourcing.state.AccountAggregate; import java.math.BigDecimal; /** - * Created by serdarh on 06.08.2017. + * Created by Serdar Hamzaogullari on 06.08.2017. */ public class MoneyTransferEvent extends DomainEvent { diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyWithdrawalEvent.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyWithdrawalEvent.java index 64fddc16e..d8c295002 100644 --- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyWithdrawalEvent.java +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyWithdrawalEvent.java @@ -28,7 +28,7 @@ import com.iluwatar.event.sourcing.state.AccountAggregate; import java.math.BigDecimal; /** - * Created by serdarh on 06.08.2017. + * Created by Serdar Hamzaogullari on 06.08.2017. */ public class MoneyWithdrawalEvent extends DomainEvent { diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/gateway/AccountCreateContractSender.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/gateway/AccountCreateContractSender.java index aa15f746f..baaf41f56 100644 --- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/gateway/AccountCreateContractSender.java +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/gateway/AccountCreateContractSender.java @@ -25,7 +25,7 @@ package com.iluwatar.event.sourcing.gateway; import com.iluwatar.event.sourcing.domain.Account; /** - * Created by serdarh on 06.08.2017. + * Created by Serdar Hamzaogullari on 06.08.2017. */ public class AccountCreateContractSender { diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/gateway/Gateways.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/gateway/Gateways.java index 932338c32..84bdff3b2 100644 --- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/gateway/Gateways.java +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/gateway/Gateways.java @@ -23,7 +23,7 @@ package com.iluwatar.event.sourcing.gateway; /** - * Created by serdarh on 06.08.2017. + * Created by Serdar Hamzaogullari on 06.08.2017. */ public class Gateways { diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/gateway/TransactionLogger.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/gateway/TransactionLogger.java index 3cfea0751..42ae7a1f5 100644 --- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/gateway/TransactionLogger.java +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/gateway/TransactionLogger.java @@ -25,7 +25,7 @@ package com.iluwatar.event.sourcing.gateway; import com.iluwatar.event.sourcing.domain.Transaction; /** - * Created by serdarh on 06.08.2017. + * Created by Serdar Hamzaogullari on 06.08.2017. */ public class TransactionLogger { diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/journal/JsonFileJournal.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/journal/JsonFileJournal.java index a5d82e5be..9379e7b5c 100644 --- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/journal/JsonFileJournal.java +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/journal/JsonFileJournal.java @@ -44,7 +44,7 @@ import java.util.ArrayList; import java.util.List; /** - * Created by serdarh on 06.08.2017. + * Created by Serdar Hamzaogullari on 06.08.2017. */ public class JsonFileJournal implements ProcessorJournal { diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/processor/DomainEventProcessor.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/processor/DomainEventProcessor.java index 81ec2e761..38e72995d 100644 --- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/processor/DomainEventProcessor.java +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/processor/DomainEventProcessor.java @@ -27,7 +27,7 @@ import com.iluwatar.event.sourcing.api.EventProcessor; import com.iluwatar.event.sourcing.api.ProcessorJournal; /** - * Created by serdarh on 06.08.2017. + * Created by Serdar Hamzaogullari on 06.08.2017. */ public class DomainEventProcessor implements EventProcessor { diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/state/AccountAggregate.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/state/AccountAggregate.java index a42826160..cfe0cfbb3 100644 --- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/state/AccountAggregate.java +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/state/AccountAggregate.java @@ -27,7 +27,7 @@ import java.util.HashMap; import java.util.Map; /** - * Created by serdarh on 06.08.2017. + * Created by Serdar Hamzaogullari on 06.08.2017. */ public class AccountAggregate { diff --git a/event-sourcing/src/main/test/java/com/iluwatar/event/sourcing/IntegrationTest.java b/event-sourcing/src/main/test/java/com/iluwatar/event/sourcing/IntegrationTest.java new file mode 100644 index 000000000..8fbed333d --- /dev/null +++ b/event-sourcing/src/main/test/java/com/iluwatar/event/sourcing/IntegrationTest.java @@ -0,0 +1,115 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.event.sourcing; + +import static com.iluwatar.event.sourcing.app.App.ACCOUNT_OF_DAENERYS; +import static com.iluwatar.event.sourcing.app.App.ACCOUNT_OF_JON; + +import com.iluwatar.event.sourcing.domain.Account; +import com.iluwatar.event.sourcing.journal.JsonFileJournal; +import com.iluwatar.event.sourcing.processor.DomainEventProcessor; +import com.iluwatar.event.sourcing.state.AccountAggregate; +import java.math.BigDecimal; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +/** + * Intergartion Test for Event Sourcing state recovery + * + * Created by Serdar Hamzaogullari on 19.08.2017. + */ +public class IntegrationTest { + + /** + * The Domain event processor. + */ + DomainEventProcessor domainEventProcessor; + /** + * The Json file journal. + */ + JsonFileJournal jsonFileJournal; + /** + * The Account service. + */ + AccountService accountService; + /** + * The Money transaction service. + */ + MoneyTransactionService moneyTransactionService; + + /** + * Initialize. + */ + @Before + public void initialize() { + domainEventProcessor = new DomainEventProcessor(); + jsonFileJournal = new JsonFileJournal(); + domainEventProcessor.setPrecessorJournal(jsonFileJournal); + accountService = new AccountService(domainEventProcessor); + moneyTransactionService = new MoneyTransactionService( + domainEventProcessor); + } + + /** + * Test state recovery. + */ + @Test + public void testStateRecovery() { + jsonFileJournal.reset(); + + accountService.createAccount(ACCOUNT_OF_DAENERYS, "Daenerys Targaryen"); + accountService.createAccount(ACCOUNT_OF_JON, "Jon Snow"); + + moneyTransactionService.depositMoney(ACCOUNT_OF_DAENERYS, new BigDecimal("100000")); + moneyTransactionService.depositMoney(ACCOUNT_OF_JON, new BigDecimal("100")); + + moneyTransactionService + .transferMoney(ACCOUNT_OF_DAENERYS, ACCOUNT_OF_JON, new BigDecimal("10000")); + moneyTransactionService.withdrawalMoney(ACCOUNT_OF_JON, new BigDecimal("1000")); + + Account accountOfDaenerysBeforeShotDown = AccountAggregate.getAccount(ACCOUNT_OF_DAENERYS); + Account accountOfJonBeforeShotDown = AccountAggregate.getAccount(ACCOUNT_OF_JON); + + AccountAggregate.resetState(); + + domainEventProcessor = new DomainEventProcessor(); + jsonFileJournal = new JsonFileJournal(); + domainEventProcessor.setPrecessorJournal(jsonFileJournal); + domainEventProcessor.recover(); + + Account accountOfDaenerysAfterShotDown = AccountAggregate.getAccount(ACCOUNT_OF_DAENERYS); + Account accountOfJonAfterShotDown = AccountAggregate.getAccount(ACCOUNT_OF_JON); + + Assert.assertEquals(accountOfDaenerysBeforeShotDown.getMoney(), + accountOfDaenerysAfterShotDown.getMoney()); + Assert + .assertEquals(accountOfJonBeforeShotDown.getMoney(), accountOfJonAfterShotDown.getMoney()); + Assert.assertEquals(accountOfDaenerysBeforeShotDown.getTransactions().size(), + accountOfDaenerysAfterShotDown.getTransactions().size()); + Assert.assertEquals(accountOfJonBeforeShotDown.getTransactions().size(), + accountOfJonAfterShotDown.getTransactions().size()); + + } + +} \ No newline at end of file From c6354c48bb0f62c3030d63a824812173263adac9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Serdar=20Hamzao=C4=9Fullar=C4=B1?= Date: Sat, 2 Sep 2017 21:52:02 +0300 Subject: [PATCH 7/7] - removed optional classes and interfaces in order to simplify the example - final fields are marked as final - removed unnecessary temp variables - added private constructor for not instantiated static class AccountAggregate - path of te test file is corrected --- event-sourcing/etc/event-sourcing.png | Bin 105812 -> 60339 bytes event-sourcing/etc/event-sourcing.ucls | 252 ++++-------------- .../event/sourcing/AccountService.java | 56 ---- .../sourcing/MoneyTransactionService.java | 85 ------ .../event/sourcing/SequenceIdGenerator.java | 42 --- .../event/sourcing/api/EventProcessor.java | 48 ---- .../event/sourcing/api/ProcessorJournal.java | 48 ---- .../com/iluwatar/event/sourcing/app/App.java | 50 ++-- .../event/sourcing/domain/Account.java | 71 ++--- .../event/sourcing/domain/Transaction.java | 98 ------- .../sourcing/event/AccountCreateEvent.java | 6 +- .../sourcing/{api => event}/DomainEvent.java | 4 +- .../sourcing/event/MoneyDepositEvent.java | 10 +- .../sourcing/event/MoneyTransferEvent.java | 16 +- .../sourcing/event/MoneyWithdrawalEvent.java | 78 ------ .../gateway/AccountCreateContractSender.java | 40 --- .../event/sourcing/gateway/Gateways.java | 50 ---- .../sourcing/gateway/TransactionLogger.java | 40 --- .../processor/DomainEventProcessor.java | 34 ++- .../JsonFileJournal.java | 42 +-- .../sourcing/state/AccountAggregate.java | 8 +- .../java}/IntegrationTest.java | 60 ++--- 22 files changed, 207 insertions(+), 931 deletions(-) delete mode 100644 event-sourcing/src/main/java/com/iluwatar/event/sourcing/AccountService.java delete mode 100644 event-sourcing/src/main/java/com/iluwatar/event/sourcing/MoneyTransactionService.java delete mode 100644 event-sourcing/src/main/java/com/iluwatar/event/sourcing/SequenceIdGenerator.java delete mode 100644 event-sourcing/src/main/java/com/iluwatar/event/sourcing/api/EventProcessor.java delete mode 100644 event-sourcing/src/main/java/com/iluwatar/event/sourcing/api/ProcessorJournal.java delete mode 100644 event-sourcing/src/main/java/com/iluwatar/event/sourcing/domain/Transaction.java rename event-sourcing/src/main/java/com/iluwatar/event/sourcing/{api => event}/DomainEvent.java (95%) delete mode 100644 event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyWithdrawalEvent.java delete mode 100644 event-sourcing/src/main/java/com/iluwatar/event/sourcing/gateway/AccountCreateContractSender.java delete mode 100644 event-sourcing/src/main/java/com/iluwatar/event/sourcing/gateway/Gateways.java delete mode 100644 event-sourcing/src/main/java/com/iluwatar/event/sourcing/gateway/TransactionLogger.java rename event-sourcing/src/main/java/com/iluwatar/event/sourcing/{journal => processor}/JsonFileJournal.java (84%) rename event-sourcing/src/{main/test/java/com/iluwatar/event/sourcing => test/java}/IntegrationTest.java (59%) diff --git a/event-sourcing/etc/event-sourcing.png b/event-sourcing/etc/event-sourcing.png index ac7192b195fb67b1d67c1b5c0e5fcc3a5cfd6fb0..0b0098546300e34203b06fefec325f0a81e1d1f7 100644 GIT binary patch literal 60339 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%0AKM&}B6&mY86G=qr~XfB};B`_tT#9i`_L z(7Xc6%eqv&?UHtT7iV7XC1!((wai=i6$b<4Q3FJ>vTjL-Bj4cLq@nn>LEWV)9ax`V z-Er*nKWp&$pM9$&8qQDctbcFka#8!o;y5YmGBB4h_Q2|uFH~F8=|f+(g08ISj=v~( z5yxSO4lME1UO}Y2!)NQ2;^T0(jWC7lun(Be(dr}0CZMAgZQAz{14fouYq{@OmE*)5 z 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 literal 105812 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 M|6O9HyGJ!3|Bxmyy?LP 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 diff --git a/event-sourcing/etc/event-sourcing.ucls b/event-sourcing/etc/event-sourcing.ucls index e6944ef70..2df56a479 100644 --- a/event-sourcing/etc/event-sourcing.ucls +++ b/event-sourcing/etc/event-sourcing.ucls @@ -1,50 +1,50 @@ - - - + - - - - - - - - - - - - - - - - + - - + + + + + + + + + + + + + + + @@ -55,203 +55,63 @@ project="event-sourcing" file="/event-sourcing/src/main/java/com/iluwatar/event/sourcing/processor/DomainEventProcessor.java" binary="false" corner="BOTTOM_RIGHT"> - + - - + - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - + + - - - - - - - - - + - - + + + + + + - + - - - - - - - - - - + + - - - - + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + transactions; /** * Instantiates a new Account. @@ -52,7 +55,6 @@ public class Account { this.accountNo = accountNo; this.owner = owner; money = BigDecimal.ZERO; - transactions = new ArrayList<>(); } /** @@ -91,23 +93,6 @@ public class Account { this.money = money; } - /** - * Gets transactions. - * - * @return the transactions - */ - public List getTransactions() { - return transactions; - } - - /** - * Sets transactions. - * - * @param transactions the transactions - */ - public void setTransactions(List transactions) { - this.transactions = transactions; - } /** * Copy account. @@ -117,7 +102,6 @@ public class Account { public Account copy() { Account account = new Account(accountNo, owner); account.setMoney(money); - account.setTransactions(transactions); return account; } @@ -127,29 +111,22 @@ public class Account { + "accountNo=" + accountNo + ", owner='" + owner + '\'' + ", money=" + money - + ", transactions=" + transactions + '}'; } - private Transaction depositMoney(BigDecimal money) { + private void depositMoney(BigDecimal money) { this.money = this.money.add(money); - Transaction transaction = new Transaction(accountNo, money, BigDecimal.ZERO, this.money); - transactions.add(transaction); - return transaction; } - private Transaction withdrawMoney(BigDecimal money) { + private void withdrawMoney(BigDecimal money) { this.money = this.money.subtract(money); - Transaction transaction = new Transaction(accountNo, BigDecimal.ZERO, money, this.money); - transactions.add(transaction); - return transaction; } private void handleDeposit(BigDecimal money, boolean realTime) { - Transaction transaction = depositMoney(money); + depositMoney(money); AccountAggregate.putAccount(this); if (realTime) { - Gateways.getTransactionLogger().log(transaction); + LOGGER.info("Some external api for only realtime execution could be called here."); } } @@ -158,15 +135,15 @@ public class Account { throw new RuntimeException("Insufficient Account Balance"); } - Transaction transaction = withdrawMoney(money); + withdrawMoney(money); AccountAggregate.putAccount(this); if (realTime) { - Gateways.getTransactionLogger().log(transaction); + LOGGER.info("Some external api for only realtime execution could be called here."); } } /** - * Handle event. + * Handles the MoneyDepositEvent. * * @param moneyDepositEvent the money deposit event */ @@ -176,29 +153,19 @@ public class Account { /** - * Handle event. - * - * @param moneyWithdrawalEvent the money withdrawal event - */ - public void handleEvent(MoneyWithdrawalEvent moneyWithdrawalEvent) { - handleWithdrawal(moneyWithdrawalEvent.getMoney(), moneyWithdrawalEvent.isRealTime()); - } - - /** - * Handle event. + * Handles the AccountCreateEvent. * * @param accountCreateEvent the account create event */ public void handleEvent(AccountCreateEvent accountCreateEvent) { AccountAggregate.putAccount(this); - // check if this event is replicated from journal before calling an external gateway function if (accountCreateEvent.isRealTime()) { - Gateways.getAccountCreateContractSender().sendContractInfo(this); + LOGGER.info("Some external api for only realtime execution could be called here."); } } /** - * Handle transfer from event. + * Handles transfer from account event. * * @param moneyTransferEvent the money transfer event */ @@ -207,7 +174,7 @@ public class Account { } /** - * Handle transfer to event. + * Handles transfer to account event. * * @param moneyTransferEvent the money transfer event */ diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/domain/Transaction.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/domain/Transaction.java deleted file mode 100644 index a0d921f5f..000000000 --- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/domain/Transaction.java +++ /dev/null @@ -1,98 +0,0 @@ -/** - * The MIT License - * Copyright (c) 2014 Ilkka Seppälä - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package com.iluwatar.event.sourcing.domain; - -import java.math.BigDecimal; - -/** - * Created by Serdar Hamzaogullari on 06.08.2017. - */ -public class Transaction { - - private final int accountNo; - private final BigDecimal moneyIn; - private final BigDecimal moneyOut; - private final BigDecimal lastBalance; - - /** - * Instantiates a new Transaction. - * - * @param accountNo the account no - * @param moneyIn the money in - * @param moneyOut the money out - * @param lastBalance the last balance - */ - public Transaction(int accountNo, BigDecimal moneyIn, BigDecimal moneyOut, - BigDecimal lastBalance) { - this.accountNo = accountNo; - this.moneyIn = moneyIn; - this.moneyOut = moneyOut; - this.lastBalance = lastBalance; - } - - /** - * Gets account no. - * - * @return the account no - */ - public int getAccountNo() { - return accountNo; - } - - /** - * Gets money in. - * - * @return the money in - */ - public BigDecimal getMoneyIn() { - return moneyIn; - } - - /** - * Gets money out. - * - * @return the money out - */ - public BigDecimal getMoneyOut() { - return moneyOut; - } - - /** - * Gets last balance. - * - * @return the last balance - */ - public BigDecimal getLastBalance() { - return lastBalance; - } - - @Override - public String toString() { - return "Transaction{" - + "accountNo=" + accountNo - + ", moneyIn=" + moneyIn - + ", moneyOut=" + moneyOut - + ", lastBalance=" + lastBalance - + '}'; - } -} diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/AccountCreateEvent.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/AccountCreateEvent.java index 350104267..c526396f3 100644 --- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/AccountCreateEvent.java +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/AccountCreateEvent.java @@ -22,11 +22,15 @@ */ package com.iluwatar.event.sourcing.event; -import com.iluwatar.event.sourcing.api.DomainEvent; import com.iluwatar.event.sourcing.domain.Account; import com.iluwatar.event.sourcing.state.AccountAggregate; /** + * This is the class that implements account create event. + * Holds the necessary info for an account create event. + * Implements the process function that finds the event related + * domain objects and calls the related domain object's handle event functions + * * Created by Serdar Hamzaogullari on 06.08.2017. */ public class AccountCreateEvent extends DomainEvent { diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/api/DomainEvent.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/DomainEvent.java similarity index 95% rename from event-sourcing/src/main/java/com/iluwatar/event/sourcing/api/DomainEvent.java rename to event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/DomainEvent.java index 6eb5141a2..fa6539a4f 100644 --- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/api/DomainEvent.java +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/DomainEvent.java @@ -20,11 +20,13 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package com.iluwatar.event.sourcing.api; +package com.iluwatar.event.sourcing.event; import java.io.Serializable; /** + * This is the base class for domain events. All events must extend this class. + * * Created by Serdar Hamzaogullari on 06.08.2017. */ public abstract class DomainEvent implements Serializable { diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyDepositEvent.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyDepositEvent.java index 3fb61bd08..1629263a7 100644 --- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyDepositEvent.java +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyDepositEvent.java @@ -22,18 +22,22 @@ */ package com.iluwatar.event.sourcing.event; -import com.iluwatar.event.sourcing.api.DomainEvent; import com.iluwatar.event.sourcing.domain.Account; import com.iluwatar.event.sourcing.state.AccountAggregate; import java.math.BigDecimal; /** + * This is the class that implements money deposit event. + * Holds the necessary info for a money deposit event. + * Implements the process function that finds the event related + * domain objects and calls the related domain object's handle event functions + * * Created by Serdar Hamzaogullari on 06.08.2017. */ public class MoneyDepositEvent extends DomainEvent { - private BigDecimal money; - private int accountNo; + private final BigDecimal money; + private final int accountNo; /** * Instantiates a new Money deposit event. diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyTransferEvent.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyTransferEvent.java index bbba89988..0307d3af7 100644 --- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyTransferEvent.java +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyTransferEvent.java @@ -22,19 +22,23 @@ */ package com.iluwatar.event.sourcing.event; -import com.iluwatar.event.sourcing.api.DomainEvent; import com.iluwatar.event.sourcing.domain.Account; import com.iluwatar.event.sourcing.state.AccountAggregate; import java.math.BigDecimal; /** + * This is the class that implements money transfer event. + * Holds the necessary info for a money transfer event. + * Implements the process function that finds the event related + * domain objects and calls the related domain object's handle event functions + * * Created by Serdar Hamzaogullari on 06.08.2017. */ public class MoneyTransferEvent extends DomainEvent { - private BigDecimal money; - private int accountNoFrom; - private int accountNoTo; + private final BigDecimal money; + private final int accountNoFrom; + private final int accountNoTo; /** * Instantiates a new Money transfer event. @@ -63,7 +67,7 @@ public class MoneyTransferEvent extends DomainEvent { } /** - * Gets account no from. + * Gets account no which the money comes from. * * @return the account no from */ @@ -72,7 +76,7 @@ public class MoneyTransferEvent extends DomainEvent { } /** - * Gets account no to. + * Gets account no which the money goes to. * * @return the account no to */ diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyWithdrawalEvent.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyWithdrawalEvent.java deleted file mode 100644 index d8c295002..000000000 --- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyWithdrawalEvent.java +++ /dev/null @@ -1,78 +0,0 @@ -/** - * The MIT License - * Copyright (c) 2014 Ilkka Seppälä - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package com.iluwatar.event.sourcing.event; - -import com.iluwatar.event.sourcing.api.DomainEvent; -import com.iluwatar.event.sourcing.domain.Account; -import com.iluwatar.event.sourcing.state.AccountAggregate; -import java.math.BigDecimal; - -/** - * Created by Serdar Hamzaogullari on 06.08.2017. - */ -public class MoneyWithdrawalEvent extends DomainEvent { - - private BigDecimal money; - private int accountNo; - - /** - * Instantiates a new Money withdrawal event. - * - * @param sequenceId the sequence id - * @param createdTime the created time - * @param accountNo the account no - * @param money the money - */ - public MoneyWithdrawalEvent(long sequenceId, long createdTime, int accountNo, BigDecimal money) { - super(sequenceId, createdTime, "MoneyWithdrawalEvent"); - this.money = money; - this.accountNo = accountNo; - } - - /** - * Gets money. - * - * @return the money - */ - public BigDecimal getMoney() { - return money; - } - - /** - * Gets account no. - * - * @return the account no - */ - public int getAccountNo() { - return accountNo; - } - - @Override - public void process() { - Account account = AccountAggregate.getAccount(accountNo); - if (account == null) { - throw new RuntimeException("Account not found"); - } - account.handleEvent(this); - } -} diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/gateway/AccountCreateContractSender.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/gateway/AccountCreateContractSender.java deleted file mode 100644 index baaf41f56..000000000 --- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/gateway/AccountCreateContractSender.java +++ /dev/null @@ -1,40 +0,0 @@ -/** - * The MIT License - * Copyright (c) 2014 Ilkka Seppälä - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package com.iluwatar.event.sourcing.gateway; - -import com.iluwatar.event.sourcing.domain.Account; - -/** - * Created by Serdar Hamzaogullari on 06.08.2017. - */ -public class AccountCreateContractSender { - - /** - * Send contract info. - * - * @param account the account - */ - public void sendContractInfo(Account account) { - // an example imaginary funciton which sends account info to some external end point - } -} diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/gateway/Gateways.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/gateway/Gateways.java deleted file mode 100644 index 84bdff3b2..000000000 --- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/gateway/Gateways.java +++ /dev/null @@ -1,50 +0,0 @@ -/** - * The MIT License - * Copyright (c) 2014 Ilkka Seppälä - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package com.iluwatar.event.sourcing.gateway; - -/** - * Created by Serdar Hamzaogullari on 06.08.2017. - */ -public class Gateways { - - private static AccountCreateContractSender accountCreateContractSender = new AccountCreateContractSender(); - private static TransactionLogger transactionLogger = new TransactionLogger(); - - /** - * Gets account create contract sender. - * - * @return the account create contract sender - */ - public static AccountCreateContractSender getAccountCreateContractSender() { - return accountCreateContractSender; - } - - /** - * Gets transaction logger. - * - * @return the transaction logger - */ - public static TransactionLogger getTransactionLogger() { - return transactionLogger; - } -} diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/gateway/TransactionLogger.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/gateway/TransactionLogger.java deleted file mode 100644 index 42ae7a1f5..000000000 --- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/gateway/TransactionLogger.java +++ /dev/null @@ -1,40 +0,0 @@ -/** - * The MIT License - * Copyright (c) 2014 Ilkka Seppälä - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package com.iluwatar.event.sourcing.gateway; - -import com.iluwatar.event.sourcing.domain.Transaction; - -/** - * Created by Serdar Hamzaogullari on 06.08.2017. - */ -public class TransactionLogger { - - /** - * Log. - * - * @param transaction the transaction - */ - public void log(Transaction transaction) { - // example imaginary function that logs the transaction to somewhere - } -} diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/processor/DomainEventProcessor.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/processor/DomainEventProcessor.java index 38e72995d..05308c7c1 100644 --- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/processor/DomainEventProcessor.java +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/processor/DomainEventProcessor.java @@ -22,33 +22,43 @@ */ package com.iluwatar.event.sourcing.processor; -import com.iluwatar.event.sourcing.api.DomainEvent; -import com.iluwatar.event.sourcing.api.EventProcessor; -import com.iluwatar.event.sourcing.api.ProcessorJournal; +import com.iluwatar.event.sourcing.event.DomainEvent; /** + * This is the implementation of event processor. + * All events are processed by this class. + * This processor uses processorJournal to persist and recover events. + * * Created by Serdar Hamzaogullari on 06.08.2017. */ -public class DomainEventProcessor implements EventProcessor { +public class DomainEventProcessor { - private ProcessorJournal precessorJournal; + private final JsonFileJournal processorJournal = new JsonFileJournal(); - @Override + /** + * Process. + * + * @param domainEvent the domain event + */ public void process(DomainEvent domainEvent) { domainEvent.process(); - precessorJournal.write(domainEvent); + processorJournal.write(domainEvent); } - @Override - public void setPrecessorJournal(ProcessorJournal precessorJournal) { - this.precessorJournal = precessorJournal; + /** + * Reset. + */ + public void reset() { + processorJournal.reset(); } - @Override + /** + * Recover. + */ public void recover() { DomainEvent domainEvent; while (true) { - domainEvent = precessorJournal.readNext(); + domainEvent = processorJournal.readNext(); if (domainEvent == null) { break; } else { diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/journal/JsonFileJournal.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/processor/JsonFileJournal.java similarity index 84% rename from event-sourcing/src/main/java/com/iluwatar/event/sourcing/journal/JsonFileJournal.java rename to event-sourcing/src/main/java/com/iluwatar/event/sourcing/processor/JsonFileJournal.java index 9379e7b5c..870d8d00f 100644 --- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/journal/JsonFileJournal.java +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/processor/JsonFileJournal.java @@ -20,17 +20,15 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package com.iluwatar.event.sourcing.journal; +package com.iluwatar.event.sourcing.processor; import com.google.gson.Gson; import com.google.gson.JsonElement; import com.google.gson.JsonParser; -import com.iluwatar.event.sourcing.api.DomainEvent; -import com.iluwatar.event.sourcing.api.ProcessorJournal; import com.iluwatar.event.sourcing.event.AccountCreateEvent; +import com.iluwatar.event.sourcing.event.DomainEvent; import com.iluwatar.event.sourcing.event.MoneyDepositEvent; import com.iluwatar.event.sourcing.event.MoneyTransferEvent; -import com.iluwatar.event.sourcing.event.MoneyWithdrawalEvent; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; @@ -44,12 +42,16 @@ import java.util.ArrayList; import java.util.List; /** + * This is the implementation of event journal. + * This implementation serialize/deserialize the events with JSON + * and writes/reads them on a Journal.json file at the working directory. + * * Created by Serdar Hamzaogullari on 06.08.2017. */ -public class JsonFileJournal implements ProcessorJournal { +public class JsonFileJournal { - private File aFile; - private List events = new ArrayList<>(); + private final File aFile; + private final List events = new ArrayList<>(); private int index = 0; /** @@ -72,7 +74,12 @@ public class JsonFileJournal implements ProcessorJournal { } } - @Override + + /** + * Write. + * + * @param domainEvent the domain event + */ public void write(DomainEvent domainEvent) { Gson gson = new Gson(); JsonElement jsonElement; @@ -80,9 +87,7 @@ public class JsonFileJournal implements ProcessorJournal { jsonElement = gson.toJsonTree(domainEvent, AccountCreateEvent.class); } else if (domainEvent instanceof MoneyDepositEvent) { jsonElement = gson.toJsonTree(domainEvent, MoneyDepositEvent.class); - } else if (domainEvent instanceof MoneyWithdrawalEvent) { - jsonElement = gson.toJsonTree(domainEvent, MoneyWithdrawalEvent.class); - } else if (domainEvent instanceof MoneyTransferEvent) { + } else if (domainEvent instanceof MoneyTransferEvent) { jsonElement = gson.toJsonTree(domainEvent, MoneyTransferEvent.class); } else { throw new RuntimeException("Journal Event not recegnized"); @@ -97,13 +102,20 @@ public class JsonFileJournal implements ProcessorJournal { } } - @Override + + /** + * Reset. + */ public void reset() { aFile.delete(); } - @Override + /** + * Read next domain event. + * + * @return the domain event + */ public DomainEvent readNext() { if (index >= events.size()) { return null; @@ -122,9 +134,7 @@ public class JsonFileJournal implements ProcessorJournal { domainEvent = gson.fromJson(jsonElement, MoneyDepositEvent.class); } else if (eventClassName.equals("MoneyTransferEvent")) { domainEvent = gson.fromJson(jsonElement, MoneyTransferEvent.class); - } else if (eventClassName.equals("MoneyWithdrawalEvent")) { - domainEvent = gson.fromJson(jsonElement, MoneyWithdrawalEvent.class); - } else { + } else { throw new RuntimeException("Journal Event not recegnized"); } diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/state/AccountAggregate.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/state/AccountAggregate.java index cfe0cfbb3..2d957268e 100644 --- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/state/AccountAggregate.java +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/state/AccountAggregate.java @@ -27,12 +27,18 @@ import java.util.HashMap; import java.util.Map; /** + * This is the static accounts map holder class. + * This class holds the state of the accounts. + * * Created by Serdar Hamzaogullari on 06.08.2017. */ public class AccountAggregate { private static Map accounts = new HashMap<>(); + private AccountAggregate() { + } + /** * Put account. * @@ -46,7 +52,7 @@ public class AccountAggregate { * Gets account. * * @param accountNo the account no - * @return the account + * @return the copy of the account or null if not found */ public static Account getAccount(int accountNo) { Account account = accounts.get(accountNo); diff --git a/event-sourcing/src/main/test/java/com/iluwatar/event/sourcing/IntegrationTest.java b/event-sourcing/src/test/java/IntegrationTest.java similarity index 59% rename from event-sourcing/src/main/test/java/com/iluwatar/event/sourcing/IntegrationTest.java rename to event-sourcing/src/test/java/IntegrationTest.java index 8fbed333d..5a3f5718a 100644 --- a/event-sourcing/src/main/test/java/com/iluwatar/event/sourcing/IntegrationTest.java +++ b/event-sourcing/src/test/java/IntegrationTest.java @@ -20,16 +20,18 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package com.iluwatar.event.sourcing; import static com.iluwatar.event.sourcing.app.App.ACCOUNT_OF_DAENERYS; import static com.iluwatar.event.sourcing.app.App.ACCOUNT_OF_JON; import com.iluwatar.event.sourcing.domain.Account; -import com.iluwatar.event.sourcing.journal.JsonFileJournal; +import com.iluwatar.event.sourcing.event.AccountCreateEvent; +import com.iluwatar.event.sourcing.event.MoneyDepositEvent; +import com.iluwatar.event.sourcing.event.MoneyTransferEvent; import com.iluwatar.event.sourcing.processor.DomainEventProcessor; import com.iluwatar.event.sourcing.state.AccountAggregate; import java.math.BigDecimal; +import java.util.Date; import org.junit.Assert; import org.junit.Before; import org.junit.Test; @@ -44,31 +46,14 @@ public class IntegrationTest { /** * The Domain event processor. */ - DomainEventProcessor domainEventProcessor; - /** - * The Json file journal. - */ - JsonFileJournal jsonFileJournal; - /** - * The Account service. - */ - AccountService accountService; - /** - * The Money transaction service. - */ - MoneyTransactionService moneyTransactionService; + private DomainEventProcessor eventProcessor; /** * Initialize. */ @Before public void initialize() { - domainEventProcessor = new DomainEventProcessor(); - jsonFileJournal = new JsonFileJournal(); - domainEventProcessor.setPrecessorJournal(jsonFileJournal); - accountService = new AccountService(domainEventProcessor); - moneyTransactionService = new MoneyTransactionService( - domainEventProcessor); + eventProcessor = new DomainEventProcessor(); } /** @@ -76,27 +61,31 @@ public class IntegrationTest { */ @Test public void testStateRecovery() { - jsonFileJournal.reset(); + eventProcessor.reset(); - accountService.createAccount(ACCOUNT_OF_DAENERYS, "Daenerys Targaryen"); - accountService.createAccount(ACCOUNT_OF_JON, "Jon Snow"); + eventProcessor.process(new AccountCreateEvent( + 0, new Date().getTime(), ACCOUNT_OF_DAENERYS, "Daenerys Targaryen")); - moneyTransactionService.depositMoney(ACCOUNT_OF_DAENERYS, new BigDecimal("100000")); - moneyTransactionService.depositMoney(ACCOUNT_OF_JON, new BigDecimal("100")); + eventProcessor.process(new AccountCreateEvent( + 1, new Date().getTime(), ACCOUNT_OF_JON, "Jon Snow")); - moneyTransactionService - .transferMoney(ACCOUNT_OF_DAENERYS, ACCOUNT_OF_JON, new BigDecimal("10000")); - moneyTransactionService.withdrawalMoney(ACCOUNT_OF_JON, new BigDecimal("1000")); + eventProcessor.process(new MoneyDepositEvent( + 2, new Date().getTime(), ACCOUNT_OF_DAENERYS, new BigDecimal("100000"))); + + eventProcessor.process(new MoneyDepositEvent( + 3, new Date().getTime(), ACCOUNT_OF_JON, new BigDecimal("100"))); + + eventProcessor.process(new MoneyTransferEvent( + 4, new Date().getTime(), new BigDecimal("10000"), ACCOUNT_OF_DAENERYS, + ACCOUNT_OF_JON)); Account accountOfDaenerysBeforeShotDown = AccountAggregate.getAccount(ACCOUNT_OF_DAENERYS); Account accountOfJonBeforeShotDown = AccountAggregate.getAccount(ACCOUNT_OF_JON); AccountAggregate.resetState(); - domainEventProcessor = new DomainEventProcessor(); - jsonFileJournal = new JsonFileJournal(); - domainEventProcessor.setPrecessorJournal(jsonFileJournal); - domainEventProcessor.recover(); + eventProcessor = new DomainEventProcessor(); + eventProcessor.recover(); Account accountOfDaenerysAfterShotDown = AccountAggregate.getAccount(ACCOUNT_OF_DAENERYS); Account accountOfJonAfterShotDown = AccountAggregate.getAccount(ACCOUNT_OF_JON); @@ -105,11 +94,6 @@ public class IntegrationTest { accountOfDaenerysAfterShotDown.getMoney()); Assert .assertEquals(accountOfJonBeforeShotDown.getMoney(), accountOfJonAfterShotDown.getMoney()); - Assert.assertEquals(accountOfDaenerysBeforeShotDown.getTransactions().size(), - accountOfDaenerysAfterShotDown.getTransactions().size()); - Assert.assertEquals(accountOfJonBeforeShotDown.getTransactions().size(), - accountOfJonAfterShotDown.getTransactions().size()); - } } \ No newline at end of file