v0.2.4 - stable

FractalX Framework

Decompose a Spring Boot modular monolith into a production-ready microservice platform with a single Maven command.

1
Annotate
@DecomposableModule
2
Decompose
mvn fractalx:decompose
3
Deploy
docker-compose up -d

What FractalX generates

For every annotated domain module a fully independent Spring Boot service is produced, plus four shared infrastructure services per project.

📦
Standalone Spring Boot service
Own pom.xml with filtered deps - no fat-JAR waste
🔗
NetScope inter-service communication
gRPC-based, auto-generated client interfaces, zero config
🛡
Resilience4j circuit-breakers
Retries and time limiters per dependency, out of the box
🔭
OpenTelemetry + Micrometer
Tracing, health metrics, structured log shipping
🐳
Multi-stage Dockerfile + docker-compose.yml
Three environment profiles: base / dev / docker

Design principles

PrincipleWhat it means
Annotation-minimalOne @DecomposableModule per module - the entire developer contract
Zero-intrusionNo changes to business logic, entities, or service classes
Safe-defaultUnknown dependencies always kept; unknown code always copied
IncrementalRe-running decompose replaces only changed output
ObservableEvery generated service ships with tracing, metrics and health checks on day one
Getting Started

Installation

Add FractalX to an existing Spring Boot Maven project in two steps.

System requirements

ComponentMinimumNotes
JDK17Tested on OpenJDK 17, 21. Java 24 needs -Djacoco.skip=true
Maven3.8Maven wrapper mvnw recommended
Spring Boot3.2.xSpring Boot 2.x not supported
PostgreSQL14+Or MySQL 8+, MongoDB 6+
Docker24+Optional - container deployment only

Add to pom.xml

pom.xml
<properties>
  <fractalx.version>0.2.4</fractalx.version>
</properties>

<!-- 1. Annotation library (compile-time only) -->
<dependencies>
  <dependency>
    <groupId>org.fractalx</groupId>
    <artifactId>fractalx-annotations</artifactId>
    <version>${fractalx.version}</version>
    <scope>provided</scope>
  </dependency>
</dependencies>

<!-- 2. Maven plugin -->
<build><plugins><plugin>
  <groupId>org.fractalx</groupId>
  <artifactId>fractalx-maven-plugin</artifactId>
  <version>${fractalx.version}</version>
</plugin></plugins></build>
scope=provided - annotations are compile-time only and will not appear in your production JAR.

Verify

Terminal
$ mvn fractalx:help

FractalX Maven Plugin 0.2.4
Available goals:
  fractalx:decompose    Decompose monolith into microservices
  fractalx:menu         Interactive decomposition menu
  fractalx:help         Display this help
Getting Started

Quick Start

Convert a two-module monolith into two independently deployable microservices.

1
Create module marker classes

For each bounded context, add a plain class annotated with @DecomposableModule.

OrderModule.java
@DecomposableModule(serviceName = "order-service", port = 8081)
public class OrderModule { }
PaymentModule.java
@DecomposableModule(serviceName = "payment-service", port = 8082)
public class PaymentModule {
  // Declare cross-module deps - FractalX wires via NetScope
  private OrderService orderService;
}
2
Run decomposition
Terminal
$ mvn fractalx:decompose

[INFO] FractalX 0.2.4 - starting decomposition
[INFO] Phase 1: Parsing 22 source files ...
[INFO] Phase 2: Detected 2 modules, 1 cross-module dependency
[INFO] Generating order-service   (port 8081) ...
[INFO] Generating payment-service (port 8082) ...
[INFO] Generating gateway, registry, admin, logger ...
[INFO] BUILD SUCCESS [610ms]
3
Start everything
Terminal
# Option A - Docker Compose (recommended)
$ docker-compose -f fractalx-output/docker-compose.yml up -d

# Option B - start script
$ ./fractalx-output/start-all.sh
Admin Dashboard at http://localhost:9090 - topology, health, traces and alerts auto-configured.
Getting Started

Project Structure

