- 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"?>
<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">
<class id="1" language="java" name="com.iluwatar.event.sourcing.api.DomainEvent" project="event-sourcing"
file="/event-sourcing/src/main/java/com/iluwatar/event/sourcing/api/DomainEvent.java" binary="false"
<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/event/DomainEvent.java" binary="false"
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"
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>
<interface id="2" language="java" name="com.iluwatar.event.sourcing.api.EventProcessor" project="event-sourcing"
file="/event-sourcing/src/main/java/com/iluwatar/event/sourcing/api/EventProcessor.java" binary="false"
<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/event/AccountCreateEvent.java" binary="false"
corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="451" y="273"/>
<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"/>
<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>
</class>
<class id="5" language="java" name="com.iluwatar.event.sourcing.domain.Transaction" project="event-sourcing"
file="/event-sourcing/src/main/java/com/iluwatar/event/sourcing/domain/Transaction.java" binary="false"
<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/event/MoneyDepositEvent.java" binary="false"
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"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
@ -55,203 +55,63 @@
project="event-sourcing"
file="/event-sourcing/src/main/java/com/iluwatar/event/sourcing/processor/DomainEventProcessor.java" binary="false"
corner="BOTTOM_RIGHT">
<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"
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="7" language="java" name="com.iluwatar.event.sourcing.journal.JsonFileJournal" project="event-sourcing"
file="/event-sourcing/src/main/java/com/iluwatar/event/sourcing/journal/JsonFileJournal.java" binary="false"
<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/processor/JsonFileJournal.java" binary="false"
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"
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="8" language="java" name="com.iluwatar.event.sourcing.event.AccountCreateEvent" project="event-sourcing"
file="/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/AccountCreateEvent.java" binary="false"
<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/domain/Account.java" binary="false"
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"
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="9" language="java" name="com.iluwatar.event.sourcing.event.MoneyDepositEvent" project="event-sourcing"
file="/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyDepositEvent.java" binary="false"
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"/>
<generalization id="9">
<end type="SOURCE" refId="2"/>
<end type="TARGET" refId="1"/>
</generalization>
<generalization id="19">
<end type="SOURCE" refId="10"/>
<generalization id="10">
<end type="SOURCE" refId="4"/>
<end type="TARGET" refId="1"/>
</generalization>
<association id="20">
<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">
<association id="11">
<end type="SOURCE" refId="6" navigable="false">
<attribute id="24" name="precessorJournal"/>
<multiplicity id="25" minimum="0" maximum="1"/>
<attribute id="12" name="processorJournal">
<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 type="TARGET" refId="3" navigable="true"/>
<end type="TARGET" refId="7" navigable="true"/>
<display labels="true" multiplicity="true"/>
</association>
<association id="26">
<end type="SOURCE" refId="15" navigable="false">
<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"/>
<generalization id="14">
<end type="SOURCE" refId="3"/>
<end type="TARGET" refId="1"/>
</generalization>
<association id="30">
<end type="SOURCE" refId="13" navigable="false">
<attribute id="31" name="eventProcessor"/>
<multiplicity id="32" minimum="0" maximum="1"/>
<association id="15">
<end type="SOURCE" refId="5" navigable="false">
<attribute id="16" name="accounts"/>
<multiplicity id="17" minimum="0" maximum="2147483647"/>
</end>
<end type="TARGET" refId="2" 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"/>
<end type="TARGET" refId="8" navigable="true"/>
<display labels="true" multiplicity="true"/>
</association>
<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;
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.AccountService;
import com.iluwatar.event.sourcing.MoneyTransactionService;
import com.iluwatar.event.sourcing.state.AccountAggregate;
import java.math.BigDecimal;
import java.util.Date;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -51,7 +52,13 @@ import org.slf4j.LoggerFactory;
public class App {
private static final Logger LOGGER = LoggerFactory.getLogger(App.class);
/**
* The constant ACCOUNT OF DAENERYS.
*/
public static final int ACCOUNT_OF_DAENERYS = 1;
/**
* The constant ACCOUNT OF JON.
*/
public static final int ACCOUNT_OF_JON = 2;
/**
@ -61,28 +68,31 @@ public class App {
*/
public static void main(String[] args) {
DomainEventProcessor domainEventProcessor = new DomainEventProcessor();
JsonFileJournal jsonFileJournal = new JsonFileJournal();
domainEventProcessor.setPrecessorJournal(jsonFileJournal);
AccountService accountService = new AccountService(domainEventProcessor);
MoneyTransactionService moneyTransactionService = new MoneyTransactionService(
domainEventProcessor);
DomainEventProcessor eventProcessor = new DomainEventProcessor();
LOGGER.info("Running the system first time............");
jsonFileJournal.reset();
eventProcessor.reset();
LOGGER.info("Creating th accounts............");
accountService.createAccount(ACCOUNT_OF_DAENERYS, "Daenerys Targaryen");
accountService.createAccount(ACCOUNT_OF_JON, "Jon Snow");
eventProcessor.process(new AccountCreateEvent(
0, new Date().getTime(), ACCOUNT_OF_DAENERYS, "Daenerys Targaryen"));
eventProcessor.process(new AccountCreateEvent(
1, new Date().getTime(), ACCOUNT_OF_JON, "Jon Snow"));
LOGGER.info("Do some money operations............");
moneyTransactionService.depositMoney(ACCOUNT_OF_DAENERYS, new BigDecimal("100000"));
moneyTransactionService.depositMoney(ACCOUNT_OF_JON, new BigDecimal("100"));
eventProcessor.process(new MoneyDepositEvent(
2, new Date().getTime(), ACCOUNT_OF_DAENERYS, new BigDecimal("100000")));
moneyTransactionService.transferMoney(ACCOUNT_OF_DAENERYS, ACCOUNT_OF_JON, new BigDecimal("10000"));
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));
LOGGER.info("...............State:............");
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............");
domainEventProcessor = new DomainEventProcessor();
jsonFileJournal = new JsonFileJournal();
domainEventProcessor.setPrecessorJournal(jsonFileJournal);
domainEventProcessor.recover();
eventProcessor = new DomainEventProcessor();
eventProcessor.recover();
LOGGER.info("...............Recovered State:............");
LOGGER.info(AccountAggregate.getAccount(ACCOUNT_OF_DAENERYS).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.MoneyDepositEvent;
import com.iluwatar.event.sourcing.event.MoneyTransferEvent;
import com.iluwatar.event.sourcing.event.MoneyWithdrawalEvent;
import com.iluwatar.event.sourcing.gateway.Gateways;
import com.iluwatar.event.sourcing.state.AccountAggregate;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import org.slf4j.Logger;
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.
*/
public class Account {
private static final Logger LOGGER = LoggerFactory.getLogger(Account.class);
private final int accountNo;
private final String owner;
private BigDecimal money;
private List<Transaction> transactions;
/**
* Instantiates a new Account.
@ -52,7 +55,6 @@ public class Account {
this.accountNo = accountNo;
this.owner = owner;
money = BigDecimal.ZERO;
transactions = new ArrayList<>();
}
/**
@ -91,23 +93,6 @@ public class Account {
this.money = money;
}
/**
* Gets transactions.
*
* @return the transactions
*/
public List<Transaction> getTransactions() {
return transactions;
}
/**
* Sets transactions.
*
* @param transactions the transactions
*/
public void setTransactions(List<Transaction> transactions) {
this.transactions = transactions;
}
/**
* Copy account.
@ -117,7 +102,6 @@ public class Account {
public Account copy() {
Account account = new Account(accountNo, owner);
account.setMoney(money);
account.setTransactions(transactions);
return account;
}
@ -127,29 +111,22 @@ public class Account {
+ "accountNo=" + accountNo
+ ", owner='" + owner + '\''
+ ", money=" + money
+ ", transactions=" + transactions
+ '}';
}
private Transaction depositMoney(BigDecimal money) {
private void depositMoney(BigDecimal money) {
this.money = this.money.add(money);
Transaction transaction = new Transaction(accountNo, money, BigDecimal.ZERO, this.money);
transactions.add(transaction);
return transaction;
}
private Transaction withdrawMoney(BigDecimal money) {
private void withdrawMoney(BigDecimal money) {
this.money = this.money.subtract(money);
Transaction transaction = new Transaction(accountNo, BigDecimal.ZERO, money, this.money);
transactions.add(transaction);
return transaction;
}
private void handleDeposit(BigDecimal money, boolean realTime) {
Transaction transaction = depositMoney(money);
depositMoney(money);
AccountAggregate.putAccount(this);
if (realTime) {
Gateways.getTransactionLogger().log(transaction);
LOGGER.info("Some external api for only realtime execution could be called here.");
}
}
@ -158,15 +135,15 @@ public class Account {
throw new RuntimeException("Insufficient Account Balance");
}
Transaction transaction = withdrawMoney(money);
withdrawMoney(money);
AccountAggregate.putAccount(this);
if (realTime) {
Gateways.getTransactionLogger().log(transaction);
LOGGER.info("Some external api for only realtime execution could be called here.");
}
}
/**
* Handle event.
* Handles the MoneyDepositEvent.
*
* @param moneyDepositEvent the money deposit event
*/
@ -176,29 +153,19 @@ public class Account {
/**
* Handle event.
*
* @param moneyWithdrawalEvent the money withdrawal event
*/
public void handleEvent(MoneyWithdrawalEvent moneyWithdrawalEvent) {
handleWithdrawal(moneyWithdrawalEvent.getMoney(), moneyWithdrawalEvent.isRealTime());
}
/**
* Handle event.
* Handles the AccountCreateEvent.
*
* @param accountCreateEvent the account create event
*/
public void handleEvent(AccountCreateEvent accountCreateEvent) {
AccountAggregate.putAccount(this);
// check if this event is replicated from journal before calling an external gateway function
if (accountCreateEvent.isRealTime()) {
Gateways.getAccountCreateContractSender().sendContractInfo(this);
LOGGER.info("Some external api for only realtime execution could be called here.");
}
}
/**
* Handle transfer from event.
* Handles transfer from account event.
*
* @param moneyTransferEvent the money transfer event
*/
@ -207,7 +174,7 @@ public class Account {
}
/**
* Handle transfer to event.
* Handles transfer to account event.
*
* @param moneyTransferEvent the money transfer event
*/

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;
import com.iluwatar.event.sourcing.api.DomainEvent;
import com.iluwatar.event.sourcing.domain.Account;
import com.iluwatar.event.sourcing.state.AccountAggregate;
/**
* This is the class that implements account create event.
* Holds the necessary info for an account create event.
* Implements the process function that finds the event related
* domain objects and calls the related domain object's handle event functions
*
* Created by Serdar Hamzaogullari on 06.08.2017.
*/
public class AccountCreateEvent extends DomainEvent {

View File

@ -20,11 +20,13 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.event.sourcing.api;
package com.iluwatar.event.sourcing.event;
import java.io.Serializable;
/**
* This is the base class for domain events. All events must extend this class.
*
* Created by Serdar Hamzaogullari on 06.08.2017.
*/
public abstract class DomainEvent implements Serializable {

View File

@ -22,18 +22,22 @@
*/
package com.iluwatar.event.sourcing.event;
import com.iluwatar.event.sourcing.api.DomainEvent;
import com.iluwatar.event.sourcing.domain.Account;
import com.iluwatar.event.sourcing.state.AccountAggregate;
import java.math.BigDecimal;
/**
* This is the class that implements money deposit event.
* Holds the necessary info for a money deposit event.
* Implements the process function that finds the event related
* domain objects and calls the related domain object's handle event functions
*
* Created by Serdar Hamzaogullari on 06.08.2017.
*/
public class MoneyDepositEvent extends DomainEvent {
private BigDecimal money;
private int accountNo;
private final BigDecimal money;
private final int accountNo;
/**
* Instantiates a new Money deposit event.

View File

@ -22,19 +22,23 @@
*/
package com.iluwatar.event.sourcing.event;
import com.iluwatar.event.sourcing.api.DomainEvent;
import com.iluwatar.event.sourcing.domain.Account;
import com.iluwatar.event.sourcing.state.AccountAggregate;
import java.math.BigDecimal;
/**
* This is the class that implements money transfer event.
* Holds the necessary info for a money transfer event.
* Implements the process function that finds the event related
* domain objects and calls the related domain object's handle event functions
*
* Created by Serdar Hamzaogullari on 06.08.2017.
*/
public class MoneyTransferEvent extends DomainEvent {
private BigDecimal money;
private int accountNoFrom;
private int accountNoTo;
private final BigDecimal money;
private final int accountNoFrom;
private final int accountNoTo;
/**
* Instantiates a new Money transfer event.
@ -63,7 +67,7 @@ public class MoneyTransferEvent extends DomainEvent {
}
/**
* Gets account no from.
* Gets account no which the money comes from.
*
* @return the account no from
*/
@ -72,7 +76,7 @@ public class MoneyTransferEvent extends DomainEvent {
}
/**
* Gets account no to.
* Gets account no which the money goes to.
*
* @return the account no to
*/

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;
import com.iluwatar.event.sourcing.api.DomainEvent;
import com.iluwatar.event.sourcing.api.EventProcessor;
import com.iluwatar.event.sourcing.api.ProcessorJournal;
import com.iluwatar.event.sourcing.event.DomainEvent;
/**
* This is the implementation of event processor.
* All events are processed by this class.
* This processor uses processorJournal to persist and recover events.
*
* Created by Serdar Hamzaogullari on 06.08.2017.
*/
public class DomainEventProcessor implements EventProcessor {
public class DomainEventProcessor {
private ProcessorJournal precessorJournal;
private final JsonFileJournal processorJournal = new JsonFileJournal();
@Override
/**
* Process.
*
* @param domainEvent the domain event
*/
public void process(DomainEvent domainEvent) {
domainEvent.process();
precessorJournal.write(domainEvent);
processorJournal.write(domainEvent);
}
@Override
public void setPrecessorJournal(ProcessorJournal precessorJournal) {
this.precessorJournal = precessorJournal;
/**
* Reset.
*/
public void reset() {
processorJournal.reset();
}
@Override
/**
* Recover.
*/
public void recover() {
DomainEvent domainEvent;
while (true) {
domainEvent = precessorJournal.readNext();
domainEvent = processorJournal.readNext();
if (domainEvent == null) {
break;
} else {

View File

@ -20,17 +20,15 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.event.sourcing.journal;
package com.iluwatar.event.sourcing.processor;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import com.iluwatar.event.sourcing.api.DomainEvent;
import com.iluwatar.event.sourcing.api.ProcessorJournal;
import com.iluwatar.event.sourcing.event.AccountCreateEvent;
import com.iluwatar.event.sourcing.event.DomainEvent;
import com.iluwatar.event.sourcing.event.MoneyDepositEvent;
import com.iluwatar.event.sourcing.event.MoneyTransferEvent;
import com.iluwatar.event.sourcing.event.MoneyWithdrawalEvent;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
@ -44,12 +42,16 @@ import java.util.ArrayList;
import java.util.List;
/**
* This is the implementation of event journal.
* This implementation serialize/deserialize the events with JSON
* and writes/reads them on a Journal.json file at the working directory.
*
* Created by Serdar Hamzaogullari on 06.08.2017.
*/
public class JsonFileJournal implements ProcessorJournal {
public class JsonFileJournal {
private File aFile;
private List<String> events = new ArrayList<>();
private final File aFile;
private final List<String> events = new ArrayList<>();
private int index = 0;
/**
@ -72,7 +74,12 @@ public class JsonFileJournal implements ProcessorJournal {
}
}
@Override
/**
* Write.
*
* @param domainEvent the domain event
*/
public void write(DomainEvent domainEvent) {
Gson gson = new Gson();
JsonElement jsonElement;
@ -80,9 +87,7 @@ public class JsonFileJournal implements ProcessorJournal {
jsonElement = gson.toJsonTree(domainEvent, AccountCreateEvent.class);
} else if (domainEvent instanceof MoneyDepositEvent) {
jsonElement = gson.toJsonTree(domainEvent, MoneyDepositEvent.class);
} else if (domainEvent instanceof MoneyWithdrawalEvent) {
jsonElement = gson.toJsonTree(domainEvent, MoneyWithdrawalEvent.class);
} else if (domainEvent instanceof MoneyTransferEvent) {
} else if (domainEvent instanceof MoneyTransferEvent) {
jsonElement = gson.toJsonTree(domainEvent, MoneyTransferEvent.class);
} else {
throw new RuntimeException("Journal Event not recegnized");
@ -97,13 +102,20 @@ public class JsonFileJournal implements ProcessorJournal {
}
}
@Override
/**
* Reset.
*/
public void reset() {
aFile.delete();
}
@Override
/**
* Read next domain event.
*
* @return the domain event
*/
public DomainEvent readNext() {
if (index >= events.size()) {
return null;
@ -122,9 +134,7 @@ public class JsonFileJournal implements ProcessorJournal {
domainEvent = gson.fromJson(jsonElement, MoneyDepositEvent.class);
} else if (eventClassName.equals("MoneyTransferEvent")) {
domainEvent = gson.fromJson(jsonElement, MoneyTransferEvent.class);
} else if (eventClassName.equals("MoneyWithdrawalEvent")) {
domainEvent = gson.fromJson(jsonElement, MoneyWithdrawalEvent.class);
} else {
} else {
throw new RuntimeException("Journal Event not recegnized");
}

View File

@ -27,12 +27,18 @@ import java.util.HashMap;
import java.util.Map;
/**
* This is the static accounts map holder class.
* This class holds the state of the accounts.
*
* Created by Serdar Hamzaogullari on 06.08.2017.
*/
public class AccountAggregate {
private static Map<Integer, Account> accounts = new HashMap<>();
private AccountAggregate() {
}
/**
* Put account.
*
@ -46,7 +52,7 @@ public class AccountAggregate {
* Gets account.
*
* @param accountNo the account no
* @return the account
* @return the copy of the account or null if not found
*/
public static Account getAccount(int accountNo) {
Account account = accounts.get(accountNo);

View File

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