Oliver Drotbohm Archive About Tags

Architecturally evident code with jMolecules

February 22nd, 2021

Expressing architectural concepts in code often stops at naming conventions and technologies implicitly describing these concepts. jMolecules provides annotations and types to describe architectural context and technology specific integration to derive concept specific default mappings and boilerplate code to minimize the gap between architectural idea and the code written to implement those.

Note: This is a follow up blog post to “Implementing DDD Building Blocks in Java” as the libraries discussed in that have evolved significantly. While I thought about updating the original article I eventually decided to write a completely new one as the extend of the changes is too great.

The architecture code gap

The general journey from architectural idea usually follows the following pattern: the team has a rough idea of the fundamental logical structure of the code they want to write. That structure can be driven by patterns solely established within the team but is usually influenced by widely adapted architectural approaches like Ports and Adapters or Domain-Driven Design building blocks like entities, aggregates, repositories and services. The teams agree on a strategy to actually implement these concepts. The means to achieve that commonly range from naming conventions for individual types or packages and package structures. Then, the final step towards running code is mapping the logical and technical constraints implied by the concept to the implementation technologies.

Let us take a look at a very simple example. Assume a simple Customer aggregate that is responsible to implement business constraints. For example, we should be allowed to assign multiple addresses to the customer, but at least a single Address instance needs to be assigned to it. This is admittedly simplistic but the complexity of the constraints does not actually matter to the point I am trying to make. Let us further assume, we would want to map the model code to the database directly using JPA and Spring Data repositories. I know this is a topic worth debating on its own, but let us stick with that for now. Even in that very reduced example, the actual implementation has significant impact on the code to be written:

There might be well justified exceptions to those mappings but fundamentally, there are rules that can and have to be applied to express the logical concept. Zooming out a bit, there are a couple of things going on here at the same time. First, the logical concept of an aggregate is not explicit. It is scattered amongst different technologies that kind of help expressing the concept but suffer a fundamental problem themselves, which is – second – they are not designed with the architectural abstraction in mind in the first place. Arguably, Spring Data is built around the concept of a DDD repository. Also, Spring provides stereotype annotations like @Service and @Repository to assign roles to container managed components. However, they are driven by the needs of the particular technology’s requirements and functionality like applying transactions and persistence exception translation in the examples given.

What if we could approach this differently? What if we could express the well established architectural and design concepts explicitly and the technology mappings or at least more reasonable defaults for them would be derived from those?

jMolecules

Let us take it one step at a time. jMolecules is a project that attempts to tackle the first step: expressing architectural concepts in code directly, either through annotations or interfaces. It has evolved from the jDDD library discussed in this earlier blog post but found a new home in the xMolecules project that in turn attempts to provide similar architectural abstractions for different programming ecosystems.

So we would start by using the Domain-Driven Design abstractions provided by jMolecules in our domain model:

import org.jmolecules.ddd.types.AggregateRoot;
import org.jmolecules.ddd.types.Entity;
import org.jmolecules.ddd.types.Identifier;

class Customer implements AggregateRoot<Customer, CustomerId> {

  private final CustomerId id;
  private List<Address> addresses;

  /* Other properties and business methods omitted. */

  @Value(staticConstructor = "of")
  public static class CustomerId implements Identifier {
    private final UUID id;
  }
}

class Address implements Entity<Customer, AddressId> {

  /* Other properties and business methods omitted. */

  @Value(staticConstructor = "of")
  static class AddressId implements Identifier {
    private final UUID id;
  }
}

We’re able to express Domain-Driven Design concepts here:

This looks very neat so far. Except the slightly verbose identifier wrapper types, we are writing only the minimal amount of code necessary and the higher level concepts are both explicitly visible to the developers and also extractable for tools that might want to derive documentation. However, if this code was supposed to be persisted using for example JPA, we would now have to go ahead and add quite a bit of boilerplate annotations and implementation code:

jMolecules technology integrations

Besides the core artifacts containing abstractions to express architectural patterns, jMolecules provides a set of technology integration libraries. At its heart, there is a ByteBuddy plugin that will translate jMolecules annotations and interfaces into the technology specific annotations and declarations to make sure they work out of the box within in a particular technology context. The complete example can be found here.

<build>
  <plugins>
    <plugin>
      <groupId>net.bytebuddy</groupId>
      <artifactId>byte-buddy-maven-plugin</artifactId>
      <version>1.10.21</version>
      <executions>
        <execution>
          <goals>
            <goal>transform</goal>
          </goals>
        </execution>
      </executions>
      <dependencies>
        <dependency>
          <groupId>org.jmolecules.integrations</groupId>
          <artifactId>jmolecules-bytebuddy</artifactId>
          <version>0.1.0</version>
        </dependency>
      </dependencies>
    </plugin>
  </plugins>
</build>

The jmolecules-bytebuddy plugin will – depending on the classpath arrangement of the project being built – inspect the code to be compiled for architectural concepts expressed via jMolecules annotations and types. It will then add the technology specific annotations that are required to implement the concept and add it to the code compiled.

With that plugin integrated into the build, the code Customer aggregate code shown above is indeed a fully functional JPA entity. All default constructors, additional interfaces and default mappings as described above will be generated. Also, if the nullability annotations arrangement of the type describes fields as non-nullable, a generated @PrePersist/@PostLoad method will verify the instance to be persisted or just loaded to be in valid state, in other words, that no non-nullable fields are actually null.

Spring integration

Besides the integration for JPA, the ByteBuddy plugin also translates jMolecules @Service, @Repository etc. annotations into their Spring counterparts so that application components can stay free of Spring annotations for the sole purpose of component definition but still work out of the box with it.

Also, jMolecules’ @DomainEventHandler annotation is translate into Spring’s @EventListener to implement annotation driven event listeners.

Summary

jMolecules allows to express architectural concepts like Domain-Driven Design building blocks as annotations or types within Java code. Its integration library projects those concepts into particular technologies like JPA and Spring.

blog comments powered by Disqus