What mvn fractalx:decompose writes to disk.

fractalx-output/
fractalx-output/
├── order-service/
│   ├── pom.xml
│   ├── Dockerfile
│   └── src/main/
│       ├── java/com/fractalx/generated/orderservice/
│       │   ├── OrderServiceApplication.java
│       │   ├── OtelConfig.java
│       │   ├── ServiceHealthConfig.java
│       │   └── ResilienceConfig.java
│       ├── java/com/myapp/order/          ← copied business logic
│       │   ├── OrderService.java          ← @NetworkPublic added
│       │   └── OrderController.java
│       ├── java/com/myapp/order/client/
│       │   └── PaymentServiceClient.java  ← NetScope client
│       └── resources/
│           ├── application.yml
│           ├── application-dev.yml
│           ├── application-docker.yml
│           └── db/migration/V1__init.sql
├── payment-service/                       ← same structure
├── fractalx-gateway/
├── fractalx-registry/
├── admin-service/
├── logger-service/
├── docker-compose.yml
└── start-all.sh

Infrastructure services

fractalx-registry
:8761
Service registry - starts first
fractalx-gateway
:9999
API gateway with auth + rate limiting
admin-service
:9090
Full-stack management dashboard
logger-service
:9099
Centralised structured log sink

Generated file types

FileDescription
pom.xmlMaven build with filtered + pruned dependencies
DockerfileMulti-stage build: builder + slim runtime
application.ymlBase Spring Boot configuration
application-dev.ymlLocal dev overrides (localhost URLs, Swagger UI)
application-docker.ymlContainer-ready overrides with service DNS names
OtelConfig.javaOpenTelemetry OTLP exporter
ResilienceConfig.javaCircuit breaker + retry + time limiter per dependency
*ServiceClient.javaNetScope @NetScopeClient interface
V1__init.sqlFlyway initial schema scaffold from @Entity classes
docker-compose.ymlFull platform - all services + Jaeger
Core Concepts

Annotations

FractalX's developer API is four annotations. Two are generated internally - do not use them manually.

@DecomposableModuleRequired

Primary annotation. Marks a class as the boundary definition for a microservice. Place one per package subtree.

OrderModule.java
@DecomposableModule(
  serviceName = "order-service",  // kebab-case, required
  port = 8081,                    // HTTP port, required
  independentDeployment = true,   // default: false
  ownedSchemas = {"orders", "order_items"}
)
public class OrderModule {
  private PaymentService paymentService; // cross-module dep
}
AttributeTypeDefaultDescription
serviceNameString-Service directory name + spring.application.name. Required.
portint-HTTP port. gRPC auto-set to port + 10000. Required.
independentDeploymentbooleanfalseNo runtime dependency on other generated services.
ownedSchemasString[]{}Scopes Flyway migrations and JPA boundaries.
@ServiceBoundaryOptional

Explicitly marks a method as cross-service callable. Without it, FractalX infers boundaries automatically.

OrderService.java
@ServiceBoundary
public boolean isConfirmed(Long orderId) {
  return repository.existsByIdAndStatus(orderId, CONFIRMED);
}
// stays internal - not exposed to other services
public void recalculateTotal(Order order) { ... }
@AdminEnabledOptional

Includes this module in admin management endpoints, health summary, and topology map.

OrderModule.java
@DecomposableModule(serviceName = "order-service", port = 8081)
@AdminEnabled
public class OrderModule { }
@DistributedSagaAdvanced

Marks a method spanning multiple services. Generates a fractalx-saga-orchestrator at port 8099 with forward steps and compensation logic.

CheckoutService.java
@DistributedSaga(sagaId = "checkout", compensation = "cancelCheckout")
public void processCheckout(Long orderId, Long userId) {
  orderService.confirm(orderId);
  paymentService.charge(userId, orderService.getTotal(orderId));
  inventoryService.reserve(orderId);
}
public void cancelCheckout(Long orderId, Long userId) {
  paymentService.refund(userId);
  orderService.cancel(orderId);
}
Stub completion required. Generated method bodies in the saga orchestrator are stubs - fill in signatures for non-trivial overloaded methods after decomposition.
Core Concepts

