Project conventions, architecture, and coding patterns for the StreamPark codebase.
StreamPark is a Maven multi-module project with five top-level reactor modules. Each has a clear responsibility boundary.
-
streampark-common(streampark-common/): Engine-API-free foundation layer. It owns the immutable configuration model (ConfigOption,Configuration,ConfigurationLoader, andGlobalConfiguration), shared option catalogs, workspace layout, utilities, file system abstractions, and common enums. It may understand external formats such as Flink YAML, but must not depend on Flink or Spark runtime APIs. -
streampark-scala(streampark-scala/): Small Scala compatibility layer containing shared Scala logging and implicit utilities. Java-only common code belongs instreampark-common; reusable Scala code belongs here instead of being embedded in an engine module. -
streampark-flink(streampark-flink/): Flink runtime integration. Its reactor contains the version-isolated shims, SQL client, submission client, packer, and Kubernetes integration. Connector modules are enabled by the module'sshadedprofile.FlinkShimsProxyin the client API is the entry point for executing version-specific code. -
streampark-spark(streampark-spark/): Spark runtime integration. It contains Spark configuration and SQL utilities, submission client API/core, and SQL client. It is a regular root reactor module; the retainedsparkprofile is not required to include it in a normal build. -
streampark-console(streampark-console/): Web management platform. Its Maven reactor contains the Spring Boot service. The siblingstreampark-console-webapp/Vue 3 application is built into the service only when the service'swebappprofile is enabled; it is not a Maven child module.
The strongest compatibility contracts are the common configuration keys and value semantics, Flink client request/response types, shims proxy serialization boundary, database schema, and REST API. Changes to these surfaces require cross-module impact analysis and focused compatibility tests.
-
Configuration model (
org.apache.streampark.common.configuration):ConfigOptiondeclares typed metadata,ConfigurationParserparses raw documents,ConfigurationLoadercomposes ordered sources, and immutableConfigurationsnapshots perform typed reads.GlobalConfigurationis the process-boundary atomic reference, not a mutable property bag. Option keys and fallback keys are user-facing contracts. -
Configuration ownership: Common option catalogs contain only settings owned by common infrastructure. Console and engine settings stay in their owning modules.
SpringConfigurationInitializeris the Console composition root and owns the explicit list of options bound from Spring; do not add globalALLregistries to option catalogs. -
Workspace initialization:
Workspace.LOCAL,Workspace.REMOTE, and derived constants are initialized from one immutable snapshot. Spring bootstrap must publish its completed configuration before workspace paths are first accessed, and must retain theWorkspace.verifyInitializedFromguard. -
Flink YAML compatibility (
FlinkConfigurationLoader): Before Flink 1.19, load onlyflink-conf.yamlwith the legacy line parser. Flink 1.19 and 1.20 support both names and preferflink-conf.yamlwhen both exist; parser selection follows the selected filename. Flink 2.0 and later load onlyconfig.yamlwith standard nested YAML parsing. Directory-based loading must always receive the target Flink version. This common utility returns engine-neutral maps; conversion to Flink'sConfigurationbelongs in a Flink module. -
FlinkShimsProxy: The multi-version classloader isolation mechanism usesChildFirstClassLoaderand serializes request/response objects across the boundary. Classloaders are cached by concrete Flink version. Never pass target-runtime Flink objects into the parent classloader or introduce target-specific static state that can leak across classloaders. -
Shims modules:
streampark-flink-shims-basecontains only contracts and implementation shared by every supported shim. Supported concrete modules are Flink 1.18, 1.19, 1.20, 2.0, 2.1, 2.2, and 2.3. APIs removed from Flink 2.x, such as legacyregisterDataStreamand table-functionregisterFunctionoverloads, belong only in the compatible 1.x implementations. Do not add classes or methods anywhere understreampark-flink-shimswithout an explicit shims architecture decision. -
Flink submission flow:
FlinkClientand its request/response packages form the stable API.FlinkClientEntrypoint,SubmitRequestResolver,FlinkConfigurationBuilder, and deployment-specific clients own request normalization, configuration assembly, and submission. Keep Flink-version calls behind the shims boundary. -
FlinkApplicationController/FlinkApplicationManageService/FlinkApplicationActionService: The core application management flow. Operations (start, stop, cancel, deploy) must be idempotent and handle all Flink states correctly. TheAppChangeEventannotation triggers state synchronization. -
Persistence entities (
console/core/entity): These classes map database rows. Keep configuration discovery, parsing, filesystem access, and other business logic in assemblers, services, or utility classes such asFlinkEnvUtilsandFlinkApplicationConfigUtils. Existing small mapping operations may remain, but new domain workflows must not be added to entities. -
SQL parsing and validation: Flink SQL validation is version-aware and executes through the selected 1.18-2.3 shim.
FlinkSql,FlinkSqlService, and the SQL client have different persistence, orchestration, and execution responsibilities.sql-rev.dicthandles database SQL differences between MySQL and PostgreSQL; it is unrelated to Flink SQL syntax compatibility. -
Kubernetes integration (
FlinkKubernetesWatchController): Uses Caffeine caches (TrackIdCache,JobStatusCache,MetricCache) for tracking K8s-deployed Flink jobs. Cache invalidation and TTL must be correct to avoid stale state. -
HTTP client and proxying:
OkHttpUtilsin common owns the shared connection pool, timeouts, and bounded retries for idempotent requests. Console proxy code inWebUtilsowns servlet adaptation, hop-by-hop header filtering, response streaming, and response closure. Do not create ad hoc clients per request or retry non-idempotent methods implicitly. -
Database schema changes: All schema changes must have corresponding upgrade scripts under
streampark-console/streampark-console-service/src/main/assembly/script/upgrade/for bothmysql/andpgsql/. Updatesql-rev.dictwhen a mapper or initialization statement needs a MySQL-to-PostgreSQL rewrite. -
Authentication & Authorization:
ShiroConfig,JWTUtil,ShiroRealm— changes here affect all user access. The@Permissionannotation andPermissionAspectenforce team-level resource isolation. Never weaken RBAC checks.
-
Immutable configuration pipeline: Declare typed options in the owning module, parse external input without a central registry, compose sources by precedence, and capture one immutable snapshot at the start of a multi-step operation. Do not repeatedly read mutable global state inside a workflow.
-
Shims / Proxy pattern:
FlinkShimsProxy.proxy(flinkVersion, function)loads the target installation and matching shim behind a child-first classloader. Shared behavior stays in shims-base; version-only API calls stay in the concrete version module. Cross-boundary values must use stable serializable StreamPark request/response types. -
Initializer pattern:
FlinkStreamInitializerandFlinkTableInitializerassemble application configuration namespaces and construct Flink environments inside the target classloader. Nativeflink.property.*, userapp.*, and command-line sources remain distinct until their documented composition point. -
Service layer separation: Console services are split by responsibility —
FlinkApplicationManageService(CRUD),FlinkApplicationActionService(start/stop/cancel),FlinkApplicationInfoService(query/info). Follow this pattern when adding new application operations. -
Scala compatibility layer: Shared Scala helpers live in
streampark-scala. New implicits must be narrowly scoped and must not introduce engine dependencies into the common foundation. -
MyBatis-Plus entity pattern: Mappers extend MyBatis-Plus
BaseMapper; entities use@TableNameand related mapping annotations. Only entities that need the shared audit fields extendBaseEntity. Pagination usesMybatisPagerandPaginationInterceptor. Keep entities focused on persistence data. -
Typed request and option APIs: Prefer typed enums, request objects,
ConfigOption, and immutable maps over unstructured string bags. Preserve serialized field names across the Console-to-client and client-to-shims boundaries. -
REST response pattern: Normal API endpoints return
RestResponseBody<T>created throughRestResponseBody.success(...)orRestResponseBody.fail(...). The map-basedRestResponseis a deprecated compatibility type and must not be introduced at new controller boundaries. Streaming proxy endpoints may write directly toHttpServletResponse. Use@Permissionfor protected resources and@AppChangeEventfor audited application changes. -
File system abstraction: Use
FsOperator(withHdfsOperator/LfsOperatorimplementations) for file operations. Never use rawjava.io.Fileor HadoopFileSystemdirectly in business logic.
- Formatting: Eclipse formatter via Spotless (
tools/checkstyle/spotless_streampark_formatter.xml). Run./mvnw spotless:applybefore committing. - Import order:
org.apache.streampark,org.apache.streampark.shaded,org.apache,javax,java,scala,\#(all others). - Static checks: Checkstyle (
tools/checkstyle/checkstyle.xml) + Spotless. No wildcard imports. No@authortags. No JUnit 4 imports. - Lombok: Use
@Slf4j,@Data,@Builderwhere appropriate. Do not use@EqualsAndHashCodeon JPA/Hibernate entities. - Testing: JUnit 5 (
org.junit.jupiter) + AssertJ. Use@Test(not@Testfrom JUnit 4). UseassertThat(...).isEqualTo(...)style. Test classes should be in the same package as the code under test. - Method names: New production and test method names must be concise, readable, and no longer than 40 characters.
- Package structure: Controllers in
controller/, service interfaces inservice/, implementations inservice/impl/, entities inentity/, mappers inmapper/, enums inenums/.
- Formatting: Scalafmt 3.7.5 (
tools/checkstyle/.scalafmt.conf). Max column 160. Run./mvnw spotless:applyto format. - Import ordering:
org.apache.streampark.*first, then other third-party, thenjavax.*,java.*,scala.*. - Static checks: Scalastyle (
tools/checkstyle/scalastyle-config.xml). No wildcard imports. Noprintlnstatements (useLoggertrait). - Testing: ScalaTest 3.2.9. Use
FlatSpecorFunSuitestyle consistent with existing tests. - Style: Use
valovervar. PreferOptionovernullin public APIs. Uselazy valfor expensive initialization. Use pattern matching instead ofisInstanceOf/asInstanceOf.
- Formatting: ESLint + Prettier. Run
pnpm lint:eslintandpnpm lint:prettier. - Vue 3 Composition API: Use
<script setup lang="ts">for new components. Use Pinia for state management (not Vuex). - API layer: All HTTP calls go through
src/api/modules usingdefHttp. API URLs are defined as enum constants. Never useaxiosorfetchdirectly in components. - Component naming: PascalCase for component files and names. Use
index.tsbarrel exports in component directories. - Unused variables: Prefix with
_to suppress ESLint warnings (argsIgnorePattern: '^_').
- Apache License header: Required on all new files. Use the copyright header from
tools/checkstyle/copyright.txt. Enforced by Spotless and Apache RAT. - No wildcard imports: Prohibited in both Java and Scala (enforced by Spotless).
- No personal pronouns in comments: Use descriptive, impersonal documentation.
- Database: Support both MySQL and PostgreSQL. All new SQL must be tested against both. Use
sql-rev.dictfor dialect differences. - Logging: Use Lombok
@Slf4j(Java) orLoggertrait (Scala). Uselog.info/log.errorwith parameterized messages. Never log credentials or sensitive data.
-
Full build (backend + frontend, skip tests):
./build.sh
Equivalent to:
./mvnw -Pshaded,webapp,dist -DskipTests clean install -
Fast build (backend only, skip all checks):
./mvnw -Pfast clean install -DskipTests
-
Build backend reactor with shaded artifacts:
./mvnw -Pshaded clean install -DskipTests
-
Build backend only:
./mvnw clean install -DskipTests
-
Run single test class (Java):
./mvnw test -pl streampark-console/streampark-console-service -Dtest=FlinkSavepointServiceTest -
Run common configuration tests:
./mvnw test -pl streampark-common -Dtest=ConfigurationTest,FlinkConfigurationUtilsTest -
Build all supported Flink shims:
./mvnw -f streampark-flink/streampark-flink-shims/pom.xml clean install
-
Format code (Java + Scala):
./mvnw spotless:apply
-
Check formatting:
./mvnw spotless:check
-
Run Checkstyle:
./mvnw checkstyle:check
-
Frontend development server:
cd streampark-console/streampark-console-webapp && pnpm dev
-
Frontend lint:
cd streampark-console/streampark-console-webapp && pnpm lint:eslint && pnpm lint:prettier
-
Frontend build:
cd streampark-console/streampark-console-webapp && pnpm build
-
Run Docker Compose (local):
docker compose -f docker/docker-compose.yaml up -d
- PR title format:
[Module] Description(e.g.,[Flink] Fix shims classloader isolation,[Console] Add team-level resource filtering,[Common] Support Hadoop 3.x configuration). - Module prefixes:
[Common],[Flink],[Spark],[Console],[K8s],[Docs],[CI],[Build]. - One concern per PR: Unrelated whitespace, import, or formatting changes go in separate PRs. Do not mix refactoring with feature work.
- Commit messages: Describe the what and why, not implementation details. Reference related GitHub issues with
#xxx. - Apache License header: Required on all new files (enforced by Spotless and Apache RAT). The
spotless:checkandapache-rat:checkgoals run in CI. - Schema changes: Must include matching scripts for MySQL and PostgreSQL under
streampark-console/streampark-console-service/src/main/assembly/script/upgrade/. - Shims changes: Keep shared methods in shims-base only when every supported version can implement them. Put version-specific Flink APIs only in the applicable 1.18-2.3 concrete modules, and verify the full shims reactor.
- Never modify
.asf.yaml,LICENSE,NOTICE, or.gitignorewithout explicit discussion. - Never upgrade Flink, Spark, Scala, Spring Boot, or other major dependency versions without discussion — these changes have broad impact across the entire project.
- Never remove or rename canonical or fallback configuration keys without an explicit compatibility decision; they are user-facing contracts.
- Never add classes or methods under
streampark-flink-shimswithout an explicit architecture decision. General utilities, Console logic, and configuration-file parsing belong outside shims. - Never add business workflows or infrastructure access to
streampark-console-service/.../core/entity. - Never commit secrets, credentials, API keys, or cloud-specific tokens.
- Never introduce a new Flink version shims module without adding the corresponding CI build configuration.
- Never add a new database migration without providing both MySQL and PostgreSQL upgrade scripts.
- Never change the
SqlConvertUtilsdialect conversion logic without testing against both MySQL and PostgreSQL.
- Ask first before adding new third-party dependencies — license compatibility with Apache 2.0 matters.
- Ask first before promoting package-private classes/methods to public.
- Ask first before adding new Maven modules or restructuring the module hierarchy.
- Ask first before introducing new Scala implicits in
streampark-scala— they affect all downstream Scala code. - Ask first before changing the authentication model (Shiro/JWT/Pac4j configuration).