Skip to content

Commit

Permalink
Develop (#16)
Browse files Browse the repository at this point in the history
  • Loading branch information
michael-schnell authored Jan 9, 2024
1 parent a9a3e7e commit ff32db0
Show file tree
Hide file tree
Showing 158 changed files with 2,517 additions and 3,314 deletions.
49 changes: 43 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ Here is an overview of how such an application looks like:
- **[Aggregates](aggregates)** - DDD related code for all demo applications (aggregates, entities and business exceptions).
- **[Quarkus](quarkus)** - Two microservices (Command & Query) based on [Quarkus](https://quarkus.io/).
- **[Spring Boot](spring-boot)** - Two microservices (Command & Query) based on [Spring Boot](https://spring.io/projects/spring-boot/).
- **[Java SE + CDI](java-se-cdi)** - Two standalone applications (Command & Query) using CDI for dependency injection.

## Getting started
The following instructions are tested on Linux (Ubuntu 22)
Expand Down Expand Up @@ -108,31 +107,69 @@ For more details see [spring-boot/command](spring-boot/command).
You should see a projection named "qry-person-stream" when you click on "Projections" in the top menu.
2. Opening [http://localhost:8080/persons](http://localhost:8080/persons) should show an empty JSON array

### Execute a test command (Console window 4)
Change into the demo directory and execute a command using cURL (See [shell script](demo/create-person-command.sh) and [command](demo/create-person-command.json))
### Execute some create commands (Console window 4)
Change into the demo directory and execute the command using cURL (See [shell script](demo/create-persons.sh) and JSON files with commands in [demo](demo))
```
cd ddd-cqrs-4-java-example/demo
./create-person-command.sh
./create-persons.sh
```
Command service (Console window 3) should show something like
```
Update aggregate: id=PERSON 954177c4-aeb7-4d1e-b6d7-3e02fe9432cb, version=-1, nextVersion=0
Update aggregate: id=PERSON 568df38c-fdc3-4f60-81aa-d3cce9ebfd7b, version=-1, nextVersion=0
Update aggregate: id=PERSON 84565d62-115e-4502-b7c9-38ad69c64b05, version=-1, nextVersion=0
```
Query service (Console window 2) should show something like
```
PersonCreatedEventHandler ... Handle PersonCreatedEvent: Person 'Peter Parker' was created
Handle PersonCreatedEvent: Person 'Harry Osborn' (954177c4-aeb7-4d1e-b6d7-3e02fe9432cb) was created
Handle PersonCreatedEvent: Person 'Mary Jane Watson' (568df38c-fdc3-4f60-81aa-d3cce9ebfd7b) was created
Handle PersonCreatedEvent: Person 'Peter Parker' (84565d62-115e-4502-b7c9-38ad69c64b05) was created
```

### Verify the query data was updated
1. Refreshing [http://localhost:8080/persons](http://localhost:8080/persons) should show
```json
[{"id":"84565d62-115e-4502-b7c9-38ad69c64b05","name":"Peter Parker"}]
[
{
"id": "568df38c-fdc3-4f60-81aa-d3cce9ebfd7b",
"name": "Mary Jane Watson"
},
{
"id": "84565d62-115e-4502-b7c9-38ad69c64b05",
"name": "Peter Parker"
},
{
"id": "954177c4-aeb7-4d1e-b6d7-3e02fe9432cb",
"name": "Harry Osborn"
}
]
```
2. Opening [http://localhost:8080/persons/84565d62-115e-4502-b7c9-38ad69c64b05](http://localhost:8080/persons/84565d62-115e-4502-b7c9-38ad69c64b05) should show
```json
{"id":"84565d62-115e-4502-b7c9-38ad69c64b05","name":"Peter Parker"}
3. The event sourced data of the person aggregate could be found in a stream named [PERSON-84565d62-115e-4502-b7c9-38ad69c64b05](http://localhost:2113/web/index.html#/streams/PERSON-84565d62-115e-4502-b7c9-38ad69c64b05)

### Execute a delete command (Console window 4)
Change into the demo directory and execute the command using cURL (See [shell script](demo/create-persons.sh) and JSON files with commands in [demo](demo))
```
cd ddd-cqrs-4-java-example/demo
./delete-harry-osborn.sh
```
### Verify the query data was updated
1. Refreshing [http://localhost:8080/persons](http://localhost:8080/persons) should show
```json
[
{
"id": "568df38c-fdc3-4f60-81aa-d3cce9ebfd7b",
"name": "Mary Jane Watson"
},
{
"id": "84565d62-115e-4502-b7c9-38ad69c64b05",
"name": "Peter Parker"
}
]
```
"Harry Osborn" should no longer be present in the list.
### Stop Event Store and Maria DB and clean up
1. Stop Docker Compose (Ubuntu shortcut = ctrl c)
Expand Down
4 changes: 2 additions & 2 deletions aggregates/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<parent>
<groupId>org.fuin.cqrs4j.example</groupId>
<artifactId>cqrs4j-example-root</artifactId>
<version>0.3.0</version>
<version>0.4.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand All @@ -23,7 +23,7 @@
<dependency>
<groupId>org.fuin.cqrs4j.example</groupId>
<artifactId>cqrs4j-example-shared</artifactId>
<version>0.3.0</version>
<version>0.4.0-SNAPSHOT</version>
</dependency>

<dependency>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,6 @@
/**
* Copyright (C) 2015 Michael Schnell. All rights reserved. http://www.fuin.org/
*
* This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with this library. If not, see
* http://www.gnu.org/licenses/.
*/
package org.fuin.cqrs4j.example.aggregates;

import jakarta.validation.constraints.NotNull;

import org.fuin.cqrs4j.example.shared.PersonId;
import org.fuin.cqrs4j.example.shared.PersonName;
import org.fuin.objects4j.common.ExceptionShortIdentifable;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,25 @@
package org.fuin.cqrs4j.example.javasecdi.cmd.domain;
package org.fuin.cqrs4j.example.aggregates;

import org.fuin.objects4j.common.NotThreadSafe;
import jakarta.validation.constraints.NotNull;

import org.fuin.cqrs4j.example.shared.PersonId;
import org.fuin.ddd4j.ddd.EntityType;
import org.fuin.ddd4j.esrepo.EventStoreRepository;
import org.fuin.esc.api.EventStore;
import org.fuin.objects4j.common.NotThreadSafe;

/**
* Event sourced repository for storing a {@link Person} aggregate.
*/
@NotThreadSafe
public class PersonRepository extends EventStoreRepository<PersonId, Person> {
public class EventStorePersonRepository extends EventStoreRepository<PersonId, Person> implements PersonRepository {

/**
* Constructor all mandatory data.
*
*
* @param eventStore
* Event store.
*/
public PersonRepository(final EventStore eventStore) {
public EventStorePersonRepository(final EventStore eventStore) {
super(eventStore);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,30 +1,19 @@
/**
* Copyright (C) 2015 Michael Schnell. All rights reserved. http://www.fuin.org/
*
* This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with this library. If not, see
* http://www.gnu.org/licenses/.
*/
package org.fuin.cqrs4j.example.aggregates;

import java.io.Serializable;
import java.util.Optional;

import jakarta.validation.constraints.NotNull;

import org.fuin.cqrs4j.example.shared.PersonCreatedEvent;
import org.fuin.cqrs4j.example.shared.PersonDeletedEvent;
import org.fuin.cqrs4j.example.shared.PersonId;
import org.fuin.cqrs4j.example.shared.PersonName;
import org.fuin.ddd4j.ddd.AbstractAggregateRoot;
import org.fuin.ddd4j.ddd.AggregateDeletedException;
import org.fuin.ddd4j.ddd.ApplyEvent;
import org.fuin.ddd4j.ddd.EntityType;
import org.fuin.objects4j.common.Contract;

import java.io.Serializable;
import java.util.Optional;

/**
* Represents a natural person.
*/
Expand All @@ -35,6 +24,11 @@ public class Person extends AbstractAggregateRoot<PersonId> implements Serializa
@NotNull
private PersonId id;

@NotNull
private PersonName name;

private boolean deleted;

/**
* Default constructor that is mandatory for aggregate roots.
*/
Expand Down Expand Up @@ -72,10 +66,22 @@ public Person(@NotNull final PersonId id, @NotNull final PersonName name, final
}

// CREATE EVENT
apply(new PersonCreatedEvent(id, name));
apply(new PersonCreatedEvent.Builder().id(id).name(name).version(getNextVersion() + 1).build());

}

/**
* Deletes the person.
*
* @throws AggregateDeletedException The aggregate was already deleted.
*/
public void delete() throws AggregateDeletedException {
if (deleted) {
throw new AggregateDeletedException(PersonId.TYPE, id);
}
apply(new PersonDeletedEvent.Builder().id(id).name(name).version(getNextVersion() + 1).build());
}

@Override
public PersonId getId() {
return id;
Expand All @@ -89,6 +95,12 @@ public EntityType getType() {
@ApplyEvent
public void applyEvent(final PersonCreatedEvent event) {
this.id = event.getEntityId();
this.name = event.getName();
}

@ApplyEvent
public void applyEvent(final PersonDeletedEvent event) {
this.deleted = true;
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,63 +1,11 @@
/**
* Copyright (C) 2015 Michael Schnell. All rights reserved. http://www.fuin.org/
*
* This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with this library. If not, see
* http://www.gnu.org/licenses/.
*/
package org.fuin.cqrs4j.example.aggregates;

import jakarta.validation.constraints.NotNull;

import org.fuin.cqrs4j.example.shared.PersonId;
import org.fuin.ddd4j.ddd.EntityType;
import org.fuin.ddd4j.esrepo.EventStoreRepository;
import org.fuin.esc.api.EventStore;
import org.fuin.objects4j.common.NotThreadSafe;
import org.fuin.ddd4j.ddd.Repository;

/**
* Event sourced repository for storing a {@link Person} aggregate.
*/
@NotThreadSafe
public class PersonRepository extends EventStoreRepository<PersonId, Person> {

/**
* Constructor all mandatory data.
*
* @param eventStore
* Event store.
*/
public PersonRepository(final EventStore eventStore) {
super(eventStore);
}

@Override
@NotNull
public Class<Person> getAggregateClass() {
return Person.class;
}

@Override
@NotNull
public EntityType getAggregateType() {
return PersonId.TYPE;
}

@Override
@NotNull
public Person create() {
return new Person();
}

@Override
@NotNull
public String getIdParamName() {
return "personId";
}
public interface PersonRepository extends Repository<PersonId, Person> {

}
Original file line number Diff line number Diff line change
@@ -1,15 +1,3 @@
/**
* Copyright (C) 2015 Michael Schnell. All rights reserved. http://www.fuin.org/
*
* This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with this library. If not, see
* http://www.gnu.org/licenses/.
*/
package org.fuin.cqrs4j.example.aggregates;

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
package org.fuin.cqrs4j.example.aggregates;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.fail;

import java.util.Optional;
import java.util.UUID;

import org.fuin.cqrs4j.example.shared.PersonCreatedEvent;
import org.fuin.cqrs4j.example.shared.PersonDeletedEvent;
import org.fuin.cqrs4j.example.shared.PersonId;
import org.fuin.cqrs4j.example.shared.PersonName;
import org.fuin.ddd4j.ddd.AggregateDeletedException;
import org.junit.jupiter.api.Test;

import java.util.Optional;
import java.util.UUID;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.fail;

/**
* Test for the {@link Person} class.
*/
Expand All @@ -33,6 +35,7 @@ public final void testCreateOK() throws DuplicatePersonNameException {
assertThat(testee.getUncommittedChanges().get(0)).isInstanceOf(PersonCreatedEvent.class);
final PersonCreatedEvent event = (PersonCreatedEvent) testee.getUncommittedChanges().get(0);
assertThat(event.getEntityId()).isEqualTo(personId);
assertThat(event.getAggregateVersionInteger()).isEqualTo(0);
assertThat(event.getName()).isEqualTo(personName);

}
Expand All @@ -57,4 +60,29 @@ public final void testCreateDuplicateName() {

}

@Test
public void testDeleteOK() throws DuplicatePersonNameException, AggregateDeletedException {

// PREPARE
final PersonId personId = new PersonId(UUID.randomUUID());
final PersonName personName = new PersonName("Peter Parker");
final PersonId otherId = new PersonId(UUID.randomUUID());
final Person testee = new Person(personId, personName, pid -> {
return Optional.empty();
});
testee.markChangesAsCommitted();

// TEST
testee.delete();

//VERIFY
assertThat(testee.getUncommittedChanges()).hasSize(1);
assertThat(testee.getUncommittedChanges().get(0)).isInstanceOf(PersonDeletedEvent.class);
final PersonDeletedEvent event = (PersonDeletedEvent) testee.getUncommittedChanges().get(0);
assertThat(event.getEntityId()).isEqualTo(personId);
assertThat(event.getAggregateVersionInteger()).isEqualTo(1);
assertThat(event.getName()).isEqualTo(personName);

}

}
6 changes: 6 additions & 0 deletions demo/create-harry-osborn-command.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"event-id": "5601097d-6e2e-4df1-a7b2-ecc4f443c068",
"event-timestamp": "2024-01-07T10:00:00.000+01:00[Europe/Berlin]",
"entity-id-path": "PERSON 954177c4-aeb7-4d1e-b6d7-3e02fe9432cb",
"name": "Harry Osborn"
}
6 changes: 6 additions & 0 deletions demo/create-mary-jane-watson-command.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"event-id": "4bf5bb56-4fe8-47a3-8358-25144e15497d",
"event-timestamp": "2024-01-07T09:00:00.000+01:00[Europe/Berlin]",
"entity-id-path": "PERSON 568df38c-fdc3-4f60-81aa-d3cce9ebfd7b",
"name": "Mary Jane Watson"
}
Loading

0 comments on commit ff32db0

Please sign in to comment.