Cross-Module Dependencies

FractalX detects inter-module dependencies automatically via two-phase AST analysis - no explicit declarations needed.

How detection works

Phase 1 parses every .java file into an in-memory AST map. Phase 2 maps each @DecomposableModule to its package prefix, then finds every import referencing a type from a different module's package - these are the cross-module dependencies.

Declaring dependencies

Explicit (recommended)
@DecomposableModule(serviceName = "leave-service", port = 8084)
public class LeaveModule {
  private EmployeeService employeeService;
  private DepartmentService departmentService;
}
Implicit (auto-detected from constructor injection)
import com.myapp.employee.EmployeeService;
import com.myapp.department.DepartmentService;

@Service
public class LeaveService {
  public LeaveService(LeaveRepository repo,
                      EmployeeService emp,
                      DepartmentService dept) { ... }
}

What gets generated (A → B)

LocationArtefactDescription
service-ABServiceClient.java@NetScopeClient interface - drop-in for BService
service-AResilience4j configCircuitBreaker, Retry, TimeLimiter for the B dep
service-B@NetworkPublicAdded to each called public method, exposes over gRPC
service-BgRPC portAuto-set to B.port + 10000
Bean name convention: PaymentService → strip suffix → kebab-case → payment-service
Core Concepts

NetScope

FractalX's gRPC-based inter-service layer. Replaces Feign with zero-config, strongly typed, resilience-wrapped remote calls.

📐
Port rule: gRPC = HTTP + 10,000. Example: HTTP :8081 → gRPC :18081. Applied automatically.

Generated client

PaymentServiceClient.java
@NetScopeClient(server = "payment-service", beanName = "paymentService")
public interface PaymentServiceClient {
  boolean charge(Long userId, BigDecimal amount);
  void    refund(Long userId);
}

application.yml - NetScope section

application.yml
netscope:
  server:
    enabled: true
    grpc.port: 18081   # port + 10000
  client.servers:
    payment-service:
      host: ${PAYMENT_HOST:localhost}
      port: 18082
Configuration

fractalx-config.yml

Place alongside pom.xml. Read at generation time to populate service-specific overrides.

Fallback chain: If absent, FractalX reads application.yml then application.properties. All values fall back to safe defaults.
fractalx-config.yml
fractalx:
  registry-url:  http://fractalx-registry:8761
  logger-url:    http://logger-service:9099
  otel-endpoint: http://jaeger:4317
  gateway-port:  9999
  admin-port:    9090

  gateway:
    security:
      bearer.enabled:  false
      oauth2.enabled:  false
      api-key.enabled: false
    cors:
      allowed-origins: "*"
      allowed-methods: "GET,POST,PUT,DELETE,OPTIONS"

  services:
    order-service:
      datasource:
        url:      jdbc:postgresql://localhost:5432/order_db
        username: postgres
        password: secret
Configuration

Maven Plugin

Full reference for goals, configuration, and command-line overrides.

Goals

GoalPhaseDescription
fractalx:decomposegenerate-sourcesRuns the full decomposition pipeline
fractalx:menunoneInteractive terminal menu. Falls back to numbered input on Windows.
fractalx:helpnonePrints available goals and plugin version

Plugin configuration

pom.xml
<plugin>
  <groupId>org.fractalx</groupId>
  <artifactId>fractalx-maven-plugin</artifactId>
  <version>0.2.4</version>
  <configuration>
    <sourceDirectory>${project.basedir}/src/main/java</sourceDirectory>
    <outputDirectory>${project.basedir}/fractalx-output</outputDirectory>
    <configFile>${project.basedir}/fractalx-config.yml</configFile>
    <generateDocker>true</generateDocker>
    <generateAdmin>true</generateAdmin>
  </configuration>
</plugin>

Command-line overrides

