fe/fe-foundation/DESIGN.md
The existing fe-common module has accumulated heavy dependencies over time (Guava, Hadoop, Trino,
ANTLR, Alibaba MaxCompute SDK, etc.), making it unsuitable as a lightweight shared library for the
plugin/SPI ecosystem. When SPI plugin authors depend on fe-common, they are forced to pull in
dozens of transitive dependencies that have nothing to do with their plugin logic.
We need a zero-dependency foundation module that sits below fe-common and fe-extension-spi
in the dependency hierarchy, providing only the most essential, general-purpose utilities that any
module — including SPI plugins — can safely depend on.
| Candidate | Verdict |
|---|---|
fe-foundation | Chosen. Clear "below-common" semantics. No ambiguity with existing modules. |
fe-base | Too generic; widely used in other contexts. |
fe-essentials | Good semantics but verbose. |
fe-primitives | Implies primitive types; misleading. |
fe-toolkit | Industry convention maps "toolkit" to heavier helper libs (e.g., Trino). |
fe-kernel | Conflicts with database "kernel" terminology. |
Industry references:
trino-spi (zero-dep contract) + lib/trino-plugin-toolkit (optional helpers)flink-annotations (lightest) → flink-core-api → flink-corecommon/ (pure utilities) → api/ (interfaces) → core/ (implementation)seatunnel-common (utilities) + seatunnel-api (SPI)fe-foundation ← Zero third-party dependencies. Pure JDK utilities.
│
├── fe-extension-spi ← Plugin contracts (Plugin, PluginFactory, PluginContext)
│ │
│ └── fe-extension-loader ← Plugin classloading & discovery
│
├── fe-common ← Heavier shared code (Gson, Guava, Hadoop, etc.)
│
└── fe-core ← Main FE module (optimizer, catalog, transaction)
Key principle: fe-foundation has ZERO compile-scope third-party dependencies. Only JDK.
A class qualifies for fe-foundation if it meets ALL of:
java.* importsorg.apache.doris.foundation/
├── property/
│ ├── ConnectorProperty.java # @annotation: marks config fields
│ ├── ConnectorPropertiesUtils.java # Reflection-based KV → Bean binder
│ ├── StoragePropertiesException.java # Property-related RuntimeException
│ └── ParamRules.java # Fluent parameter validation DSL
├── type/
│ └── ResultOr.java # Rust-style Result<T, E> type
├── format/
│ └── FormatOptions.java # Immutable data formatting config
└── util/
├── BitUtil.java # Bit manipulation helpers
├── ByteBufferUtil.java # Unsigned ByteBuffer reads
├── SerializationUtils.java # Deep clone via Java serialization
└── PathUtils.java # URI path comparison utilities
| Class | Original Location | External Deps | Description |
|---|---|---|---|
ConnectorProperty | o.a.d.datasource.property | None | Runtime annotation for connector config fields |
ConnectorPropertiesUtils | o.a.d.datasource.property | None (Guava/commons-lang3 removed) | Reflection-based KV→Bean binding |
ParamRules | o.a.d.datasource.property | None | Fluent validation DSL with chained rules |
StoragePropertiesException | o.a.d.datasource.property.storage.exception | None | RuntimeException for property errors |
| Class | Original Location | External Deps | Description |
|---|---|---|---|
ResultOr<T,E> | o.a.d.common | None | Success-or-error result type |
| Class | Original Location | External Deps | Description |
|---|---|---|---|
FormatOptions | o.a.d.common | None | Immutable formatting configuration |
| Class | Original Location | External Deps | Description |
|---|---|---|---|
BitUtil | o.a.d.common.util | None | log2, power-of-2 rounding |
ByteBufferUtil | o.a.d.common.util | None | Unsigned byte buffer reads |
SerializationUtils | o.a.d.common.util | None | Deep clone via serialization |
PathUtils | o.a.d.common.util | None | URI path comparison with S3 handling |
| Class | Reason |
|---|---|
Pair, Triple | Depend on @SerializedName (Gson) for persistence. Moving would require adding Gson or breaking persistence compatibility. |
Writable, Codec, CountingDataOutputStream, DataInputBuffer, DataOutputBuffer, etc. | Deeply embedded in the persistence layer (194+ importers for Writable). High-risk migration better done in a separate phase. |
CloudCredential | Depends on commons-lang3. Could be migrated after trivial refactoring. |
GZIPUtils, EnvUtils | Depend on commons-io / Guava. Could be migrated after trivial refactoring. |
All 10 classes are SAFE to move (package rename):
java.io.Serializable (except StoragePropertiesException via Throwable,
but it is never serialized to disk)RuntimeTypeAdapterFactory (no class name stored in JSON)serialVersionUIDfe-foundation module with zero dependenciesorg.apache.doris.foundation.* packages// fe-core: org.apache.doris.datasource.property.ParamRules
// Now just re-exports the foundation class
package org.apache.doris.datasource.property;
public class ParamRules extends org.apache.doris.foundation.property.ParamRules {}
org.apache.doris.foundation.*<!-- fe-foundation/pom.xml -->
<artifactId>fe-foundation</artifactId>
<packaging>jar</packaging>
<name>Doris FE Foundation</name>
<description>Zero-dependency foundation utilities for Doris FE modules and SPI plugins</description>
<dependencies>
<!-- Intentionally empty. This module has ZERO third-party dependencies. -->
</dependencies>
fe-foundation (0 deps)
↑
├── fe-extension-spi (depends on fe-foundation)
│ ↑
│ └── fe-extension-loader
│
├── fe-common (depends on fe-foundation + Guava + Gson + Hadoop + ...)
│ ↑
│ └── fe-core
│
└── fe-core (depends on fe-foundation + fe-common + ...)