The Coordinator-Architecture enforces the use of Single-responsibility principle using Mediator Pattern with the aim of reducing coupling.
Using Coordinator-Architecture, each component will be responsible of only one task:
- Validator : validate the request
- MapperInput : maps request to domain input
- Service : execute domain logic
- MapperOutput : maps domain output to response.
The Coordinator component will process the request step-by-step with the help of the components above.
Coordinator is based on one of the three interfaces:
CoordinatorRequestResponseAware : will process the request and will return a response.
public interface CoordinatorRequestResponseAware<REQUEST, RESPONSE> {
RESPONSE process(REQUEST request) throws FunctionalException, TechnicalException;
}
CoordinatorRequestAware : will process the request and will not return any response.
public interface CoordinatorRequestAware<REQUEST> {
void process(REQUEST request) throws FunctionalException, TechnicalException;
}
CoordinatorResponseAware : will process the service logic and will return a response.
public interface CoordinatorResponseAware<RESPONSE> {
RESPONSE process() throws FunctionalException, TechnicalException;
}
To create a Coordinator component, you have to extend one of the following abstract implementation:
CoordinatorFull : implements CoordinatorRequestResponseAware and will process the request using Validator, MapperInput, ServiceInputOutputAware, MapperOutput
@RequiredArgsConstructor
public abstract class CoordinatorFull<REQUEST, DOMAIN_INPUT, DOMAIN_OUTPUT, RESPONSE> implements CoordinatorRequestResponseAware<REQUEST, RESPONSE> {
private final Validator<REQUEST> validator;
private final MapperInput<REQUEST, DOMAIN_INPUT> mapperInput;
private final ServiceInputOutputAware<DOMAIN_INPUT, DOMAIN_OUTPUT> service;
private final MapperOutput<DOMAIN_OUTPUT, RESPONSE> mapperOutput;
@Override
public RESPONSE process(REQUEST request) throws FunctionalException, TechnicalException {
// STEP 1: validate the request
validator.validate(request);
// STEP 2: map request to domain object
DOMAIN_INPUT domainInput = mapperInput.toDomain(request);
// STEP 3: execute domain logic
DOMAIN_OUTPUT domainOutput = service.execute(domainInput);
// STEP 4: map domain object to response
RESPONSE response = mapperOutput.toResponse(domainOutput);
// STEP 5: return the response
return response;
}
}
CoordinatorServiceResponse : implements CoordinatorResponseAware and will process the service logic of ServiceOutputAware and will map the domain output to response using MapperOutput
@RequiredArgsConstructor
public abstract class CoordinatorServiceResponse<DOMAIN_OUTPUT, RESPONSE> implements CoordinatorResponseAware<RESPONSE> {
private final ServiceOutputAware<DOMAIN_OUTPUT> service;
private final MapperOutput<DOMAIN_OUTPUT, RESPONSE> mapperOutput;
@Override
public RESPONSE process() throws FunctionalException, TechnicalException {
// STEP 1: execute domain logic
DOMAIN_OUTPUT domainOutput = service.execute();
// STEP 2: map domain object to response
RESPONSE response = mapperOutput.toResponse(domainOutput);
// STEP 3: return the response
return response;
}
}
CoordinatorWithoutInputMapping : implements CoordinatorRequestResponseAware and will process the request using Validator, ServiceInputOutputAware, MapperOutput
@RequiredArgsConstructor
public abstract class CoordinatorWithoutInputMapping<REQUEST, DOMAIN_OUTPUT, RESPONSE> implements CoordinatorRequestResponseAware<REQUEST, RESPONSE> {
private final Validator<REQUEST> validator;
private final ServiceInputOutputAware<REQUEST, DOMAIN_OUTPUT> service;
private final MapperOutput<DOMAIN_OUTPUT, RESPONSE> mapperOutput;
@Override
public RESPONSE process(REQUEST request) throws FunctionalException, TechnicalException {
// STEP 1: validate the request
validator.validate(request);
// STEP 2: execute domain logic
DOMAIN_OUTPUT domainOutput = service.execute(request);
// STEP 3: map domain object to response
RESPONSE response = mapperOutput.toResponse(domainOutput);
// STEP 4: return the response
return response;
}
}
CoordinatorWithoutOutputMapping : implements CoordinatorRequestAware and will process the request using Validator, MapperInput, ServiceInputAware
@RequiredArgsConstructor
public abstract class CoordinatorWithoutOutputMapping<REQUEST, DOMAIN_INPUT> implements CoordinatorRequestAware<REQUEST> {
private final Validator<REQUEST> validator;
private final MapperInput<REQUEST, DOMAIN_INPUT> mapperInput;
private final ServiceInputAware<DOMAIN_INPUT> service;
@Override
public void process(REQUEST request) throws FunctionalException, TechnicalException {
// STEP 1: validate the request
validator.validate(request);
// STEP 2: map request to domain object
DOMAIN_INPUT domainInput = mapperInput.toDomain(request);
// STEP 3: execute domain logic
service.execute(domainInput);
}
}
CoordinatorWithoutMappings : implements CoordinatorRequestAware and will process the request using Validator, ServiceInputAware
@RequiredArgsConstructor
public abstract class CoordinatorWithoutMappings<REQUEST> implements CoordinatorRequestAware<REQUEST> {
private final Validator<REQUEST> validator;
private final ServiceInputAware<REQUEST> service;
@Override
public void process(REQUEST request) throws FunctionalException, TechnicalException {
// STEP 1: validate the request
validator.validate(request);
// STEP 2: execute domain logic
service.execute(request);
}
}
Validator will implement:
public interface Validator<REQUEST> {
void validate(REQUEST request) throws FunctionalException, TechnicalException;
}
or eventually JakartaBaseValidator/JakartaCollectionValidator that has a default implementation to validate the request using jakarta.validation.Validator
MapperInput will implement:
public interface MapperInput<REQUEST, DOMAIN> {
DOMAIN toDomain(REQUEST request) throws FunctionalException, TechnicalException;
}
MapperOutput will implement:
public interface MapperOutput<DOMAIN, RESPONSE> {
RESPONSE toResponse(DOMAIN domain);
}
Service will implement one of the following interfaces:
ServiceInputOutputAware:
public interface ServiceInputOutputAware<DOMAIN_INPUT, DOMAIN_OUTPUT> {
DOMAIN_OUTPUT execute(DOMAIN_INPUT domain) throws FunctionalException, TechnicalException;
}
ServiceInputAware:
public interface ServiceInputAware<DOMAIN_INPUT> {
void execute(DOMAIN_INPUT domain) throws FunctionalException, TechnicalException;
}
ServiceOutputAware:
public interface ServiceOutputAware<DOMAIN_OUTPUT> {
DOMAIN_OUTPUT execute() throws FunctionalException, TechnicalException;
}