SimpleBus: Una implementació de Command Bus per a Spring Boot

•

Darrerament, he estat treballant en diversos projectes amb Spring Boot, i m’he trobat buscant formes de traslladar alguns dels conceptes que utilitzo habitualment amb Symfony al món de Java i Spring Boot. Un dels patrons que sempre intento aplicar és el CQRS (Command Query Responsibility Segregation), especialment l’ús d’un Command Bus per gestionar les comandes.

Amb això en ment, vaig decidir investigar com seria implementar un Command Bus en Java, mantenint com a objectiu principal que la llibreria fos molt fàcil d’utilitzar i d’integrar amb Spring.

Objectius de la implementació

Aquesta llibreria, que he anomenat SimpleBus, té dos objectius principals:

  1. Senzillesa d’ús: Fer que les interfícies siguin intuïtives per als desenvolupadors.
  2. Integració amb Spring Boot: Que la integració amb el framework sigui simple i eficient, aprofitant els avantatges de la injecció de dependències i la configuració automàtica que ofereix Spring.

A més, vaig voler publicar la llibreria al repository Maven per facilitar-ne la instal·lació i també per entendre el procés de publicació de llibreries al món Java, que té algunes diferències respecte a altres ecosistemes.

Com funciona la llibreria

La llibreria SimpleBus implementa un Command Bus i un Query Bus, facilitant l’enviament i processament de comandes i consultes en aplicacions Spring Boot.

Integració amb Spring Boot

L’ús de SimpleBus amb Spring Boot és molt senzill gràcies a l’autoconfiguració que s’inclou en la llibreria. Només cal afegir la dependència al projecte, i el framework s’encarrega de configurar automàticament tots els components necessaris.

Java
@Controller
public class EditTagController {

    private final SimpleCommandBus simpleCommandBus;
    private final SimpleQueryBus simpleQueryBus;

    public EditTagController(
            SimpleCommandBus simpleCommandBus,
            SimpleQueryBus simpleQueryBus
    ) {
        this.simpleCommandBus = simpleCommandBus;
        this.simpleQueryBus = simpleQueryBus;
    }

    @PreAuthorize("hasRole('ROLE_ADMIN')")
    @GetMapping(value = "/edit/tag/{tagId}")
    public String editTagForm(@PathVariable("tagId") String tagId, Model model)
            throws Exception {
        TagDTO tag = (TagDTO) this.simpleQueryBus.handle(new TagDetailCommand(tagId));
        model.addAttribute("editTagForm", tag);
        return "tag/edit";
    }

    @PreAuthorize("hasRole('ROLE_ADMIN')")
    @PostMapping("/edit/tag/{tagId}")
    public String editTagFormSubmit(
            @PathVariable("tagId") String tagId,
            @ModelAttribute TagDTO editTagForm,
            Model model)
            throws Exception {
        model.addAttribute("editTagForm", editTagForm);
        EditTagCommand command =
                new EditTagCommand(
                        tagId,
                        editTagForm.getName());
        this.simpleCommandBus.handle(command);
        return "redirect:/view/tag/" + tagId;
    }
}

Comandes i Queries

Per implementar una comanda, simplement es defineix una classe que hereti de la interfície Command, i després es crea un CommandHandler per gestionar la seva execució.

Java
public class MyCommand implements Command {
    // Dto de comanda amb tota la informació per a poder ser procesada
}

@Service
public class MyCommandHandler implements CommandHandler<MyCommand> {
    @Override
    public void execute(MyCommand command) {
        // Implementació del processament de la comanda
    }
}

També es poden utilitzar consultes (queries) d’una manera similar, facilitant la implementació del patró CQRS complet. En aquest cas s’haurien d’utilitzar les interfícies Query i QyeryHandler.

Middleware

Un dels punts forts de la llibreria és la capacitat d’afegir middlewares al flux de processament. Per exemple, el LoggingMiddleware és un middleware que registra cada comanda i consulta rebuda al sistema:

Java
@Component
public class LoggingMiddleware<M extends Message, R> implements Middleware<M, R> {
    private static final Logger logger = LoggerFactory.getLogger(LoggingMiddleware.class);

    @Override
    public R execute(M message, Function<M, R> next) {
        logger.info(String.format("Received %s", message.getClass().getName()));
        R result = next.apply(message);
        logger.info("Handled successfully");
        return result;
    }
}

Amb aquesta estructura, pots afegir fàcilment altres middlewares per gestionar altres aspectes del flux, com ara la validació, la seguretat o la gestió de transaccions.

Com configurar els middlewares

Una de les característiques clau de SimpleBus és la capacitat de configurar middlewares, que permeten afegir comportaments addicionals a les comandes i consultes. Els middlewares actuen com una capa intermèdia que es processa abans o després d’una comanda o consulta, sense necessitat de modificar la lògica del Command Handler o Query Handler.

Per configurar els middlewares, simplement es crea una classe de configuració on es defineixen quins middlewares s’utilitzaran. Per exemple, si vols utilitzar un middleware de registre (logging), la configuració es veuria així:

Java
@Configuration
public class MiddlewareConfig {

    @Bean
    public <M extends Message, R> List<Middleware<M, R>> middlewares(LoggingMiddleware<M, R> loggingMiddleware) {
        return Collections.singletonList(loggingMiddleware);
    }
}

Aquest codi indica que, quan el Command Bus o Query Bus s’executin, el middleware de registre LoggingMiddleware serà el que gestionarà les comandes o consultes abans de passar-les al Message Handler corresponent.

Importació

El projecte està pujat al repositori central de Maven, es pot trobar a la següent direcció: https://central.sonatype.com/artifact/cat.bernado/simplebus

Així que per a importar-lo a un projecte és molt senzill. Si el teu projecte està basat en Gradle simplement afegeix la següent línia:

implementation 'cat.bernado:simplebus:0.0.1'

Si, en canvi, encara utilitzes Maven hauries d’importar el projecte així:

XML
<dependency>
    <groupId>cat.bernado</groupId>
    <artifactId>simplebus</artifactId>
    <version>0.0.1</version>
</dependency>

Conclusió

SimpleBus és una llibreria pensada per a desenvolupar aplicacions amb el patró CQRS en un entorn Spring Boot. La seva implementació busca ser simple però potent, amb un fort enfocament en la modularitat i la facilitat d’ús. A més, la publicació a Maven Central facilita la seva distribució i integració.

Podeu trobar la llibreria al meu repositori de GitHub i afegir-la fàcilment als vostres projectes a través de Maven.