Architecture
GridLink is ports-and-adapters. gridlink-core is a pure model of ag-grid’s contract with no framework
ties; each adapter maps that model onto one query technology.
flowchart TB
IN["ag-grid<br/>filter / sort JSON"]
subgraph core["gridlink-core — zero runtime deps"]
MODEL["GridRequest / GridResponse<br/>FilterModel / ColumnFilter<br/>SortModel"]
end
subgraph es["gridlink-elasticsearch — spring-data-elasticsearch (provided)"]
QUERIES["ElasticsearchQueries<br/>(the only Spring-aware code)"]
end
OUT["Criteria<br/>Sort<br/>CriteriaQuery"]
IN -->|binds to typed records| core
MODEL --> es
QUERIES --> OUT
Modules
| Module | Java | Runtime dependencies | Role |
|---|---|---|---|
gridlink-core |
17 | none | The port: typed model of ag-grid’s filter / sort / request-response JSON. |
gridlink-elasticsearch |
17 | spring-data-elasticsearch (provided) |
The adapter: model → Criteria / Sort / CriteriaQuery. |
jspecify supplies nullness annotations at provided scope only — it is not required at runtime.
The core model
The port is a small, sealed type hierarchy. FilterModel
exposes its columns as a Map<String, ColumnFilter>, and every value is one of exactly three records —
the compiler enforces the set, so translation needs no reflection.
classDiagram
class FilterModel {
<<interface>>
+filters() Map~String, ColumnFilter~
}
class ColumnFilter {
<<sealed interface>>
}
class FieldFilter {
<<record>>
+TextFilterType type
+String filter
+FilterOperator operator
+List~FieldFilter~ conditions
}
class NumberFieldFilter {
<<record>>
+NumberFilterType type
+Number filter
+Number filterTo
+FilterOperator operator
+List~NumberFieldFilter~ conditions
}
class DateFieldFilter {
<<record>>
+DateFilterType type
+String dateFrom
+String dateTo
+FilterOperator operator
+List~DateFieldFilter~ conditions
}
FilterModel o-- ColumnFilter : values
ColumnFilter <|-- FieldFilter
ColumnFilter <|-- NumberFieldFilter
ColumnFilter <|-- DateFieldFilter
Design decisions
- The model mirrors ag-grid’s wire JSON, so a request payload binds directly into typed records with no DTO or mapping layer. See Filtering.
- Columns are addressed by a
Map<String, ColumnFilter>, so a column’s name is decoupled from the document field it targets, andColumnFilterstays sealed and reflection-free. - Paging is derived, not stored —
offset()/limit()/pageNumber()compute from the rawstartRow/endRow. See Sorting & paging. - Columns combine via
subCriteria, preserving compoundOR/ANDbranches. See Elasticsearch adapter. - The adapter uses only the version-stable
Criteriasubset, so one artifact serves both Spring lines.
Compatibility
| Version | |
|---|---|
| Java (libraries) | 17+ |
| spring-data-elasticsearch | 5.x → Spring Boot 3 · 6.x → Spring Boot 4 |
| Elasticsearch / OpenSearch | any cluster the chosen spring-data-elasticsearch line supports |
One adapter artifact is tested against every supported spring-data-elasticsearch line in
CI; the list lives in
ci/spring-data-elasticsearch-versions.json.
Example
examples/opensearch-aggrid-demo
wires the whole path end to end — an ag-grid UI, a Spring Boot 4 backend, and OpenSearch — against a
~1,200-row catalogue.