CQRS, the easy way
Chenile separates the command side (services and workflows that change state) from the query side (read-optimized search). Chenile Query — in chenile-query-workflow-blueprints — is the read side: a metadata-driven search framework backed by MyBatis.
The headline is what you don’t write. To add a searchable query you configure two files and write no Java:
- a MyBatis mapper (the SQL), and
- a JSON metadata file describing the queryable columns.
Chenile turns that into a REST endpoint that supports filtering, like/contains matches, sorting, pagination, downloads and canned reports.
The query metadata
Each column is declared once, with the capabilities it should expose:
{
"id": "Student.getAll",
"name": "students",
"columnMetadata": {
"name": { "name": "name", "columnType": "Text", "filterable": true, "likeQuery": true },
"branch": { "name": "branch", "columnType": "Text", "filterable": true, "containsQuery": true, "sortable": true },
"percentage": { "name": "percentage", "columnType": "Number", "filterable": true, "sortable": true }
}
}
filterable, likeQuery, containsQuery and sortable are the switches that decide what a caller may do with each column — the framework enforces them so clients can’t craft arbitrary queries.
Try it
This is the query builder in miniature. Toggle the filters and sort; watch the SearchRequest Chenile would build, and the rows it returns — no Java involved:
Behind the scenes a SearchRequest flows through the SearchService to a MyBatis-backed QueryStore, and comes back as a paginated SearchResponse (SearchPaginationInfo, SortCriterion, ResponseRow). Downloads (DownloadFormat) and saved canned reports (CannedReport) come along for free.
No code — generated by blueprint
Because a query is just configuration, the mybatisQuery blueprint generates the whole module for you: a project with the mapper and metadata folders (by convention, …query.service.mapper) that depends on chenile-query-controller. The student-query-service and order-query-service projects in chenile-samples are complete working examples — open them and note that src/main/java is essentially empty.
Built for many tenants
The query side is tenant-aware out of the box. A QueryTenantResolver resolves the current tenant, and QueryDatasourcesProperties lets each tenant read from its own query datasource — so the same endpoint serves every customer while each sees only its own rows. That’s the thread we pick up in part 13.
QueryMetadata, ColumnMetadata, SearchRequest/SearchResponse, SearchService, QueryStore in query-api / chenile-query-service; the endpoint in chenile-query-controller. Samples: student-query-service, order-query-service.