Develop with the microservice SDK
Product microservices use a backend-lib + server architecture. backend-lib contains MVC logic and is published to Nexus. server contains the Spring Boot entry point, container adaptation, and delivery configuration. Custom modules depend on the matching library and add controllers, services, components, entities, and repositories.
Use this approach for extensions within the product process that reuse transactions and permission context. For cross-system queries and approval orchestration, prefer REST and a separate adapter.
1. Prerequisites
- SDK authorization and the matching library, source/interface guidance, and product release.
- JDK 17, Maven 3.8 or later, and enterprise Nexus configuration.
- Accessible configuration and service registries, with addresses, namespaces, and credentials injected at runtime.
- A separate Git branch recording product, SDK, and migration revisions.
Keep passwords and personal cookies out of POM files, scripts, and logs.
2. Split the project
datablau-<service>-backend-lib/ # Product Maven library
datablau-<service>-server/ # Extension startup module
|-- src/main/java/.../Application.java
|-- src/main/resources/
`-- pom.xml- Rename the original backend module to
datablau-<service>-backend, with artifactdatablau-<service>-backend-libmatching the product release. - Create the server module, copy resources, and move
Applicationinto it. - Add extension scan paths in
SpringApplicationandJpaConfiguration, reserving anextpackage. - Depend on backend-lib and container starters rather than copying business dependencies.
Minimal library Maven structure:
<artifactId>datablau-metadata-backend-lib</artifactId>
<packaging>jar</packaging>
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.csv</include>
<include>**/*.xml</include>
<include>**/*.properties</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes><include>**/*.csv</include></includes>
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
</plugins>
</build>Server dependencies and packaging:
<dependencies>
<dependency>
<groupId>com.datablau</groupId>
<artifactId>datablau-metadata-backend-lib</artifactId>
<version>${datablau.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration><includeSystemScope>true</includeSystemScope></configuration>
<executions><execution><goals><goal>repackage</goal></goals></execution></executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
</plugin>
</plugins>
</build>Keep container differences in server profiles. Use Tomcat by default and change starter profiles for BES or TongWeb without changing business logic.
3. Override Beans
Product implementations and extension methods must permit inheritance. Product Beans require explicit names and a missing-extension condition:
@Service("userService")
@ConditionalOnMissingBean(name = "userServiceExt")
public class UserServiceImpl implements UserService {
// Supported extension methods use protected or public visibility.
}Use an XXXExt name for the extension:
@Service("userServiceExt")
public class UserServiceExtImpl extends UserServiceImpl {
@Override
public UserDto findById(Long id) {
UserDto user = super.findById(id);
// Add enterprise-specific composition or validation.
return user;
}
}Private methods, internal classes, and non-overridable internals are not supported extension points. Keep Bean names stable to avoid duplicate implementations. Prefer new xxxExt methods and DTOs when changing responses; returning subclasses on existing interfaces can break remote deserialization. Calling super suits lightweight enrichment. Changes to SQL, pagination, or transactions require a complete override and contract tests.
4. Persist extension properties
Keep product tables and Repositories unchanged. Add a separate table linked by the product business key, such as a user hireDate:
@Entity
@Table(name = "db_users_ext")
public class UserExt {
@Id
private Long userId;
@Column(name = "hire_date")
@Temporal(TemporalType.TIMESTAMP)
private Date hireDate;
}
@Repository("userExtRepository")
public interface UserExtRepository extends CrudRepository<UserExt, Long> {
@Query("select ue from UserExt ue where ue.userId in :userIds")
List<UserExt> findByUserIds(@Param("userIds") Collection<Long> userIds);
}Include extension Entity and Repository packages in JpaConfiguration, such as ...ext.jpa.entity and ...ext.jpa.repository. For unmanaged entities or missing repositories, check scans, primary keys, profiles, and migrations.
Expose properties in a separate DTO:
public class UserExtDto extends UserDto {
private Date hireDate;
public UserExtDto(UserDto source) {
BeanUtils.copyProperties(source, this);
}
// getter / setter
}For paginated lists, retrieve the product page, bulk-read extension rows by business key, and merge. Avoid one query per row. Preserve original remote DTO compatibility through a new endpoint where needed.
5. Add a cross-service interface
- Create
datablau-servicea-<project>-apiwith shared interfaces and DTOs only. - Publish to Nexus and use the same version in provider and consumer.
- Extend
HttpExporterConfigurationin Service A and registerHttpInvokerServiceExporter. - Configure
RemoteServiceProxyFactoryBeanin Service B with actual service name, interface, URL, and local empty implementation. - Inject the interface and design timeout, retries, circuit breaking, and permission logging.
@Configuration("httpExporterConfigurationExt")
public class HttpExporterConfigurationExt extends HttpExporterConfiguration {
@Bean(name = "/ExampleInterface")
public HttpInvokerServiceExporter exampleServiceExporter(ExampleInterface service) {
HttpInvokerServiceExporter exporter = new HttpInvokerServiceExporter();
exporter.setService(service);
exporter.setServiceInterface(ExampleInterface.class);
return exporter;
}
}Use actual context, ports, and discovery names. Keep shared DTOs stable and do not expose internal Entities as remote contracts.
6. Build, start, and debug
# Install parent dependency and version management.
mvn -f backend/datablau-parent/pom.xml install
# Build all backend modules.
mvn -f backend/pom.xml install
# Build a single extended service.
mvn -pl datablau-metadata-server -am packageConfigure JDK, registry, namespace, application home, and logs. Inject configuration credentials through environment variables:
export JAVA_HOME=/path/to/jdk-17
export METADATA_HOME=/opt/datablau_metadata
java -Xms2g -Xmx8g \
-Dspring.cloud.nacos.username="$NACOS_USERNAME" \
-Dspring.cloud.nacos.password="$NACOS_PASSWORD" \
-Dspring.cloud.nacos.discovery.server-addr="$NACOS_ADDR" \
-Dspring.cloud.nacos.discovery.namespace="$NACOS_NAMESPACE" \
-Dlogging.file.path="$METADATA_HOME/logs" \
-jar "$METADATA_HOME/app/datablau-metadata-server.jar"For local testing, start against a test configuration center, take other instances of the same test microservice offline, log into DAM, and test the extension with a current session through Postman or automation. Inspect Beans, persistence, audit, and responses. Restore the other test instances afterward.
7. Release and upgrade
Organize backend, frontend, and service modules separately. Record library and extension versions, configuration differences, and migrations. Replace the library and compile, check Bean creation, regress original and extended APIs and remote permissions, migrate, roll out gradually, and retain rollback packages.
Check signatures, DTOs, Bean names, JPA scans, remote URLs, Parser SPI, and reporting fields. Maintain repeatable acceptance samples for every release.