Terminal
$ mvn fractalx:decompose -DoutputDirectory=./my-services
$ mvn fractalx:decompose -DgenerateDocker=false
$ mvn fractalx:decompose -Djacoco.skip=true        # JDK 24
$ mvn fractalx:decompose -Dfractalx.verbose=true
Features

Observability

Distributed tracing, health metrics, and correlation IDs - zero developer action required.

Distributed tracing - OpenTelemetry

Each service gets an OtelConfig.java configuring the OTLP/gRPC exporter pointing at Jaeger. W3C traceparent headers propagate through all HTTP and gRPC calls automatically.

Jaeger UI at http://localhost:16686 when using the generated docker-compose.yml.

Health metrics - Micrometer

ServiceHealthConfig.java registers a TCP health check per dependency and exposes a Micrometer gauge fractalx.service.dependency.up for each one. Scraped by Prometheus and visualised on the Admin Dashboard.

Correlation IDs

HeaderDescription
X-Request-IdUnique per HTTP request (UUID)
X-Correlation-IdSpans a logical operation across multiple service calls - set once at gateway, propagated
Features

Resilience

Three Resilience4j components auto-configured per cross-module dependency in ResilienceConfig.java.

ComponentDefault behaviourConfig key
Circuit BreakerOpens after 50% failure rate over 10-call window. Half-open after 30s.resilience4j.circuitbreaker
Retry3 attempts, 500ms exponential backoff for IOException + TimeoutExceptionresilience4j.retry
Time Limiter2s timeout per call before raising TimeoutExceptionresilience4j.timelimiter
Features

Data Management

Database isolation, schema migrations, and distributed saga orchestration.

Database isolation

When ownedSchemas is set, each service gets scoped @EntityScan + @EnableJpaRepositories, datasource properties from fractalx-config.yml, and Flyway migrations at db/migration/V1__init.sql.

Cross-service FKs. FractalX generates a ReferenceValidator bean replacing FK integrity with a NetScope exists() call at write time. Review ReferenceValidatorConfig.java before production.

Saga orchestration

Methods annotated with @DistributedSaga trigger a dedicated fractalx-saga-orchestrator service at port 8099.

EndpointDescription
POST /saga/{sagaId}/startStart a saga instance - returns correlationId
GET /saga/status/{correlationId}Poll: STARTEDIN_PROGRESSDONE / FAILED
GET /sagaList all definitions and recent executions
Features

API Gateway

Spring Cloud Gateway at :9999 - dynamic routing, auth, rate limiting and observability, generated automatically.

CapabilityDescriptionDefault
Dynamic routingRoutes fetched live from registry; static YAML fallbackOn
Rate limitingToken-bucket per IP+service; no Redis requiredOn
CORSCorsWebFilter; origins/methods config-drivenOn
Circuit breakerResilience4j per route + GatewayFallbackControllerOn
Request loggingMethod, path, status, duration on every requestOn
Tracing filterInjects X-Request-Id, X-Correlation-Id, traceparentOn
JWT (HMAC)Bearer token validation with shared secretOff
OAuth2JWK Set URI (Keycloak, Auth0…)Off
API KeyX-Api-Key header or ?api_key= paramOff
Features

Admin Dashboard

Full-stack management interface at http://localhost:9090. No additional setup required.

SectionContent
OverviewService count, health summary, recent alerts
ServicesPer-service status, uptime, instance count
Network MapInteractive topology graph of all services and dependencies
gRPC / NetScopeActive connections, call counts, latency per dependency
API ExplorerLive REST client against any generated service
ObservabilityDistributed traces (Jaeger embedded), log search
AlertsRules with SSE, webhook, Slack, email channels
Config EditorView and override FractalxConfig at runtime
Data ConsistencyOutbox event queue status, saga instance tracker
Deployment

Deploying Your App

Three options: Docker Compose, start script, or run services individually.

Docker Compose

Terminal
$ docker-compose -f fractalx-output/docker-compose.yml up -d
$ docker-compose -f fractalx-output/docker-compose.yml logs -f order-service
$ docker-compose -f fractalx-output/docker-compose.yml down

