- 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:
parent
82d7e57e8e
commit
c6354c48bb
Binary file not shown.
Before Width: | Height: | Size: 103 KiB After Width: | Height: | Size: 59 KiB |
@ -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"
|
||||||
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -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();
|
|
||||||
}
|
|
@ -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();
|
|
||||||
}
|
|
@ -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());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
*/
|
*/
|
||||||
|
@ -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
|
|
||||||
+ '}';
|
|
||||||
}
|
|
||||||
}
|
|
@ -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 {
|
||||||
|
@ -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 {
|
@ -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.
|
||||||
|
@ -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
|
||||||
*/
|
*/
|
||||||
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
@ -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 {
|
||||||
|
@ -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,9 +87,7 @@ 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) {
|
} else if (domainEvent instanceof MoneyTransferEvent) {
|
||||||
jsonElement = gson.toJsonTree(domainEvent, MoneyWithdrawalEvent.class);
|
|
||||||
} else if (domainEvent instanceof MoneyTransferEvent) {
|
|
||||||
jsonElement = gson.toJsonTree(domainEvent, MoneyTransferEvent.class);
|
jsonElement = gson.toJsonTree(domainEvent, MoneyTransferEvent.class);
|
||||||
} else {
|
} else {
|
||||||
throw new RuntimeException("Journal Event not recegnized");
|
throw new RuntimeException("Journal Event not recegnized");
|
||||||
@ -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,9 +134,7 @@ 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")) {
|
} else {
|
||||||
domainEvent = gson.fromJson(jsonElement, MoneyWithdrawalEvent.class);
|
|
||||||
} else {
|
|
||||||
throw new RuntimeException("Journal Event not recegnized");
|
throw new RuntimeException("Journal Event not recegnized");
|
||||||
}
|
}
|
||||||
|
|
@ -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);
|
||||||
|
@ -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());
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user