- 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
This commit is contained in:
Serdar Hamzaoğulları 2017-09-02 21:52:02 +03:00
parent 82d7e57e8e
commit c6354c48bb
22 changed files with 207 additions and 931 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 103 KiB

After

Width:  |  Height:  |  Size: 59 KiB

View File

@ -1,50 +1,50 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<class-diagram version="1.2.0" icons="true" automaticImage="PNG" always-add-relationships="false" generalizations="true" <class-diagram version="1.2.0" icons="true" automaticImage="PNG" always-add-relationships="true" generalizations="true"
realizations="true" associations="true" dependencies="false" nesting-relationships="true" router="FAN"> realizations="true" associations="true" dependencies="false" nesting-relationships="true" router="FAN">
<class id="1" language="java" name="com.iluwatar.event.sourcing.api.DomainEvent" project="event-sourcing" <class id="1" language="java" name="com.iluwatar.event.sourcing.event.DomainEvent" project="event-sourcing"
file="/event-sourcing/src/main/java/com/iluwatar/event/sourcing/api/DomainEvent.java" binary="false" file="/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/DomainEvent.java" binary="false"
corner="BOTTOM_RIGHT"> corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="802" y="455"/> <position height="-1" width="-1" x="809" y="161"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" <display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true"> sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/> <attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/> <operations public="true" package="true" protected="true" private="true" static="true"/>
</display> </display>
</class> </class>
<interface id="2" language="java" name="com.iluwatar.event.sourcing.api.EventProcessor" project="event-sourcing" <class id="2" language="java" name="com.iluwatar.event.sourcing.event.AccountCreateEvent" project="event-sourcing"
file="/event-sourcing/src/main/java/com/iluwatar/event/sourcing/api/EventProcessor.java" binary="false" file="/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/AccountCreateEvent.java" binary="false"
corner="BOTTOM_RIGHT"> corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="451" y="273"/> <position height="-1" width="-1" x="145" y="455"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</interface>
<interface id="3" language="java" name="com.iluwatar.event.sourcing.api.ProcessorJournal" project="event-sourcing"
file="/event-sourcing/src/main/java/com/iluwatar/event/sourcing/api/ProcessorJournal.java" binary="false"
corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="134" y="275"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</interface>
<class id="4" language="java" name="com.iluwatar.event.sourcing.domain.Account" project="event-sourcing"
file="/event-sourcing/src/main/java/com/iluwatar/event/sourcing/domain/Account.java" binary="false"
corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="444" y="1120"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" <display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true"> sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/> <attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/> <operations public="true" package="true" protected="true" private="true" static="true"/>
</display> </display>
</class> </class>
<class id="5" language="java" name="com.iluwatar.event.sourcing.domain.Transaction" project="event-sourcing" <class id="3" language="java" name="com.iluwatar.event.sourcing.event.MoneyDepositEvent" project="event-sourcing"
file="/event-sourcing/src/main/java/com/iluwatar/event/sourcing/domain/Transaction.java" binary="false" file="/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyDepositEvent.java" binary="false"
corner="BOTTOM_RIGHT"> corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="821" y="1121"/> <position height="-1" width="-1" x="480" y="451"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<class id="4" language="java" name="com.iluwatar.event.sourcing.event.MoneyTransferEvent" project="event-sourcing"
file="/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyTransferEvent.java" binary="false"
corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="809" y="472"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<class id="5" language="java" name="com.iluwatar.event.sourcing.state.AccountAggregate" project="event-sourcing"
file="/event-sourcing/src/main/java/com/iluwatar/event/sourcing/state/AccountAggregate.java" binary="false"
corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="104" y="826"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" <display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true"> sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/> <attributes public="true" package="true" protected="true" private="true" static="true"/>
@ -55,203 +55,63 @@
project="event-sourcing" project="event-sourcing"
file="/event-sourcing/src/main/java/com/iluwatar/event/sourcing/processor/DomainEventProcessor.java" binary="false" file="/event-sourcing/src/main/java/com/iluwatar/event/sourcing/processor/DomainEventProcessor.java" binary="false"
corner="BOTTOM_RIGHT"> corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="452" y="475"/> <position height="-1" width="-1" x="458" y="121"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" <display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true"> sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/> <attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/> <operations public="true" package="true" protected="true" private="true" static="true"/>
</display> </display>
</class> </class>
<class id="7" language="java" name="com.iluwatar.event.sourcing.journal.JsonFileJournal" project="event-sourcing" <class id="7" language="java" name="com.iluwatar.event.sourcing.processor.JsonFileJournal" project="event-sourcing"
file="/event-sourcing/src/main/java/com/iluwatar/event/sourcing/journal/JsonFileJournal.java" binary="false" file="/event-sourcing/src/main/java/com/iluwatar/event/sourcing/processor/JsonFileJournal.java" binary="false"
corner="BOTTOM_RIGHT"> corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="134" y="499"/> <position height="189" width="171" x="69" y="26"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" <display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true"> sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/> <attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/> <operations public="true" package="true" protected="true" private="true" static="true"/>
</display> </display>
</class> </class>
<class id="8" language="java" name="com.iluwatar.event.sourcing.event.AccountCreateEvent" project="event-sourcing" <class id="8" language="java" name="com.iluwatar.event.sourcing.domain.Account" project="event-sourcing"
file="/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/AccountCreateEvent.java" binary="false" file="/event-sourcing/src/main/java/com/iluwatar/event/sourcing/domain/Account.java" binary="false"
corner="BOTTOM_RIGHT"> corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="138" y="749"/> <position height="-1" width="-1" x="472" y="827"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" <display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true"> sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/> <attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/> <operations public="true" package="true" protected="true" private="true" static="true"/>
</display> </display>
</class> </class>
<class id="9" language="java" name="com.iluwatar.event.sourcing.event.MoneyDepositEvent" project="event-sourcing" <generalization id="9">
file="/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyDepositEvent.java" binary="false" <end type="SOURCE" refId="2"/>
corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="473" y="745"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<class id="10" language="java" name="com.iluwatar.event.sourcing.event.MoneyTransferEvent" project="event-sourcing"
file="/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyTransferEvent.java" binary="false"
corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="802" y="766"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<class id="11" language="java" name="com.iluwatar.event.sourcing.event.MoneyWithdrawalEvent" project="event-sourcing"
file="/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyWithdrawalEvent.java" binary="false"
corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="1107" y="748"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<class id="12" language="java" name="com.iluwatar.event.sourcing.state.AccountAggregate" project="event-sourcing"
file="/event-sourcing/src/main/java/com/iluwatar/event/sourcing/state/AccountAggregate.java" binary="false"
corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="97" y="1120"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<class id="13" language="java" name="AccountService" project="event-sourcing"
file="/event-sourcing/src/main/java/com/iluwatar/event/sourcing/service/AccountService.java" binary="false"
corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="687" y="68"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<class id="14" language="java" name="MoneyTransactionService"
project="event-sourcing"
file="/event-sourcing/src/main/java/com/iluwatar/event/sourcing/service/MoneyTransactionService.java" binary="false"
corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="178" y="88"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<class id="15" language="java" name="com.iluwatar.event.sourcing.gateway.Gateways" project="event-sourcing"
file="/event-sourcing/src/main/java/com/iluwatar/event/sourcing/gateway/Gateways.java" binary="false"
corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="476" y="1484"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<class id="16" language="java" name="com.iluwatar.event.sourcing.gateway.AccountCreateContractSender"
project="event-sourcing"
file="/event-sourcing/src/main/java/com/iluwatar/event/sourcing/gateway/AccountCreateContractSender.java"
binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="920" y="1483"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<class id="17" language="java" name="com.iluwatar.event.sourcing.gateway.TransactionLogger" project="event-sourcing"
file="/event-sourcing/src/main/java/com/iluwatar/event/sourcing/gateway/TransactionLogger.java" binary="false"
corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="94" y="1484"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<generalization id="18">
<end type="SOURCE" refId="8"/>
<end type="TARGET" refId="1"/> <end type="TARGET" refId="1"/>
</generalization> </generalization>
<generalization id="19"> <generalization id="10">
<end type="SOURCE" refId="10"/> <end type="SOURCE" refId="4"/>
<end type="TARGET" refId="1"/> <end type="TARGET" refId="1"/>
</generalization> </generalization>
<association id="20"> <association id="11">
<end type="SOURCE" refId="4" navigable="false">
<attribute id="21" name="transactions"/>
<multiplicity id="22" minimum="0" maximum="2147483647"/>
</end>
<end type="TARGET" refId="5" navigable="true"/>
<display labels="true" multiplicity="true"/>
</association>
<association id="23">
<end type="SOURCE" refId="6" navigable="false"> <end type="SOURCE" refId="6" navigable="false">
<attribute id="24" name="precessorJournal"/> <attribute id="12" name="processorJournal">
<multiplicity id="25" minimum="0" maximum="1"/> <position height="0" width="0" x="0" y="0"/>
</attribute>
<multiplicity id="13" minimum="0" maximum="1">
<position height="0" width="0" x="0" y="0"/>
</multiplicity>
</end> </end>
<end type="TARGET" refId="3" navigable="true"/> <end type="TARGET" refId="7" navigable="true"/>
<display labels="true" multiplicity="true"/> <display labels="true" multiplicity="true"/>
</association> </association>
<association id="26"> <generalization id="14">
<end type="SOURCE" refId="15" navigable="false"> <end type="SOURCE" refId="3"/>
<attribute id="27" name="transactionLogger"/>
<multiplicity id="28" minimum="0" maximum="1"/>
</end>
<end type="TARGET" refId="17" navigable="true"/>
<display labels="true" multiplicity="true"/>
</association>
<generalization id="29">
<end type="SOURCE" refId="11"/>
<end type="TARGET" refId="1"/> <end type="TARGET" refId="1"/>
</generalization> </generalization>
<association id="30"> <association id="15">
<end type="SOURCE" refId="13" navigable="false"> <end type="SOURCE" refId="5" navigable="false">
<attribute id="31" name="eventProcessor"/> <attribute id="16" name="accounts"/>
<multiplicity id="32" minimum="0" maximum="1"/> <multiplicity id="17" minimum="0" maximum="2147483647"/>
</end> </end>
<end type="TARGET" refId="2" navigable="true"/> <end type="TARGET" refId="8" navigable="true"/>
<display labels="true" multiplicity="true"/>
</association>
<realization id="33">
<end type="SOURCE" refId="6"/>
<end type="TARGET" refId="2"/>
</realization>
<association id="34">
<end type="SOURCE" refId="15" navigable="false">
<attribute id="35" name="accountCreateContractSender"/>
<multiplicity id="36" minimum="0" maximum="1"/>
</end>
<end type="TARGET" refId="16" navigable="true"/>
<display labels="true" multiplicity="true"/>
</association>
<realization id="37">
<end type="SOURCE" refId="7"/>
<end type="TARGET" refId="3"/>
</realization>
<association id="38">
<end type="SOURCE" refId="12" navigable="false">
<attribute id="39" name="accounts"/>
<multiplicity id="40" minimum="0" maximum="2147483647"/>
</end>
<end type="TARGET" refId="4" navigable="true"/>
<display labels="true" multiplicity="true"/>
</association>
<generalization id="41">
<end type="SOURCE" refId="9"/>
<end type="TARGET" refId="1"/>
</generalization>
<association id="42">
<end type="SOURCE" refId="14" navigable="false">
<attribute id="43" name="eventProcessor"/>
<multiplicity id="44" minimum="0" maximum="1"/>
</end>
<end type="TARGET" refId="2" navigable="true"/>
<display labels="true" multiplicity="true"/> <display labels="true" multiplicity="true"/>
</association> </association>
<classifier-display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" <classifier-display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"

