Search code examples
javaspring-bootmicroservicesaxon

Axon - Does the unique Id of my Aggregate equal/be the same as that of my Entity?


I must be doing something wrong here.

I have a very simple Axon application that has two simple functions: create a person & change the name of the person.

So I have Person Entity:

@Entity
@Data
@NoArgsConstructor
public class Person {    
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    private String name;
}

And my PersonAggregate:

@Aggregate
@Data
@NoArgsConstructor
public class PersonAggregate {

    @AggregateIdentifier
    private UUID id;
    private String name;


    @CommandHandler
    public PersonAggregate(CreatePersonCommand command) {
        apply(new PersonCreatedEvent(
                command.getId(),
                command.getName()
        ));
    }

    @EventSourcingHandler
    public void on(PersonCreatedEvent event) {
        this.id = event.getId();
        this.name = event.getName();
    }

    @CommandHandler
    public void handle(ChangeNameCommand command) {
        apply(
                new NameChangedEvent(
                        command.getId(),
                        command.getName()
                )
        );
    }

    @EventSourcingHandler
    public void on(NameChangedEvent event) {
        this.name = event.getName();
    }

}

And this is my ChangeNameCommand:

@Data
@NoArgsConstructor
@AllArgsConstructor
public class ChangeNameCommand {
    @TargetAggregateIdentifier
     private UUID id;
     private String name;    
}

And this is my NameChangedEvent:

@Value
public class NameChangedEvent {
    private final UUID id;
    private final String name;
}

And Repository:

@Repository
public interface PersonCommandRepository extends JpaRepository<Person, Long> {
}

The Problem is that I am not sure how to structure my Event Handler

@EventHandler
public void changeName(NameChangedEvent event) {
    Optional<Person> opt = null;//this.personCommandRepository.findById(event.getId());
    if (opt.isPresent()) {
        Person person = opt.get();
        person.setName(event.getName());
        this.personCommandRepository.save(person); //Does'nt this defeat the purpose of Event Sourcing? A single point of truth?
    }

And also, in my controller, I have quite I big problem, in the comment:

   @RequestMapping(value = "/rename", method = RequestMethod.POST)
    public ResponseEntity<?> changeName(@RequestParam("id") Long id, @RequestParam("name")String name){
        //1. Which Id do we pass in here? Aggregate or Entity?
        //this.commandGateway.send(new ChangeNameCommand(id,name));
        return new ResponseEntity<String>("Renamed", HttpStatus.OK);
    }

}

Solution

  • I feel you are still mixing the idea of the Command Model and the Query Model with one another.

    The command model, which typically is the aggregate (or several), only handles requests of intent to change some state. These requests of intent, i.e. command message, are thus the sole operations targeted towards your aggregate/command model. Vice versa, if you have the requirement to change something on a Person, that means you dispatch a command marking that intent towards the PersonAggregate.

    The query model on the other hand is solely tasked with answering the questions targeted towards your application. If there is somehow a need to know the state of a Person, that means you dispatch a query towards a component capable of returning the Person entity you have shared.

    Going back to "the problems" you described:

    1. "Doesn't this defeat the purpose of Event Sourcing? A single point of truth?": Event Sourcing is a concept specific towards rehydrating the command model from the events itself has published. Thus, employing Event Sourcing just means you are making certain not only your query models are created through events, but also your command models. Ergo, you are enforcing a single source of truth by doing this. Using the events to update query models thus does not defeat the purpose of ES.
    2. "Which Id do we pass in here? Aggregate or Entity?": When you are in doubt, try to figure out what you want to achieve. Are you requesting information? Then it's a query targeted towards your query model. Is the intent to change some state to a model? Then you dispatch a command towards the command model. In this exact scenario, you want to change the name of a Person. Hence, you dispatch a command towards the Aggregate/Command Model.

    On AxonIQ's webpage there is an Architectural Concepts section describing all the main principles. There's also one about CQRS which might help. Might help to read up on these; pretty sure a lot of these will solve a lot of the question you have. :-)

    Hope this helps!