Skip to content

Instantly share code, notes, and snippets.

@aleixmorgadas
Last active September 6, 2023 04:07
Show Gist options
  • Save aleixmorgadas/be30c6030db2fd9b560ac31cb873f68c to your computer and use it in GitHub Desktop.
Save aleixmorgadas/be30c6030db2fd9b560ac31cb873f68c to your computer and use it in GitHub Desktop.

Revisions

  1. aleixmorgadas revised this gist Sep 6, 2023. 2 changed files with 38 additions and 0 deletions.
    1 change: 1 addition & 0 deletions 0_README.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1 @@
    # Entity Event Sourced with Spring Boot and MongoDB
    37 changes: 37 additions & 0 deletions ProfileRepositoryTest.java
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,37 @@
    package dev.aleixmorgadas.eventsourcedmongo.domain;

    import org.junit.jupiter.api.Test;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.autoconfigure.data.mongo.DataMongoTest;
    import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
    import org.testcontainers.containers.MongoDBContainer;
    import org.testcontainers.junit.jupiter.Container;
    import org.testcontainers.junit.jupiter.Testcontainers;
    import org.testcontainers.utility.DockerImageName;

    import static org.assertj.core.api.Assertions.assertThat;

    @DataMongoTest
    @Testcontainers
    public class ProfileRepositoryTest {
    @Container
    @ServiceConnection
    static MongoDBContainer mongo = new MongoDBContainer(DockerImageName.parse("mongo:6.0.7"));

    @Autowired
    ProfileRepository repository;

    @Test
    void savesProfileIntoDB() {
    var profile = Profile.of("Aleix");
    profile.withAge(30);
    repository.save(profile);

    var id = profile.id;

    var savedProfile = repository.findById(id).orElseThrow();
    assertThat(savedProfile.events).hasSize(2);
    assertThat(savedProfile.name()).isEqualTo("Aleix");
    assertThat(savedProfile.age()).isEqualTo(30);
    }
    }
  2. aleixmorgadas revised this gist Aug 25, 2023. 1 changed file with 1 addition and 0 deletions.
    1 change: 1 addition & 0 deletions Profile.java
    Original file line number Diff line number Diff line change
    @@ -27,6 +27,7 @@ public Profile(String id, String name) {

    void apply(Event event) {
    events.add(event);
    // update with pattern matching in Java 21
    if (event instanceof Event.Created created) {
    on(created);
    } else if (event instanceof Event.AgeUpdated ageUpdated) {
  3. aleixmorgadas revised this gist Aug 25, 2023. 1 changed file with 7 additions and 4 deletions.
    11 changes: 7 additions & 4 deletions Profile.java
    Original file line number Diff line number Diff line change
    @@ -16,7 +16,7 @@ public class Profile {
    final List<Event> events = new ArrayList<>();

    public Profile(String id, String name) {
    on(new Event.Created(id, name));
    apply(new Event.Created(id, name));
    }

    @PersistenceCreator
    @@ -27,16 +27,19 @@ public Profile(String id, String name) {

    void apply(Event event) {
    events.add(event);
    if (event instanceof Event.Created created) {
    on(created);
    } else if (event instanceof Event.AgeUpdated ageUpdated) {
    on(ageUpdated);
    }
    }

    void on(Event.Created created) {
    events.add(created);
    id = created.id;
    state.name = created.name;
    }

    void on(Event.AgeUpdated ageUpdated) {
    events.add(ageUpdated);
    state.age = ageUpdated.age();
    }

    @@ -53,7 +56,7 @@ public int age() {
    }

    public void withAge(int age) {
    on(new Event.AgeUpdated(age));
    apply(new Event.AgeUpdated(age));
    }

    static class State {
  4. aleixmorgadas created this gist Aug 25, 2023.
    68 changes: 68 additions & 0 deletions Profile.java
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,68 @@
    package dev.aleixmorgadas.eventsourcedmongo.domain;

    import org.springframework.data.annotation.Id;
    import org.springframework.data.annotation.PersistenceCreator;
    import org.springframework.data.annotation.Transient;
    import org.springframework.data.mongodb.core.mapping.Document;

    import java.util.*;

    @Document
    public class Profile {
    @Id
    String id;
    @Transient
    final State state = new State();
    final List<Event> events = new ArrayList<>();

    public Profile(String id, String name) {
    on(new Event.Created(id, name));
    }

    @PersistenceCreator
    Profile(String id, List<Event> events) {
    this.id = id;
    events.forEach(this::apply);
    }

    void apply(Event event) {
    events.add(event);
    }

    void on(Event.Created created) {
    events.add(created);
    id = created.id;
    state.name = created.name;
    }

    void on(Event.AgeUpdated ageUpdated) {
    events.add(ageUpdated);
    state.age = ageUpdated.age();
    }

    String name() {
    return state.name;
    }

    public static Profile of(String name) {
    return new Profile(UUID.randomUUID().toString(), name);
    }

    public int age() {
    return state.age;
    }

    public void withAge(int age) {
    on(new Event.AgeUpdated(age));
    }

    static class State {
    String name;
    int age;
    }

    sealed interface Event {
    record Created(String id, String name) implements Event {}
    record AgeUpdated(int age) implements Event {}
    }
    }
    26 changes: 26 additions & 0 deletions ProfileTest.java
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,26 @@
    package dev.aleixmorgadas.eventsourcedmongo.domain;

    import org.junit.jupiter.api.Test;

    import static org.assertj.core.api.Assertions.assertThat;

    public class ProfileTest {

    @Test
    void createsAProfile() {
    var profile = Profile.of("Aleix");

    assertThat(profile.name()).isEqualTo("Aleix");
    assertThat(profile.events).hasSize(1);
    }

    @Test
    void addsAge() {
    var profile = Profile.of("Aleix");
    profile.withAge(30);

    assertThat(profile.age()).isEqualTo(30);
    assertThat(profile.events).hasSize(2);
    assertThat(profile.events.get(1)).isInstanceOf(Profile.Event.AgeUpdated.class);
    }
    }