View File

@ -1,56 +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;
import com.iluwatar.event.sourcing.api.EventProcessor;
import com.iluwatar.event.sourcing.event.AccountCreateEvent;
import java.util.Date;
/**
* Created by Serdar Hamzaogullari on 06.08.2017.
*/
public class AccountService {
private EventProcessor eventProcessor;
/**
* 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);
}
}

View File

@ -1,85 +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;
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 Serdar Hamzaogullari on 06.08.2017.
*/
public class MoneyTransactionService {
private EventProcessor eventProcessor;
/**
* Instantiates a new Money transaction service.
*
* @param eventProcessor the event processor
*/
public MoneyTransactionService(EventProcessor eventProcessor) {
this.eventProcessor = eventProcessor;
}
/**
* 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);
}
/**
* 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);
}
}

View File

@ -1,42 +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;
/**
* Created by Serdar Hamzaogullari on 06.08.2017.
*/
public class SequenceIdGenerator {
private static long sequenceId = 0L;
/**
* Next sequence id long.
*
* @return the long
*/
public static long nextSequenceId() {
sequenceId++;
return sequenceId;
}
}

View File

@ -1,48 +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.api;
/**
* Created by Serdar Hamzaogullari on 06.08.2017.
*/
public interface EventProcessor {
/**
* 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();
}

View File

@ -1,48 +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.api;
/**
* Created by Serdar Hamzaogullari on 06.08.2017.
*/
public interface ProcessorJournal {
/**
* Write.
*
* @param domainEvent the domain event
*/
void write(DomainEvent domainEvent);
/**
* Reset.
*/
void reset();
/**
* Read next domain event.
*
* @return the domain event
*/
DomainEvent readNext();
}

View File

@ -22,12 +22,13 @@
*/ */
package com.iluwatar.event.sourcing.app; package com.iluwatar.event.sourcing.app;
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.processor.DomainEventProcessor;
import com.iluwatar.event.sourcing.AccountService;
import com.iluwatar.event.sourcing.MoneyTransactionService;
import com.iluwatar.event.sourcing.state.AccountAggregate; import com.iluwatar.event.sourcing.state.AccountAggregate;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.Date;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -51,7 +52,13 @@ import org.slf4j.LoggerFactory;
public class App { public class App {
private static final Logger LOGGER = LoggerFactory.getLogger(App.class); private static final Logger LOGGER = LoggerFactory.getLogger(App.class);
/**
* The constant ACCOUNT OF DAENERYS.
*/
public static final int ACCOUNT_OF_DAENERYS = 1; public static final int ACCOUNT_OF_DAENERYS = 1;
/**
* The constant ACCOUNT OF JON.
*/
public static final int ACCOUNT_OF_JON = 2; public static final int ACCOUNT_OF_JON = 2;
/** /**
@ -61,28 +68,31 @@ public class App {
*/ */
public static void main(String[] args) { public static void main(String[] args) {
DomainEventProcessor domainEventProcessor = new DomainEventProcessor(); DomainEventProcessor eventProcessor = new DomainEventProcessor();
JsonFileJournal jsonFileJournal = new JsonFileJournal();
domainEventProcessor.setPrecessorJournal(jsonFileJournal);
AccountService accountService = new AccountService(domainEventProcessor);
MoneyTransactionService moneyTransactionService = new MoneyTransactionService(
domainEventProcessor);
LOGGER.info("Running the system first time............"); LOGGER.info("Running the system first time............");
jsonFileJournal.reset(); eventProcessor.reset();
LOGGER.info("Creating th accounts............"); LOGGER.info("Creating th accounts............");
accountService.createAccount(ACCOUNT_OF_DAENERYS, "Daenerys Targaryen"); eventProcessor.process(new AccountCreateEvent(
accountService.createAccount(ACCOUNT_OF_JON, "Jon Snow"); 0, new Date().getTime(), ACCOUNT_OF_DAENERYS, "Daenerys Targaryen"));
eventProcessor.process(new AccountCreateEvent(
1, new Date().getTime(), ACCOUNT_OF_JON, "Jon Snow"));
LOGGER.info("Do some money operations............"); LOGGER.info("Do some money operations............");
moneyTransactionService.depositMoney(ACCOUNT_OF_DAENERYS, new BigDecimal("100000")); eventProcessor.process(new MoneyDepositEvent(
moneyTransactionService.depositMoney(ACCOUNT_OF_JON, new BigDecimal("100")); 2, new Date().getTime(), ACCOUNT_OF_DAENERYS, new BigDecimal("100000")));
moneyTransactionService.transferMoney(ACCOUNT_OF_DAENERYS, ACCOUNT_OF_JON, new BigDecimal("10000")); eventProcessor.process(new MoneyDepositEvent(
moneyTransactionService.withdrawalMoney(ACCOUNT_OF_JON, new BigDecimal("1000")); 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));
LOGGER.info("...............State:............"); LOGGER.info("...............State:............");
LOGGER.info(AccountAggregate.getAccount(ACCOUNT_OF_DAENERYS).toString()); LOGGER.info(AccountAggregate.getAccount(ACCOUNT_OF_DAENERYS).toString());
@ -93,13 +103,13 @@ public class App {
LOGGER.info("Recover the system by the events in journal file............"); LOGGER.info("Recover the system by the events in journal file............");
domainEventProcessor = new DomainEventProcessor(); eventProcessor = new DomainEventProcessor();
jsonFileJournal = new JsonFileJournal(); eventProcessor.recover();
domainEventProcessor.setPrecessorJournal(jsonFileJournal);
domainEventProcessor.recover();
LOGGER.info("...............Recovered State:............"); LOGGER.info("...............Recovered State:............");
LOGGER.info(AccountAggregate.getAccount(ACCOUNT_OF_DAENERYS).toString()); LOGGER.info(AccountAggregate.getAccount(ACCOUNT_OF_DAENERYS).toString());
LOGGER.info(AccountAggregate.getAccount(ACCOUNT_OF_JON).toString()); LOGGER.info(AccountAggregate.getAccount(ACCOUNT_OF_JON).toString());
} }
} }

View File

@ -25,22 +25,25 @@ package com.iluwatar.event.sourcing.domain;
import com.iluwatar.event.sourcing.event.AccountCreateEvent; import com.iluwatar.event.sourcing.event.AccountCreateEvent;
import com.iluwatar.event.sourcing.event.MoneyDepositEvent; import com.iluwatar.event.sourcing.event.MoneyDepositEvent;
import com.iluwatar.event.sourcing.event.MoneyTransferEvent; 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 com.iluwatar.event.sourcing.state.AccountAggregate;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.ArrayList; import org.slf4j.Logger;
import java.util.List; import org.slf4j.LoggerFactory;
/** /**
* This is the Account class that holds the account info, the account number,
* account owner name and money of the account. Account class also have the business logic of events
* that effects this account.
*
* Created by Serdar Hamzaogullari on 06.08.2017. * Created by Serdar Hamzaogullari on 06.08.2017.
*/ */
public class Account { public class Account {
private static final Logger LOGGER = LoggerFactory.getLogger(Account.class);
private final int accountNo; private final int accountNo;
private final String owner; private final String owner;
private BigDecimal money; private BigDecimal money;
private List<Transaction> transactions;
/** /**
* Instantiates a new Account. * Instantiates a new Account.
@ -52,7 +55,6 @@ public class Account {
this.accountNo = accountNo; this.accountNo = accountNo;
this.owner = owner; this.owner = owner;
money = BigDecimal.ZERO; money = BigDecimal.ZERO;
transactions = new ArrayList<>();
} }
/** /**
@ -91,23 +93,6 @@ public class Account {
this.money = money; this.money = money;
} }
/**
* Gets transactions.
*
* @return the transactions
*/
public List<Transaction> getTransactions() {
return transactions;
}
/**
* Sets transactions.
*
* @param transactions the transactions
*/
public void setTransactions(List<Transaction> transactions) {
this.transactions = transactions;
}
/** /**
* Copy account. * Copy account.
@ -117,7 +102,6 @@ public class Account {
public Account copy() { public Account copy() {
Account account = new Account(accountNo, owner); Account account = new Account(accountNo, owner);
account.setMoney(money); account.setMoney(money);
account.setTransactions(transactions);
return account; return account;
} }
@ -127,29 +111,22 @@ public class Account {
+ "accountNo=" + accountNo + "accountNo=" + accountNo
+ ", owner='" + owner + '\'' + ", owner='" + owner + '\''
+ ", money=" + money + ", money=" + money
+ ", transactions=" + transactions
+ '}'; + '}';
} }
private Transaction depositMoney(BigDecimal money) { private void depositMoney(BigDecimal money) {
this.money = this.money.add(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); 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) { private void handleDeposit(BigDecimal money, boolean realTime) {
Transaction transaction = depositMoney(money); depositMoney(money);
AccountAggregate.putAccount(this); AccountAggregate.putAccount(this);
if (realTime) { 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"); throw new RuntimeException("Insufficient Account Balance");
} }
Transaction transaction = withdrawMoney(money); withdrawMoney(money);
AccountAggregate.putAccount(this); AccountAggregate.putAccount(this);
if (realTime) { 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 * @param moneyDepositEvent the money deposit event
*/ */
@ -176,29 +153,19 @@ public class Account {
/** /**
* Handle event. * Handles the AccountCreateEvent.
*
* @param moneyWithdrawalEvent the money withdrawal event
*/
public void handleEvent(MoneyWithdrawalEvent moneyWithdrawalEvent) {
handleWithdrawal(moneyWithdrawalEvent.getMoney(), moneyWithdrawalEvent.isRealTime());
}
/**
* Handle event.
* *
* @param accountCreateEvent the account create event * @param accountCreateEvent the account create event
*/ */
public void handleEvent(AccountCreateEvent accountCreateEvent) { public void handleEvent(AccountCreateEvent accountCreateEvent) {
AccountAggregate.putAccount(this); AccountAggregate.putAccount(this);
// check if this event is replicated from journal before calling an external gateway function
if (accountCreateEvent.isRealTime()) { 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 * @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 * @param moneyTransferEvent the money transfer event
*/ */

View File

@ -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
+ '}';
}
}

View File

@ -22,11 +22,15 @@
*/ */
package com.iluwatar.event.sourcing.event; 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.Account;
import com.iluwatar.event.sourcing.state.AccountAggregate; 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. * Created by Serdar Hamzaogullari on 06.08.2017.
*/ */
public class AccountCreateEvent extends DomainEvent { public class AccountCreateEvent extends DomainEvent {

View File

@ -20,11 +20,13 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
*/ */
package com.iluwatar.event.sourcing.api; package com.iluwatar.event.sourcing.event;
import java.io.Serializable; 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. * Created by Serdar Hamzaogullari on 06.08.2017.
*/ */
public abstract class DomainEvent implements Serializable { public abstract class DomainEvent implements Serializable {

View File

@ -22,18 +22,22 @@
*/ */
package com.iluwatar.event.sourcing.event; 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.Account;
import com.iluwatar.event.sourcing.state.AccountAggregate; import com.iluwatar.event.sourcing.state.AccountAggregate;
import java.math.BigDecimal; 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. * Created by Serdar Hamzaogullari on 06.08.2017.
*/ */
public class MoneyDepositEvent extends DomainEvent { public class MoneyDepositEvent extends DomainEvent {
private BigDecimal money; private final BigDecimal money;
private int accountNo; private final int accountNo;
/** /**
* Instantiates a new Money deposit event. * Instantiates a new Money deposit event.

View File

@ -22,19 +22,23 @@
*/ */
package com.iluwatar.event.sourcing.event; 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.Account;
import com.iluwatar.event.sourcing.state.AccountAggregate; import com.iluwatar.event.sourcing.state.AccountAggregate;
import java.math.BigDecimal; 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. * Created by Serdar Hamzaogullari on 06.08.2017.
*/ */
public class MoneyTransferEvent extends DomainEvent { public class MoneyTransferEvent extends DomainEvent {
private BigDecimal money; private final BigDecimal money;
private int accountNoFrom; private final int accountNoFrom;
private int accountNoTo; private final int accountNoTo;
/** /**
* Instantiates a new Money transfer event. * 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 * @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 * @return the account no to
*/ */

View File

@ -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);
}
}

View File

@ -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
}
}

View File

@ -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;
}
}

View File

@ -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
}
}

View File

@ -22,33 +22,43 @@
*/ */
package com.iluwatar.event.sourcing.processor; package com.iluwatar.event.sourcing.processor;
import com.iluwatar.event.sourcing.api.DomainEvent; import com.iluwatar.event.sourcing.event.DomainEvent;
import com.iluwatar.event.sourcing.api.EventProcessor;
import com.iluwatar.event.sourcing.api.ProcessorJournal;
/** /**
* 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. * 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) { public void process(DomainEvent domainEvent) {
domainEvent.process(); domainEvent.process();
precessorJournal.write(domainEvent); processorJournal.write(domainEvent);
} }
@Override /**
public void setPrecessorJournal(ProcessorJournal precessorJournal) { * Reset.
this.precessorJournal = precessorJournal; */
public void reset() {
processorJournal.reset();
} }
@Override /**
* Recover.
*/
public void recover() { public void recover() {
DomainEvent domainEvent; DomainEvent domainEvent;
while (true) { while (true) {
domainEvent = precessorJournal.readNext(); domainEvent = processorJournal.readNext();
if (domainEvent == null) { if (domainEvent == null) {
break; break;
} else { } else {

View File

@ -20,17 +20,15 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
*/ */
package com.iluwatar.event.sourcing.journal; package com.iluwatar.event.sourcing.processor;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.JsonElement; import com.google.gson.JsonElement;
import com.google.gson.JsonParser; 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.AccountCreateEvent;
import com.iluwatar.event.sourcing.event.DomainEvent;
import com.iluwatar.event.sourcing.event.MoneyDepositEvent; import com.iluwatar.event.sourcing.event.MoneyDepositEvent;
import com.iluwatar.event.sourcing.event.MoneyTransferEvent; import com.iluwatar.event.sourcing.event.MoneyTransferEvent;
import com.iluwatar.event.sourcing.event.MoneyWithdrawalEvent;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.BufferedWriter; import java.io.BufferedWriter;
import java.io.File; import java.io.File;
@ -44,12 +42,16 @@ import java.util.ArrayList;
import java.util.List; 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. * Created by Serdar Hamzaogullari on 06.08.2017.
*/ */
public class JsonFileJournal implements ProcessorJournal { public class JsonFileJournal {
private File aFile; private final File aFile;
private List<String> events = new ArrayList<>(); private final List<String> events = new ArrayList<>();
private int index = 0; 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) { public void write(DomainEvent domainEvent) {
Gson gson = new Gson(); Gson gson = new Gson();
JsonElement jsonElement; JsonElement jsonElement;
@ -80,8 +87,6 @@ public class JsonFileJournal implements ProcessorJournal {
jsonElement = gson.toJsonTree(domainEvent, AccountCreateEvent.class); jsonElement = gson.toJsonTree(domainEvent, AccountCreateEvent.class);
} else if (domainEvent instanceof MoneyDepositEvent) { } else if (domainEvent instanceof MoneyDepositEvent) {
jsonElement = gson.toJsonTree(domainEvent, MoneyDepositEvent.class); 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); jsonElement = gson.toJsonTree(domainEvent, MoneyTransferEvent.class);
} else { } else {
@ -97,13 +102,20 @@ public class JsonFileJournal implements ProcessorJournal {
} }
} }
@Override
/**
* Reset.
*/
public void reset() { public void reset() {
aFile.delete(); aFile.delete();
} }
@Override /**
* Read next domain event.
*
* @return the domain event
*/
public DomainEvent readNext() { public DomainEvent readNext() {
if (index >= events.size()) { if (index >= events.size()) {
return null; return null;
@ -122,8 +134,6 @@ public class JsonFileJournal implements ProcessorJournal {
domainEvent = gson.fromJson(jsonElement, MoneyDepositEvent.class); domainEvent = gson.fromJson(jsonElement, MoneyDepositEvent.class);
} else if (eventClassName.equals("MoneyTransferEvent")) { } else if (eventClassName.equals("MoneyTransferEvent")) {
domainEvent = gson.fromJson(jsonElement, MoneyTransferEvent.class); 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"); throw new RuntimeException("Journal Event not recegnized");
} }

View File

@ -27,12 +27,18 @@ import java.util.HashMap;
import java.util.Map; 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. * Created by Serdar Hamzaogullari on 06.08.2017.
*/ */
public class AccountAggregate { public class AccountAggregate {
private static Map<Integer, Account> accounts = new HashMap<>(); private static Map<Integer, Account> accounts = new HashMap<>();
private AccountAggregate() {
}
/** /**
* Put account. * Put account.
* *
@ -46,7 +52,7 @@ public class AccountAggregate {
* Gets account. * Gets account.
* *
* @param accountNo the account no * @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) { public static Account getAccount(int accountNo) {
Account account = accounts.get(accountNo); Account account = accounts.get(accountNo);

View File

@ -20,16 +20,18 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * 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_DAENERYS;
import static com.iluwatar.event.sourcing.app.App.ACCOUNT_OF_JON; import static com.iluwatar.event.sourcing.app.App.ACCOUNT_OF_JON;
import com.iluwatar.event.sourcing.domain.Account; 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.processor.DomainEventProcessor;
import com.iluwatar.event.sourcing.state.AccountAggregate; import com.iluwatar.event.sourcing.state.AccountAggregate;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.Date;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@ -44,31 +46,14 @@ public class IntegrationTest {
/** /**
* The Domain event processor. * The Domain event processor.
*/ */
DomainEventProcessor domainEventProcessor; private DomainEventProcessor eventProcessor;
/**
* The Json file journal.
*/
JsonFileJournal jsonFileJournal;
/**
* The Account service.
*/
AccountService accountService;
/**
* The Money transaction service.
*/
MoneyTransactionService moneyTransactionService;
/** /**
* Initialize. * Initialize.
*/ */
@Before @Before
public void initialize() { public void initialize() {
domainEventProcessor = new DomainEventProcessor(); eventProcessor = new DomainEventProcessor();
jsonFileJournal = new JsonFileJournal();
domainEventProcessor.setPrecessorJournal(jsonFileJournal);
accountService = new AccountService(domainEventProcessor);
moneyTransactionService = new MoneyTransactionService(
domainEventProcessor);
} }
/** /**
@ -76,27 +61,31 @@ public class IntegrationTest {
*/ */
@Test @Test
public void testStateRecovery() { public void testStateRecovery() {
jsonFileJournal.reset(); eventProcessor.reset();
accountService.createAccount(ACCOUNT_OF_DAENERYS, "Daenerys Targaryen"); eventProcessor.process(new AccountCreateEvent(
accountService.createAccount(ACCOUNT_OF_JON, "Jon Snow"); 0, new Date().getTime(), ACCOUNT_OF_DAENERYS, "Daenerys Targaryen"));
moneyTransactionService.depositMoney(ACCOUNT_OF_DAENERYS, new BigDecimal("100000")); eventProcessor.process(new AccountCreateEvent(
moneyTransactionService.depositMoney(ACCOUNT_OF_JON, new BigDecimal("100")); 1, new Date().getTime(), ACCOUNT_OF_JON, "Jon Snow"));
moneyTransactionService eventProcessor.process(new MoneyDepositEvent(
.transferMoney(ACCOUNT_OF_DAENERYS, ACCOUNT_OF_JON, new BigDecimal("10000")); 2, new Date().getTime(), ACCOUNT_OF_DAENERYS, new BigDecimal("100000")));
moneyTransactionService.withdrawalMoney(ACCOUNT_OF_JON, new BigDecimal("1000"));
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 accountOfDaenerysBeforeShotDown = AccountAggregate.getAccount(ACCOUNT_OF_DAENERYS);
Account accountOfJonBeforeShotDown = AccountAggregate.getAccount(ACCOUNT_OF_JON); Account accountOfJonBeforeShotDown = AccountAggregate.getAccount(ACCOUNT_OF_JON);
AccountAggregate.resetState(); AccountAggregate.resetState();
domainEventProcessor = new DomainEventProcessor(); eventProcessor = new DomainEventProcessor();
jsonFileJournal = new JsonFileJournal(); eventProcessor.recover();
domainEventProcessor.setPrecessorJournal(jsonFileJournal);
domainEventProcessor.recover();
Account accountOfDaenerysAfterShotDown = AccountAggregate.getAccount(ACCOUNT_OF_DAENERYS); Account accountOfDaenerysAfterShotDown = AccountAggregate.getAccount(ACCOUNT_OF_DAENERYS);
Account accountOfJonAfterShotDown = AccountAggregate.getAccount(ACCOUNT_OF_JON); Account accountOfJonAfterShotDown = AccountAggregate.getAccount(ACCOUNT_OF_JON);
@ -105,11 +94,6 @@ public class IntegrationTest {
accountOfDaenerysAfterShotDown.getMoney()); accountOfDaenerysAfterShotDown.getMoney());
Assert Assert
.assertEquals(accountOfJonBeforeShotDown.getMoney(), accountOfJonAfterShotDown.getMoney()); .assertEquals(accountOfJonBeforeShotDown.getMoney(), accountOfJonAfterShotDown.getMoney());
Assert.assertEquals(accountOfDaenerysBeforeShotDown.getTransactions().size(),
accountOfDaenerysAfterShotDown.getTransactions().size());
Assert.assertEquals(accountOfJonBeforeShotDown.getTransactions().size(),
accountOfJonAfterShotDown.getTransactions().size());
} }
} }