I am using clean architecture and have 3 modules: application -> details -> core
I'm trying to use Mapstruct to generate mappers in application & mappers in details. I have this mapper in application module
When I do mvn package in the parent pom to build all modules then mvn spring-boot:run -pl application when I send a request & hit the mapper method it throws this error
java.lang.Error: Unresolved compilation problems:
The import com.aura.app.http.rest.v1.institutions.requests cannot be resolved
The import com.aura.app.http.rest.v1.institutions.responses cannot be resolved
AppInstitutionMapper cannot be resolved to a type
CreateInstitutionRequestDTO cannot be resolved to a type
InstitutionResponseDTO cannot be resolved to a type
The method from(Institution) of type AppInstitutionMapperImpl must override or implement a supertype method
InstitutionResponseDTO cannot be resolved to a type
InstitutionResponseDTO cannot be resolved
at com.aura.app.http.rest.v1.institutions.mappers.AppInstitutionMapperImpl.<init>(AppInstitutionMapperImpl.java:3) ~[classes/:na]
at java.base/jdk.internal.reflect.DirectConstructorHandleAccessor.newInstance(DirectConstructorHandleAccessor.java:62) ~[na:na]
at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:499) ~[na:na]
at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:483) ~[na:na]
at org.mapstruct.factory.Mappers.doGetMapper(Mappers.java:85) ~[mapstruct-1.6.3.jar:na]
at org.mapstruct.factory.Mappers.getMapper(Mappers.java:69) ~[mapstruct-1.6.3.jar:na]
at org.mapstruct.factory.Mappers.getMapper(Mappers.java:58) ~[mapstruct-1.6.3.jar:na]
at com.aura.app.http.rest.v1.institutions.mappers.AppInstitutionMapper.<clinit>(AppInstitutionMapper.java:11) ~[classes/:na]
at com.aura.app.http.rest.v1.institutions.InstitutionsController.createInstitution(InstitutionsController.java:51) ~[classes/:na]
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:565) ~[na:na]
AppInstitutionMapper.java:
package com.aura.app.http.rest.v1.institutions.mappers;
import com.aura.app.http.rest.v1.institutions.requests.CreateInstitutionRequestDTO;
import com.aura.app.http.rest.v1.institutions.responses.InstitutionResponseDTO;
import com.aura.core.institutions.entities.Institution;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
@Mapper
public interface AppInstitutionMapper {
AppInstitutionMapper INSTANCE = Mappers.getMapper(AppInstitutionMapper.class);
Institution from(CreateInstitutionRequestDTO dto);
InstitutionResponseDTO from(Institution institution);
}
This is the generated AppInstitutionMapperImpl.java:
package com.aura.app.http.rest.v1.institutions.mappers;
import com.aura.app.http.rest.v1.institutions.requests.CreateInstitutionRequestDTO;
import com.aura.app.http.rest.v1.institutions.responses.InstitutionResponseDTO;
import com.aura.core.institutions.entities.Institution;
import javax.annotation.processing.Generated;
@Generated(
value = "org.mapstruct.ap.MappingProcessor",
date = "2025-11-07T00:04:30+0800",
comments = "version: 1.6.3, compiler: javac, environment: Java 25 (Homebrew)"
)
public class AppInstitutionMapperImpl implements AppInstitutionMapper {
@Override
public Institution from(CreateInstitutionRequestDTO dto) {
if ( dto == null ) {
return null;
}
Institution.InstitutionBuilder institution = Institution.builder();
institution.name( dto.getName() );
institution.email( dto.getEmail() );
institution.uen( dto.getUen() );
institution.address( dto.getAddress() );
institution.contactNumber( dto.getContactNumber() );
return institution.build();
}
@Override
public InstitutionResponseDTO from(Institution institution) {
if ( institution == null ) {
return null;
}
InstitutionResponseDTO.InstitutionResponseDTOBuilder institutionResponseDTO = InstitutionResponseDTO.builder();
institutionResponseDTO.id( institution.id() );
institutionResponseDTO.name( institution.name() );
institutionResponseDTO.email( institution.email() );
institutionResponseDTO.uen( institution.uen() );
institutionResponseDTO.address( institution.address() );
institutionResponseDTO.contactNumber( institution.contactNumber() );
return institutionResponseDTO.build();
}
}
The CreateInstitutionRequestDTO exists in the correct package, likewise for Institution .
In my parent pom I list out my modules like this:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.aura</groupId>
<artifactId>parent</artifactId>
<version>1.0.0</version>
<packaging>pom</packaging>
<name>Aura Parent</name>
<description>Parent POM for Aura</description>
<modules>
<module>core</module>
<module>details</module>
<module>application</module>
</modules>
<properties>
<java.version>24</java.version>
<maven.compiler.source>24</maven.compiler.source>
<maven.compiler.target>24</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!-- Dependency versions -->
<spring.boot.version>3.5.6</spring.boot.version>
<lombok.version>1.18.38</lombok.version>
<mapstruct.version>1.6.3</mapstruct.version>
<lombok-mapstruct-binding.version>0.2.0</lombok-mapstruct-binding.version>
<keycloak-admin-client.version>26.0.7</keycloak-admin-client.version>
<hibernate-envers.version>6.6.5.Final</hibernate-envers.version>
<springdoc-openapi-stgarter-webmvc-ui.version>2.8.8</springdoc-openapi-stgarter-webmvc-ui.version>
<spring-boot-starter-data-jpa.version>3.4.2</spring-boot-starter-data-jpa.version>
<jakarta-validation-api.version>3.0.2</jakarta-validation-api.version>
<archunit.version>1.4.1</archunit.version>
<junit.version>4.13.2</junit.version>
<slf4j.version>2.0.17</slf4j.version>
<!-- used for liquibase profile, TODO: check if needed-->
<liquibase.version>4.27.0</liquibase.version>
<liquibase-maven-plugin.version>4.27.0</liquibase-maven-plugin.version>
<liquibase-hibernate6.version>4.23.0</liquibase-hibernate6.version>
<hibernate-validation.version>8.0.2.Final</hibernate-validation.version>
<postgresql.version>42.7.5</postgresql.version>
</properties>
<dependencyManagement>
<dependencies>
<!-- Spring Boot BOM, only imports the SB dependency versioning -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring.boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
and in the application module, I import the dependencies from core & details module.
<?xml version="1.0"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.aura</groupId>
<artifactId>parent</artifactId>
<version>1.0.0</version>
</parent>
<packaging>jar</packaging>
<artifactId>application</artifactId>
<version>1.0.0</version>
<description>aura application containing app runtime and configurations</description>
<dependencies>
<dependency>
<groupId>com.aura</groupId>
<artifactId>core</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>com.aura</groupId>
<artifactId>details</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<!-- Liquibase -->
<dependency>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-core</artifactId>
<version>${liquibase.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>${springdoc-openapi-stgarter-webmvc-ui.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-admin-client</artifactId>
<version>${keycloak-admin-client.version}</version>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${mapstruct.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-contract-shade</artifactId>
<version>4.1.0</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<profiles>
<profile>
<id>generate-db-changelog</id>
<build>
<plugins>
<plugin>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-maven-plugin</artifactId>
<version>${liquibase-maven-plugin.version}</version>
<configuration>
<propertyFile>src/main/resources/db/liquibase.properties</propertyFile>
</configuration>
<dependencies>
<dependency>
<groupId>org.liquibase.ext</groupId>
<artifactId>liquibase-hibernate6</artifactId>
<version>${liquibase-hibernate6.version}</version>
</dependency>
<!-- Add this dependency -->
<dependency>
<groupId>jakarta.validation</groupId>
<artifactId>jakarta.validation-api</artifactId>
<version>${jakarta-validation-api.version}</version>
</dependency>
<!-- Also add the implementation -->
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>${hibernate-validation.version}</version>
</dependency>
<!-- Include your PostgreSQL driver -->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>${postgresql.version}</version>
</dependency>
<!-- Add your database driver dependency here -->
</dependencies>
</plugin>
</plugins>
</build>
</profile>
</profiles>
<build>
<finalName>report-api</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<release>${java.version}</release>
<!-- processes annotations -->
<annotationProcessorPaths>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${mapstruct.version}</version>
</path>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</path>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok-mapstruct-binding</artifactId>
<version>${lombok-mapstruct-binding.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring.boot.version}</version>
<configuration>
<mainClass>com.aura.app.Application</mainClass>
<layout>JAR</layout>
<excludes>
<!-- Exclude Lombok as it's not needed at runtime -->
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
<!-- Force unpacking of module dependencies -->
<requiresUnpack>
<dependency>
<groupId>com.aura</groupId>
<artifactId>core</artifactId>
</dependency>
<dependency>
<groupId>com.aura</groupId>
<artifactId>details</artifactId>
</dependency>
</requiresUnpack>
<!-- This ensures classes are included in the JAR properly -->
<classifier>exec</classifier>
</configuration>
<executions>
<execution>
<id>repackage</id>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Since the annotations for mapper are successfully being processed in both details & application module, I do not know why when the application jar is run at runtime, the generated mapper implementation cannot find any of the other classes.
more details
when i look into the jar of application module to check if the relevant classes are there i find that the classes are all there
the details module dependency is in classpath as a jar
jar tf application/target/report-api.jar | grep /details
> BOOT-INF/lib/details-1.0.0.jar
the mapper modules seem to be there too
jar tf application/target/report-api.jar | grep /mapper
> BOOT-INF/classes/com/aura/app/http/rest/v1/institutions/mappers/
BOOT-INF/classes/com/aura/app/http/rest/v1/institutions/mappers/AppInstitutionMapper.class
BOOT-INF/classes/com/aura/app/http/rest/v1/institutions/mappers/AppInstitutionMapperImpl.class
BOOT-INF/classes/com/aura/app/http/rest/v1/institutions/mappers/UserMapper.class
the DTO class which the AppInstitutionMapperImpl could not find is also there
jar tf application/target/report-api.jar | grep /response
BOOT-INF/classes/com/aura/app/http/rest/v1/institutions/responses/
BOOT-INF/classes/com/aura/app/http/rest/v1/institutions/responses/InstitutionResponseDTO$InstitutionResponseDTOBuilder.class
BOOT-INF/classes/com/aura/app/http/rest/v1/institutions/responses/InstitutionResponseDTO.class
this is weird because the mapper class could not import the DTO class at runtime, however the DTO class is just literally in the same jar