Repository: iluwatar/java-design-patterns
Issue: Refactor event sourcing
Labels: type: refactoring
epic: code quality
status: stale

I'll refactor some codes of event sourcing pattern. The specific tasks I suggest are listed below.

- [ ] remove the use of deprecated `JsonParser`
- [ ] use jackson `ObjectMapper` instead of `Gson`
- [ ] fix some warnings checked by sonarlint

Issue comments:
This issue has been automatically marked as stale because it has not had recent activity. The issue will be unassigned if no further activity occurs. Thank you for your contributions.

Hey, seeing as this issue hasn't been solved I would like to give a try if it's ok.
This issue has been automatically marked as stale because it has not had recent activity. The issue will be unassigned if no further activity occurs. Thank you for your contributions.


Pull request review comments:
<picture><img alt="6% of developers fix this issue" src="https://lift.sonatype.com/api/commentimage/fixrate/6/display.svg"></picture>

<b>*[UnnecessarilyFullyQualified](https://errorprone.info/bugpattern/UnnecessarilyFullyQualified):</b>*  This fully qualified name is unambiguous to the compiler if imported.

---


```suggestion
Generated
```


❗❗ <b>3 similar findings have been found in this PR</b>

<details><summary>🔎 Expand here to view all instances of this finding</summary><br/>
  
  
<div align=\"center\">


| **File Path** | **Line Number** |
| ------------- | ------------- |
| event-sourcing/src/main/java/com/iluwatar/event/sourcing/processor/EventJournal.java | [10](https://github.com/iluwatar/java-design-patterns/blob/b88f0beba0f80885ff4d1e60d99b5acc1f146287/event-sourcing/src/main/java/com/iluwatar/event/sourcing/processor/EventJournal.java#L10) |
| event-sourcing/src/main/java/com/iluwatar/event/sourcing/processor/EventJournal.java | [10](https://github.com/iluwatar/java-design-patterns/blob/b88f0beba0f80885ff4d1e60d99b5acc1f146287/event-sourcing/src/main/java/com/iluwatar/event/sourcing/processor/EventJournal.java#L10) |
| event-sourcing/src/main/java/com/iluwatar/event/sourcing/processor/EventJournal.java | [10](https://github.com/iluwatar/java-design-patterns/blob/b88f0beba0f80885ff4d1e60d99b5acc1f146287/event-sourcing/src/main/java/com/iluwatar/event/sourcing/processor/EventJournal.java#L10) |
<p><a href="https://lift.sonatype.com/results/github.com/iluwatar/java-design-patterns/01GZXQTRGNEZWMFTDJ3QA86Q84?t=ErrorProne|UnnecessarilyFullyQualified" target="_blank">Visit the Lift Web Console</a> to find more details in your report.</p></div></details>



---

<details><summary>ℹ️ Expand to see all <b>@sonatype-lift</b> commands</summary>

You can reply with the following commands. For example, reply with ***@sonatype-lift ignoreall*** to leave out all findings.
| **Command** | **Usage** |
| ------------- | ------------- |
| `@sonatype-lift ignore` | Leave out the above finding from this PR |
| `@sonatype-lift ignoreall` | Leave out all the existing findings from this PR |
| `@sonatype-lift exclude <file\|issue\|path\|tool>` | Exclude specified `file\|issue\|path\|tool` from Lift findings by updating your config.toml file |

**Note:** When talking to LiftBot, you need to **refresh** the page to see its response.
<sub>[Click here](https://github.com/apps/sonatype-lift/installations/new) to add LiftBot to another repo.</sub></details>



Patch that resolved the issue:
diff --git a/event-sourcing/pom.xml b/event-sourcing/pom.xml
index ae609d67e76b..569a5fceebd8 100644
--- a/event-sourcing/pom.xml
+++ b/event-sourcing/pom.xml
@@ -40,8 +40,8 @@
       <scope>test</scope>
     </dependency>
     <dependency>
-      <groupId>com.google.code.gson</groupId>
-      <artifactId>gson</artifactId>
+      <groupId>com.fasterxml.jackson.core</groupId>
+      <artifactId>jackson-databind</artifactId>
     </dependency>
   </dependencies>
   <build>
diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/app/App.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/app/App.java
index d356a8b99f8a..4b90c6ae0bdb 100644
--- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/app/App.java
+++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/app/App.java
@@ -28,6 +28,7 @@
 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.JsonFileJournal;
 import com.iluwatar.event.sourcing.state.AccountAggregate;
 import java.math.BigDecimal;
 import java.util.Date;
@@ -42,16 +43,18 @@
  * transactional data, and maintain full audit trails and history that can enable compensating
  * actions.
  *
- * <p>This App class is an example usage of an Event Sourcing pattern. As an example, two bank accounts
- * are created, then some money deposit and transfer actions are taken, so a new state of accounts is
- * created. At that point, state is cleared in order to represent a system shut-down. After the shut-down,
- * system state is recovered by re-creating the past events from event journals. Then state is
- * printed so a user can view the last state is same with the state before a system shut-down.
+ * <p>This App class is an example usage of an Event Sourcing pattern. As an example, two bank
+ * accounts are created, then some money deposit and transfer actions are taken, so a new state of
+ * accounts is created. At that point, state is cleared in order to represent a system shut-down.
+ * After the shut-down, system state is recovered by re-creating the past events from event
+ * journals. Then state is printed so a user can view the last state is same with the state before a
+ * system shut-down.
  *
  * <p>Created by Serdar Hamzaogullari on 06.08.2017.
  */
 @Slf4j
 public class App {
+
   /**
    * The constant ACCOUNT OF DAENERYS.
    */
@@ -68,8 +71,7 @@ public class App {
    */
   public static void main(String[] args) {
 
-    var eventProcessor = new DomainEventProcessor();
-
+    var eventProcessor = new DomainEventProcessor(new JsonFileJournal());
 
     LOGGER.info("Running the system first time............");
     eventProcessor.reset();
@@ -103,7 +105,7 @@ public static void main(String[] args) {
 
     LOGGER.info("Recover the system by the events in journal file............");
 
-    eventProcessor = new DomainEventProcessor();
+    eventProcessor = new DomainEventProcessor(new JsonFileJournal());
     eventProcessor.recover();
 
     LOGGER.info("...............Recovered State:............");
diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/AccountCreateEvent.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/AccountCreateEvent.java
index 9e6b40e62ee0..f3f4e78e6ab3 100644
--- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/AccountCreateEvent.java
+++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/AccountCreateEvent.java
@@ -24,14 +24,15 @@
  */
 package com.iluwatar.event.sourcing.event;
 
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
 import com.iluwatar.event.sourcing.domain.Account;
 import com.iluwatar.event.sourcing.state.AccountAggregate;
 import lombok.Getter;
 
 /**
- * This is the class that implements account created event.
- * Holds the necessary info for an account created event.
- * Implements the process function that finds the event-related domain objects and
+ * This is the class that implements account created event. Holds the necessary info for an account
+ * created event. Implements the process function that finds the event-related domain objects and
  * calls the related domain object's handle event functions
  *
  * <p>Created by Serdar Hamzaogullari on 06.08.2017.
@@ -50,7 +51,10 @@ public class AccountCreateEvent extends DomainEvent {
    * @param accountNo   the account no
    * @param owner       the owner
    */
-  public AccountCreateEvent(long sequenceId, long createdTime, int accountNo, String owner) {
+  @JsonCreator
+  public AccountCreateEvent(@JsonProperty("sequenceId") long sequenceId,
+      @JsonProperty("createdTime") long createdTime,
+      @JsonProperty("accountNo") int accountNo, @JsonProperty("owner") String owner) {
     super(sequenceId, createdTime, "AccountCreateEvent");
     this.accountNo = accountNo;
     this.owner = owner;
diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyDepositEvent.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyDepositEvent.java
index 96afd4640012..07e88bc0ea28 100644
--- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyDepositEvent.java
+++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyDepositEvent.java
@@ -24,6 +24,8 @@
  */
 package com.iluwatar.event.sourcing.event;
 
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
 import com.iluwatar.event.sourcing.state.AccountAggregate;
 import java.math.BigDecimal;
 import java.util.Optional;
@@ -50,7 +52,10 @@ public class MoneyDepositEvent extends DomainEvent {
    * @param accountNo   the account no
    * @param money       the money
    */
-  public MoneyDepositEvent(long sequenceId, long createdTime, int accountNo, BigDecimal money) {
+  @JsonCreator
+  public MoneyDepositEvent(@JsonProperty("sequenceId") long sequenceId,
+      @JsonProperty("createdTime") long createdTime,
+      @JsonProperty("accountNo") int accountNo, @JsonProperty("money") BigDecimal money) {
     super(sequenceId, createdTime, "MoneyDepositEvent");
     this.money = money;
     this.accountNo = accountNo;
diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyTransferEvent.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyTransferEvent.java
index 18d98cded008..3b4fa1ed1bb0 100644
--- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyTransferEvent.java
+++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyTransferEvent.java
@@ -24,6 +24,8 @@
  */
 package com.iluwatar.event.sourcing.event;
 
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
 import com.iluwatar.event.sourcing.state.AccountAggregate;
 import java.math.BigDecimal;
 import java.util.Optional;
@@ -52,8 +54,11 @@ public class MoneyTransferEvent extends DomainEvent {
    * @param accountNoFrom the account no from
    * @param accountNoTo   the account no to
    */
-  public MoneyTransferEvent(long sequenceId, long createdTime, BigDecimal money, int accountNoFrom,
-                            int accountNoTo) {
+  @JsonCreator
+  public MoneyTransferEvent(@JsonProperty("sequenceId") long sequenceId,
+      @JsonProperty("createdTime") long createdTime,
+      @JsonProperty("money") BigDecimal money, @JsonProperty("accountNoFrom") int accountNoFrom,
+      @JsonProperty("accountNoTo") int accountNoTo) {
     super(sequenceId, createdTime, "MoneyTransferEvent");
     this.money = money;
     this.accountNoFrom = accountNoFrom;
diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/processor/DomainEventProcessor.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/processor/DomainEventProcessor.java
index e1f11eebec6b..cdfe44e336b2 100644
--- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/processor/DomainEventProcessor.java
+++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/processor/DomainEventProcessor.java
@@ -28,13 +28,17 @@
 
 /**
  * This is the implementation of event processor. All events are processed by this class. This
- * processor uses processorJournal to persist and recover events.
+ * processor uses eventJournal to persist and recover events.
  *
  * <p>Created by Serdar Hamzaogullari on 06.08.2017.
  */
 public class DomainEventProcessor {
 
-  private final JsonFileJournal processorJournal = new JsonFileJournal();
+  private final EventJournal eventJournal;
+
+  public DomainEventProcessor(EventJournal eventJournal) {
+    this.eventJournal = eventJournal;
+  }
 
   /**
    * Process.
@@ -43,14 +47,14 @@ public class DomainEventProcessor {
    */
   public void process(DomainEvent domainEvent) {
     domainEvent.process();
-    processorJournal.write(domainEvent);
+    eventJournal.write(domainEvent);
   }
 
   /**
    * Reset.
    */
   public void reset() {
-    processorJournal.reset();
+    eventJournal.reset();
   }
 
   /**
@@ -58,7 +62,7 @@ public void reset() {
    */
   public void recover() {
     DomainEvent domainEvent;
-    while ((domainEvent = processorJournal.readNext()) != null) {
+    while ((domainEvent = eventJournal.readNext()) != null) {
       domainEvent.process();
     }
   }
diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/processor/EventJournal.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/processor/EventJournal.java
new file mode 100644
index 000000000000..49bc89582a13
--- /dev/null
+++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/processor/EventJournal.java
@@ -0,0 +1,37 @@
+package com.iluwatar.event.sourcing.processor;
+
+import com.iluwatar.event.sourcing.event.DomainEvent;
+import java.io.File;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * Base class for Journaling implementations.
+ */
+@Slf4j
+public abstract class EventJournal {
+
+  File file;
+
+  /**
+   * Write.
+   *
+   * @param domainEvent the domain event.
+   */
+  abstract void write(DomainEvent domainEvent);
+
+  /**
+   * Reset.
+   */
+  void reset() {
+    if (file.delete()) {
+      LOGGER.info("File cleared successfully............");
+    }
+  }
+
+  /**
+   * Read domain event.
+   *
+   * @return the domain event.
+   */
+  abstract DomainEvent readNext();
+}
diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/processor/JsonFileJournal.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/processor/JsonFileJournal.java
index 048e270e2eaa..cfde566dc2fb 100644
--- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/processor/JsonFileJournal.java
+++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/processor/JsonFileJournal.java
@@ -24,9 +24,8 @@
  */
 package com.iluwatar.event.sourcing.processor;
 
-import com.google.gson.Gson;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
 import com.iluwatar.event.sourcing.event.AccountCreateEvent;
 import com.iluwatar.event.sourcing.event.DomainEvent;
 import com.iluwatar.event.sourcing.event.MoneyDepositEvent;
@@ -49,9 +48,8 @@
  *
  * <p>Created by Serdar Hamzaogullari on 06.08.2017.
  */
-public class JsonFileJournal {
+public class JsonFileJournal extends EventJournal {
 
-  private final File file;
   private final List<String> events = new ArrayList<>();
   private int index = 0;
 
@@ -81,22 +79,12 @@ public JsonFileJournal() {
    *
    * @param domainEvent the domain event
    */
+  @Override
   public void write(DomainEvent domainEvent) {
-    var gson = new Gson();
-    JsonElement jsonElement;
-    if (domainEvent instanceof AccountCreateEvent) {
-      jsonElement = gson.toJsonTree(domainEvent, AccountCreateEvent.class);
-    } else if (domainEvent instanceof MoneyDepositEvent) {
-      jsonElement = gson.toJsonTree(domainEvent, MoneyDepositEvent.class);
-    } else if (domainEvent instanceof MoneyTransferEvent) {
-      jsonElement = gson.toJsonTree(domainEvent, MoneyTransferEvent.class);
-    } else {
-      throw new RuntimeException("Journal Event not recognized");
-    }
-
+    var mapper = new ObjectMapper();
     try (var output = new BufferedWriter(
         new OutputStreamWriter(new FileOutputStream(file, true), StandardCharsets.UTF_8))) {
-      var eventString = jsonElement.toString();
+      var eventString = mapper.writeValueAsString(domainEvent);
       output.write(eventString + "\r\n");
     } catch (IOException e) {
       throw new RuntimeException(e);
@@ -104,14 +92,6 @@ public void write(DomainEvent domainEvent) {
   }
 
 
-  /**
-   * Reset.
-   */
-  public void reset() {
-    file.delete();
-  }
-
-
   /**
    * Read the next domain event.
    *
@@ -124,19 +104,19 @@ public DomainEvent readNext() {
     var event = events.get(index);
     index++;
 
-    var parser = new JsonParser();
-    var jsonElement = parser.parse(event);
-    var eventClassName = jsonElement.getAsJsonObject().get("eventClassName").getAsString();
-    var gson = new Gson();
+    var mapper = new ObjectMapper();
     DomainEvent domainEvent;
-    if (eventClassName.equals("AccountCreateEvent")) {
-      domainEvent = gson.fromJson(jsonElement, AccountCreateEvent.class);
-    } else if (eventClassName.equals("MoneyDepositEvent")) {
-      domainEvent = gson.fromJson(jsonElement, MoneyDepositEvent.class);
-    } else if (eventClassName.equals("MoneyTransferEvent")) {
-      domainEvent = gson.fromJson(jsonElement, MoneyTransferEvent.class);
-    } else {
-      throw new RuntimeException("Journal Event not recegnized");
+    try {
+      var jsonElement = mapper.readTree(event);
+      var eventClassName = jsonElement.get("eventClassName").asText();
+      domainEvent = switch (eventClassName) {
+        case "AccountCreateEvent" -> mapper.treeToValue(jsonElement, AccountCreateEvent.class);
+        case "MoneyDepositEvent" -> mapper.treeToValue(jsonElement, MoneyDepositEvent.class);
+        case "MoneyTransferEvent" -> mapper.treeToValue(jsonElement, MoneyTransferEvent.class);
+        default -> throw new RuntimeException("Journal Event not recognized");
+      };
+    } catch (JsonProcessingException jsonProcessingException) {
+      throw new RuntimeException("Failed to convert JSON");
     }
 
     domainEvent.setRealTime(false);


Test patch:
diff --git a/event-sourcing/src/test/java/IntegrationTest.java b/event-sourcing/src/test/java/IntegrationTest.java
index 9e15a2163bcc..c737bd0da43d 100644
--- a/event-sourcing/src/test/java/IntegrationTest.java
+++ b/event-sourcing/src/test/java/IntegrationTest.java
@@ -22,6 +22,7 @@
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  * THE SOFTWARE.
  */
+
 import static com.iluwatar.event.sourcing.app.App.ACCOUNT_OF_DAENERYS;
 import static com.iluwatar.event.sourcing.app.App.ACCOUNT_OF_JON;
 import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -30,6 +31,7 @@
 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.JsonFileJournal;
 import com.iluwatar.event.sourcing.state.AccountAggregate;
 import java.math.BigDecimal;
 import java.util.Date;
@@ -53,7 +55,7 @@ class IntegrationTest {
    */
   @BeforeEach
   void initialize() {
-    eventProcessor = new DomainEventProcessor();
+    eventProcessor = new DomainEventProcessor(new JsonFileJournal());
   }
 
   /**
@@ -84,7 +86,7 @@ void testStateRecovery() {
 
     AccountAggregate.resetState();
 
-    eventProcessor = new DomainEventProcessor();
+    eventProcessor = new DomainEventProcessor(new JsonFileJournal());
     eventProcessor.recover();
 
     var accountOfDaenerysAfterShotDown = AccountAggregate.getAccount(ACCOUNT_OF_DAENERYS);