Skip to content

Codegen

Billy Bolton edited this page Apr 17, 2024 · 42 revisions

Overview

Motivation

Building (RESTful) APIs in Java Spring Boot requires many of the same components: Controllers, Services, Mappers, Repository interfaces and/or data-access objects (DAOs), data-transfer-objects (DTO), and entity models. The business logic to implement CRUD operations for each domain is similar as well. Thus, many of these components can be abstracted with parameterized dependencies so that implementing them at the concrete level can be done with minimal lines of code. This abstraction is provided by the components submodule.

Although the components submodule greatly improves a developer’s ability to quickly implement Spring Boot components with full CRUD functionality, the developer experience requires implementing and/or extending the interfaces and abstract classes that have been parameterized. This task can be tedious and reduce developer productivity.

To improve the developer experience further by reducing the amount of code necessary to implement CRUD functionality for a domain’s Spring Boot components, the codegen project uses code generation technologies to auto implement the Controller, Service, Mapper, Repository interfaces and/or DAOs and DTOs based on a domain's Entity model. It is the Entity model that typically dictates differences in the business logic, after all.

Community Considerations

Coupling

There are concerns about generating APIs based on introspecting database schemas (ruslantalpa r/java on Reddit), which is related to how this project introspects Entity models to generate code for Spring Boot components. Indeed, this coupling is an anti-pattern to the DTO pattern and could expose sensitive attributes.

These concerns are addressed with plans to support generating DTO objects based on the Entity model in ways that can exclude sensitive properties. There will also be support for providing DTO objects to the code generator thereby giving full granular control to the developer. Please see Next-Steps for more information.

IDE Plugins: Code Generators

Development

Architecture

Abstracted Components

As discussed in the Motivation section of this document, components have been abstracted to make implementing domain-driven APIs with Spring Boot easy with minimal amounts of new code. These components are contained in the /projects/core/components/. submodule.

In the Codegen submodule of this project, we take the level of abstraction even further by automatically generating the "minimal new code" that is required to implement at the concrete level of a specific domain. The abstracted components that are referenced for this generation are under the .

Annotations

The @Harvest annotation here is what is used for the user to provide metadata that will be used to generate Spring Boot components at compile time.

@Target

@Target is a meta-annotation to scope which types of objects the annotation will be used for. The following table (source) illustrates different flavours of ElementType that defines these scopes.

Element Type Element to be Annotated
Type Class, interface or enumeration
Field Field
Method Method
Constructor Constructor
Local_variable Local variable
Annotation_type Annotation Type
Type_Parameter Type Parameter
Parameter Formal Parameter

Because this annotation will be used on a class with an @Entity annotation, we have used the ElementType.TYPE.

@Retention

The following article here describes the @Retention annotation.

@Retention is also a meta-annotation that comes with some retention policies. These retention policies determine at which point an annotation is discarded. There are three types of retention policies: SOURCE, CLASS, and RUNTIME.

  • RetentionPolicy.SOURCE: The annotations annotated using the SOURCE retention policy are discarded at runtime.

  • RetentionPolicy.CLASS: The annotations annotated using the CLASS retention policy are recorded in the .class file but are discarded during runtime. CLASS is the default retention policy in Java.

  • RetentionPolicy.RUNTIME: The annotations annotated using the RUNTIME retention policy are retained during runtime and can be accessed in our program during runtime.

Properties

Property Name Description
domainContextPath Defines the root directory that the generated components will be published in.
domainNameSingular The singular form of the domain name. This value is used to generate directories and packages, component names, etc.
domainNamePlural The plural form of the domain name. This value is used just like domainNameSingular, but is used instead of the singular form when the plural case is more appropriate grammatically. Appropriate cases that are used in code generation are when there are lists, collections, sets, etc., of a particular entity type.
parentDomainName Used when an entity has an ownership association with another entity. This optional property ensures that the generated components follow the parent-child package structure this project uses. This property may not be relevant for some use cases and can be ignored.

Models

HarvestBO

HarvestBO here is a business object (BO) model used to transmit metadata that is leveraged from the @Harvest annotation amongst the different factories we will use to generate each component layer of the Spring Boot api.

Annotation Processing

The @Harvest annotation processing adapts a pattern similar to the hub-and-spoke design where the HarvestProcessor class here (the hub) uses one-to-many factories (spokes) to generate each layer of the Spring Boot API.

The following pseudo code illustrates how the @Harvest annotation is processed:

For each element that is annotated with the @Harvest annotation

If the element is not a class type, throw an error and cease processing. Else continue.

Extract the properties from the @Harvest annotation and store as constant Strings.

The annotation processing is handled in the overridden java public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) method

1. Initializing HarvestBO

Initializing the HarvestBO must occur first so that it can be leveraged by each factory generator after. It does this

Component Generator Factories

The layers of the Spring Boot API are generated bottom up: Repository->Mappers->Services->Constants->Controllers

Usage

1. Setup

Complete the Setup guide.

2. Consume

Ingest the dev.springharvest.codegen dependency with the desired version into your preferred build tool.

Maven

<dependency>
  <groupId>dev.springharvest</groupId>
  <artifactId>codegen</artifactId>
  <version>${version}</version>
</dependency>

Gradle

compileOnly "dev.springharvest:codegen:${version}"
annotationProcessor "dev.springharvest:codegen:${version}"
testAnnotationProcessor "dev.springharvest:codegen:${version}"
compileOnly files("$buildDir/generated/sources/annotationProcessor/java/main")
annotationProcessor files("$buildDir/generated/sources/annotationProcessor/java/main")

3. Annotate

Annotate a domain's Entity model with the @Harvest annotation and set the applicable values for domainContextPath, domainNameSingular, domainNamePlural, and parentDomainName.

See Development documentation for more information.

Constraints

The Entity being annotated with @Harvest must already have the @Entity annotation.

4. Clean & Build

5. Testing

Manual Inspection

Automated Testing

Example

The books-catalogue-codegen example can be referenced to view how the annotation is used.

Please note that since this example is a nested submodule for the project repository, its build.gradle file consumes adjacent submodules in the project as opposed to consuming the dependencies from GitHub. As such, the build.gradle configuration shown in this example may need changes to be used in your own project. Please reference the Setup guide for more information.

Next Steps

This codegen project is in alpha and currently under active development to provide additional and more robust features.

As such, the following features below are not yet supported but are planned for future development.

For more information about these and other planned features, please visit the backlog under the Codegen Project linked to this GitHub repository.

Nested Annotations

DTO

  • Implement DTO 1:1 based on Entity model
  • Ability to exclude attributes in the DTO that are present in the Entity model.

Business Logic for nested entities

Criteria Specification Search

Testing

GraphQL Support

IDE Agnostic Annotation Processing

Resources

Clone this wiki locally