Relationships between aggregates root

I have a tow aggregates:
A=aggregates root
B= aggregates root

A and B have a Composition relationship
how Axon do this type of relationship?

1

In DDD we model the domain using several aggregates (root + entities). One such aggregate or entity can hold a reference to another aggregate root through its id.

In axon, I see the concept of aggregates and member entities, but I do not see the notion of references to other aggregates.

Hi Zeynab,

To coordinate between aggregates, you can use sagas. We sometimes call the same component process manager, which might be less confusing.

In short, a saga can hold references to multiple aggregates. A specific event processor backs it, which will check for each event if there is a saga that should handle it or whether a new saga should be created.

1 Like

Thank you, I will do it.

Can you give me an example? please

Please give me one example for this subject

Sure, this is from an example project,

package com.example.axon.saga;

import com.example.axon.api.MarkValidationFailedCommand;
import com.example.axon.api.MarkValidationSucceededCommand;
import com.example.axon.api.ProductCreatedEvent;
import com.example.axon.api.StartValidationCommand;
import com.example.axon.api.ValidateDelayedCommand;
import com.example.axon.api.ValidateDelayedEvent;
import com.example.axon.api.ValidateTrueCommand;
import com.example.axon.api.ValidateTrueEvent;
import com.example.axon.api.ValidationEnded;
import com.example.axon.api.ValidationStarted;
import lombok.extern.slf4j.Slf4j;
import org.axonframework.commandhandling.gateway.CommandGateway;
import org.axonframework.eventhandling.scheduling.EventScheduler;
import org.axonframework.eventhandling.scheduling.ScheduleToken;
import org.axonframework.modelling.saga.EndSaga;
import org.axonframework.modelling.saga.SagaEventHandler;
import org.axonframework.modelling.saga.SagaLifecycle;
import org.axonframework.modelling.saga.StartSaga;
import org.axonframework.spring.stereotype.Saga;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.annotation.Transient;

import java.time.Duration;
import java.util.UUID;

import static java.util.Objects.isNull;

@Saga
@Slf4j
public class ValidatingSaga {

    private String productId;
    private ScheduleToken scheduleId;
    private boolean alwaysTrueValidated = false;
    private boolean delayedValidated = false;


    @Transient
    CommandGateway commandGateway;

    @Transient
    EventScheduler eventScheduler;

    @Autowired
    void setCommandGateway(CommandGateway commandGateway) {
        this.commandGateway = commandGateway;
    }

    @Autowired
    void setDeadlineManager(EventScheduler eventScheduler) {
        this.eventScheduler = eventScheduler;
    }


    @StartSaga
    @SagaEventHandler(associationProperty = "productId")
    public void handle(ProductCreatedEvent event) {
        this.productId = event.getProductId();
        commandGateway.send(StartValidationCommand.builder().productId(event.getProductId()).build());
    }

    @SagaEventHandler(associationProperty = "productId")
    public void handle(ValidationStarted event) {
        String validateTrueId = UUID.randomUUID().toString();
        String validateDelayedId = UUID.randomUUID().toString();
        SagaLifecycle.associateWith("validateTrueId", validateTrueId);
        SagaLifecycle.associateWith("validateDelayedId", validateDelayedId);
        scheduleId = eventScheduler.schedule(
                Duration.ofSeconds(5L),
                ValidateDelayedEvent.builder()
                                    .validateDelayedId(validateDelayedId)
                                    .success(false)
                                    .fromScheduler(true)
                                    .build());
        commandGateway.send(ValidateTrueCommand.builder().validateTrueId(validateTrueId).build());
        commandGateway.send(ValidateDelayedCommand.builder().validateDelayedId(validateDelayedId).build());
    }

    @SagaEventHandler(associationProperty = "validateTrueId")
    public void handle(ValidateTrueEvent event) {
        if (event.isSuccess()) {
            alwaysTrueValidated = true;
            bothCompleteCheck();
        } else {
            markFailed();
        }
    }

    @SagaEventHandler(associationProperty = "validateDelayedId")
    public void handle(ValidateDelayedEvent event) {
        log.info("validating delayed event: {} while validated: {}", event, delayedValidated);
        if (event.isFromScheduler()){
            scheduleId = null;
        }
        if (event.isSuccess()) {
            delayedValidated = true;
            bothCompleteCheck();
        } else {
            markFailed();
        }
    }

    private void bothCompleteCheck() {
        if (alwaysTrueValidated && delayedValidated) {
            commandGateway.send(MarkValidationSucceededCommand.builder().productId(productId).build());
        }
    }

    private void markFailed() {
        commandGateway.send(MarkValidationFailedCommand.builder().productId(productId).build());
    }

    @SagaEventHandler(associationProperty = "productId")
    @EndSaga
    public void handle(ValidationEnded event) {
        if (!isNull(scheduleId)) {
            eventScheduler.cancelSchedule(scheduleId);
            scheduleId = null;
            log.info("Set scheduleId to null.");
        } else {
            log.info("No deadline to cancel.");
        }
        log.info("Saga with product id: [{}] was ended.", productId);
    }

    public String getProductId() {
        return productId;
    }

    public ScheduleToken getScheduleId() {
        return scheduleId;
    }

    public boolean isAlwaysTrueValidated() {
        return alwaysTrueValidated;
    }

    public boolean isDelayedValidated() {
        return delayedValidated;
    }
}

Great example. But could he have meant something like a one-to-one, one-to-many, many-to-many relationship type of thing by “Composition” ?

Like:


// BossAggregate.java
@Aggregate
public class Boss {

	@AggregateIdentifier
	private UUID bossId;

        String name;


	private List<Employee> employees = new ArrayList<Employee>;

	@CommandHandler
	public void handle(ListEmployeesCommand command) {
        	AggregateLifecycle.apply(new ListEmployeesEvent(bossId, getEmployees()));
    }

    @EventHandler
    public void on(GetEmployeesEvent event) {
        return getEmployees();
    }

    // getter, setter
    public ArrayList<Employee> getEmployees() {
        return this.employees
    }



}

// EmployeeAggregate.java
@Aggregate
public class Employee {

	@AggregateIdentifier
	private UUID employeeId;

	private UUID bossId;

       // ...
}