Oliver Drotbohm Archive About Tags

Moduliths 1.1 released

July 2nd, 2021

I’d like to announce the availability of Moduliths 1.1 GA. Moduliths is a toolkit to create modular, monolithic applications based on Spring Boot. The release ships with significant improvements and new features:

Let me walk you through the most significant additions in a bit more detail.

Transaction event publication registry

Probably the biggest feature of this release is the addition of an event publication registry for transactional events. By default, Spring’s transactional event listeners are invoked after transaction commit, which means that a listener failing to execute properly will have lost the event and there’s no way to recover from that.

The registry added in Moduliths 1.1 will write a publication log for all events to be consumed by transactional event listeners. It will write that log within the original business transaction. The execution of the transactional event listeners is then decorated to mark the publications as completed if they successfully execute. In case of a failure, the log entry will stay untouched and API is provided to access outstanding event publications for re-submission or escalation. I’ve discussed the topic in the “A Deep Dive into Spring Application Events” talk here (recording here).

To get started with the registry, all you need to do is adding the Moduliths Events Starter POM to you project:

<dependency>
  <groupId>org.moduliths</groupId>
  <artifactId>moduliths-events-starter</artifactId>
  <version>${moduliths.version}</version>
</dependency>

It currently pulls in JPA for persistence and Jackson for serialization. Published events that are consumed by a transactional event listener will need to be serializable by Jackson to make this work out of the box. Both, persistence mechanism and serialization strategy are pluggable. If you fancy to contribute different implementations (JDBC, anyone?), please make yourself heard in the bug tracker.

Improvements to the Module Canvas

Moduliths’ Module Canvas tries to provide a canonical overview of entry points into a particular module. In case a developer asks: “What are the most interesing technical parts of a module?”, the Canvas is supposed to provide the answer. It’s obviously inspired by Chris Richardson’s Microservice Canvas and the Bounded Context Canvas primarily driven by Nick Tune. Here’s one taken from Salespoint, a POS library I am involved with developed by the TU Dresden:

On the very surface, the Canvas provides information about

Grouping components

Moduliths 1.1 has added more details and smarts to the Canvas. The Spring components listed are now grouped and API was introduced to customize the groupings. If your application works with a custom stereotype, that is expressed in the code base, you can now create a custom group for it. Assume our web layer contained components named …Representations that were responsible for mapping inbound and outbound JSON representations on our domain model. We could make those show up in a dedicated group by customizing the canvas creation like this:

import static org.moduliths.docs.Documenter.CanvasOptions.*;

var representations = Grouping.of("Representations",
  "Components to map from domain types to DTOs and vice versa.",
  Grouping.nameMatching(".*Representations"));

var documenter = new Documenter(modules).writeModuleCanvases(
  CanvasOptions.defaults().groupingBy(representations));

More details about events and event listeners

The sections describing published and listened to events as been enriched to contain more detail about the listener and the publication. For the listeners, we now pull in the Javadoc on the listening method as description of the listener (not shown in the image above).

For that to work, you need to use capital.scalable:spring-auto-restdocs-json-doclet in your Javadoc generation to extract the Javadoc into a JSON file that is then transparently picked up by the documentation generation. A request to extract that functionality into a separate project has been filed here.

<configuration>
  <doclet>c.s.r.jsondoclet.ExtractDocumentationAsJsonDoclet</doclet>
  <docletArtifact>
    <groupId>capital.scalable</groupId>
    <artifactId>spring-auto-restdocs-json-doclet</artifactId>
    <version>2.0.8</version>
  </docletArtifact>
  </doclet>
</configuration>

The origin of an event publication is tracked by finding constructor and factory method invocations on the event type within the code base. If the method that call is coming from is part of the Javadoc created for the project, it gets linked to, just as all other publicly visible types.

Support for jMolecules

jMolecules is a Java library within the xMolecules family of projects that aims to provide programming language specific means to express architectural concepts in code. Moduliths 1.1 has added support for jMolecules’ @DomainEventHandler as well as the Repository interface. If used in code, methods and classes annotated with them will be recognized as event listener or repository respectfully.

Module detection API

To be as little invasive as possible, Moduliths assumes a certain package structure to detect logical modules in a particular codebase. All packages, directly located under the one that contains the Spring Boot application class, are considered module packages. While this is a decent default when structuring new code bases, ones that haven’t been started with that model in mind usually deviate from that. To be able to accommodate customization needs, Moduliths 1.1 ships a ModuleDetectionStrategy SPI, that allows users to return all module base packages given the root package of the application:

public interface ModuleDetectionStrategy {
  Stream<JavaPackage> getModuleBasePackages(JavaPackage basePackage);
}

We ship two different strategies out of the box: the default one that returns the direct subpackages as described above, and one that considers packages explicitly annotated with @Module only.

To customize the strategy to be used, register your custom implementation inside META-INF/spring.factories:

org.moduliths.model.ModuleDetectionStrategy=\
  com.acme.CustomModuleDetectionStrategy

The strategy will now be picked up, as soon as you create a Modules instance in your code.

blog comments powered by Disqus