Local development

Terminal
# 1. Start registry first
$ cd fractalx-output/fractalx-registry && mvn spring-boot:run

# 2. Start domain services with dev profile
$ cd fractalx-output/order-service
$ mvn spring-boot:run -Dspring.profiles.active=dev

# Or use the generated script
$ ./fractalx-output/start-all.sh

Service startup order

1
fractalx-registry:8761starts first
2
logger-service:9099
3
domain services:8081–808xany order
4
fractalx-gateway:9999
5
admin-service:9090starts last
Deployment

Port Reference

Default port assignments for all services in a FractalX platform.

ServiceHTTPgRPCDescription
fractalx-registry8761-Service registry
fractalx-gateway9999-API gateway
admin-service9090-Admin dashboard
logger-service9099-Log sink
Domain service 1808118081First domain service
Domain service 2808218082Second domain service
Domain service n808n1808nn-th domain service
Jaeger166864317Distributed trace UI + OTLP
Custom port: set port in @DecomposableModule (1–65535). gRPC is always port + 10000. No two modules may share the same port.
Reference

Troubleshooting

Fixes for the most common issues.

No modules detected
[WARN] No @DecomposableModule classes found. BUILD SUCCESS (no output)

1. Class not in scanned source root - verify <sourceDirectory> in plugin config.

2. Annotation on inner class - move to a top-level class.

3. Missing/wrong-scope dependency - run mvn dependency:list | grep fractalx.

Cross-module dependency not detected

1. Field uses interface type, not concrete class. Use BService, not IBService.

2. Import missing (IDE auto-imported different class). Check import statements.

3. Declare explicitly: private BService bService; in AModule.

JaCoCo failure on JDK 24
java.lang.IllegalArgumentException: Unsupported class file major version 68
Terminal
$ mvn fractalx:decompose -Djacoco.skip=true
Generated service fails to start - datasource not found
Failed to configure a DataSource: 'url' attribute is not specified

1. Create fractalx-config.yml with datasource URL before running decompose.

2. Set env var: export ORDER_DB_URL=jdbc:postgresql://localhost:5432/order_db

3. Use Docker Compose - it starts database containers automatically.

Flyway migration fails - relation already exists
ERROR: relation "orders" already exists
application-dev.yml
spring.flyway:
  baseline-on-migrate: true
  baseline-version: 0

Diagnostic commands

Terminal
$ mvn fractalx:decompose -Dfractalx.verbose=true
$ curl http://localhost:8761/api/services | jq .
$ curl http://localhost:9999/actuator/gateway/routes | jq .
$ curl http://localhost:9090/api/health/summary | jq .
$ curl http://localhost:9090/api/topology | jq .
Reference

FAQ

Answers to the most frequently asked questions.

Can I decompose a monolith I didn't write?

Yes. FractalX only parses source files - it doesn't compile or run them. Add @DecomposableModule to one class per bounded context and run decompose. No modification to existing code is required.

Does FractalX modify my original source files?

No. FractalX is read-only on the monolith source tree. All generated code is written to fractalx-output/ by default.

Can I run decompose multiple times?

Yes. Each run replaces the output directory. Safe to run iteratively as you refine module boundaries.

What databases are supported?

PostgreSQL, MySQL, and MongoDB datasource configuration is generated from fractalx-config.yml. H2 is supported for testing.

Is there a Spring Boot 2.x version?

No. FractalX targets Spring Boot 3.2.x and Jakarta EE namespaces. Spring Boot 2.x uses javax.* namespaces which are incompatible with the generated code.

What happens to shared utility classes?

Classes imported by multiple modules are copied into each service. Extract shared utilities to a common library before decomposition if intentional sharing is needed.

How do I add custom non-functional requirements?

Implement the ServiceFileGenerator interface and register it in ServiceGenerator.buildPipeline(). Each step receives a GenerationContext with module descriptor, output path, and platform config.

On this page