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, and ColumnFilter stays sealed and reflection-free.
  • Paging is derived, not stored — offset() / limit() / pageNumber() compute from the raw startRow / endRow. See Sorting & paging.
  • Columns combine via subCriteria, preserving compound OR/AND branches. See Elasticsearch adapter.
  • The adapter uses only the version-stable Criteria subset, 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.