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] 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