Skip to content

Multi-module reactor, ArchUnit guards, and a filter-sql-adapter module - #1

Merged
bbartolome merged 11 commits into
mainfrom
feature/multimodule-archunit-sql-adapter
Sep 16, 2026
Merged

bbartolome merged 11 commits into
mainfrom
feature/multimodule-archunit-sql-adapter

Conversation

@bbartolome

Copy link
Copy Markdown
Contributor

Summary

Restructures the single-module org.sequeless:sequeless-filter jar into a two-module Maven reactor:

  • filter-core — the existing DSL (AST, ANTLR grammar/parser, serializer, registries, completion), largely unchanged, plus two small independently-motivated fixes surfaced while designing the SQL adapter.
  • filter-sql-adapter (new) — translates a FilterNode AST into SQL via a hexagonal QueryBuilder vendor port, with a Template Method AnsiQueryBuilder default implementation and an H2-backed end-to-end test suite.

ArchUnit guards the module/package boundaries that are actually enforceable (see docs/specs/multimodule-archunit-sql-adapter/plan.md for the full design record — 33 decisions, D1–D33, resolved via user interview and three rounds of automated review before any code was written).

  • FieldRegistry.of(...) is reinstated in filter-core main scope, retyped to take a builder-constructed FieldRegistrySpec instead of a raw List; PermissiveFieldRegistry moves to test-only.
  • OperatorDefinition gains a derived value-shape signal (NONE/SCALAR/LIST), and FilterValidator gains a value-shape check — benefits every consumer of the parser, not just the SQL adapter.
  • filter-sql-adapter ships SqlFragment, ColumnMapping, FilterQueryTranslator/DefaultFilterQueryTranslator, SqlRenderException, and the QueryBuilder port + AnsiQueryBuilder default (8 protected extension hooks).
  • New ArchUnit rules keep filter-sql-adapter's main scope off filter-core's internal/spi packages, and document (though Maven already enforces) that filter-core may not depend back on sql...
  • docs/sql-adapter.md documents the pipeline, the JsonNode→Java value-conversion table, vendor QueryBuilder authoring, and known limitations (flat/single-table column mappings only, vendor-defined LIKE escaping, no temporal coercion).

No commit in this branch is marked as a breaking change (per plan decisions D1/D10), even though a couple of the filter-core API changes are technically breaking — nothing in this repo is actually published today (no <distributionManagement>/deploy step exists), so there's no real external consumer to break.

Commits

  • T1 fix: — retype FieldRegistry.of to FieldRegistrySpec, drop permissive mode
  • T2 feat: — derived value-shape signal + FilterValidator shape check
  • T3 build: — convert to a Maven reactor with a filter-core module
  • T4 build: — reactor-proof release tooling and docs
  • T5 feat: — add filter-sql-adapter module contracts
  • T6 feat: — implement AnsiQueryBuilder and FilterQueryTranslator
  • T7 feat: — add H2 test fixture and end-to-end SQL adapter tests
  • T8 feat: — add filter-sql-adapter ArchUnit boundary rules
  • T9 docs: — add filter-sql-adapter reference

Test plan

  • ./mvnw -B verify green across all 3 reactor modules (82 filter-core tests + 41 filter-sql-adapter tests)
  • filter-core's existing BoundaryRulesTest rules pass unmodified
  • filter-sql-adapter's new ArchUnit rule (sql.. may not reach internal../spi..) verified to actually fail on a deliberate violation, then reverted before commit
  • H2 end-to-end tests cover all 13 built-in operators, any-expansion with a partial ColumnMapping (dropped-unmapped-operand behavior), and a null-valued is in element
  • Manual review of the design decisions in docs/specs/multimodule-archunit-sql-adapter/plan.md, particularly the accepted breaking-but-unmarked FieldRegistry/FilterValidator changes

🤖 Generated with Claude Code

@bbartolome
bbartolome marked this pull request as draft July 30, 2026 22:49
@bbartolome
bbartolome force-pushed the feature/multimodule-archunit-sql-adapter branch 11 times, most recently from 99e48fc to c2a105c Compare September 4, 2026 02:54
@sonarqubecloud

sonarqubecloud Bot commented Sep 4, 2026

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
73.0% Coverage on New Code (required ≥ 80%)
C Reliability Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

bbartolome and others added 11 commits September 7, 2026 20:10
… mode (T1)

FieldRegistry.of now takes a Lombok-built FieldRegistrySpec instead of a raw
List<FieldDefinition>, so the registry can gain configuration later without
another signature change. FieldRegistry.permissive()/isPermissive() are removed
and PermissiveFieldRegistry becomes a test-only fixture; the two isPermissive()
call sites collapse to an unconditional fields.find(path).isEmpty() check,
which is behaviour-preserving because the fixture's find() always resolves.
…(T2)

OperatorDefinition gains a derived, three-state ValueShape (NONE for unary,
LIST for FUNCTION-syntax or explicitly list-valued operators like "is in",
SCALAR otherwise) rather than a plain boolean, since a scalar-defaulting
boolean would wrongly reject every FUNCTION-syntax filter. FilterValidator
now checks a FieldFilter/AnyFilter's value shape against its resolved
operator's declared shape, alongside the existing applicableTypes/
permittedOperators checks, benefiting every FilterValidator consumer.
Wire filter-sql-adapter into the reactor and define its public API/SPI
contracts (sql.api.SqlFragment/ColumnMapping/FilterQueryTranslator/
SqlRenderException, sql.spi.QueryBuilder) per plan.md's Phase 3 design.
Contracts and Javadoc only — no translation logic yet (T6).
Adds sql.spi.AnsiQueryBuilder, a public Template Method QueryBuilder
implementation whose final binary()/unary() dispatchers delegate to
8 protected hooks covering all 13 built-in operators plus a
renderUnknown/unaryUnknown fallback pair for custom operators (D16),
with a security-relevant mandated quoteIdentifier escaping contract
(D23).

Adds sql.api.DefaultFilterQueryTranslator, the concrete
FilterQueryTranslator implementation: validates via FilterValidator
on entry (D27), converts JsonNode values per D7/D19's mapping
(including the catch-all for unrecognized subtypes), expands
AnyFilter per-node in visitAny while dropping unmapped operands
(D18/D29), rejects zero-operand And/OrFilter at any depth and empty
list-shaped values (D11/D22/D30), and dispatches binary()/unary() by
operator.isUnary() rather than by null value (D33).

Adds unit tests covering all 13 built-in operators' SQL/bind-parameter
output, a custom-operator renderUnknown fallback (with and without an
override), the status-is-null vs. exists dispatch distinction, and the
empty and/or / empty-list / any-expansion drop-unmapped edge cases.
Pins H2 2.3.232 (test scope) in the parent dependencyManagement and
filter-sql-adapter's own dependencies. Adds a test-scope H2QueryBuilder
extending AnsiQueryBuilder, overriding quoteIdentifier solely to exercise
the Template Method extension path with real coverage. Adds
H2FilterQueryTranslatorTest, executing DefaultFilterQueryTranslator output
as real PreparedStatements against an in-memory H2 schema/seed dataset,
covering each operator family end to end plus D18's any-expansion
drop-unmapped-operands path and D21's null-tolerant is-in parameter list.
Adds BoundaryRulesTest to filter-sql-adapter with a module-local
public-types rule (sql.api/sql.spi types must be public, mirroring
filter-core's existing rule), a cross-module rule that filter-sql-adapter's
main scope may not depend on filter-core's internal/spi packages, and
a rule that filter-core may not depend on filter-sql-adapter (kept per
D26 as documentation of intent even though Maven's reactor build order
already makes it unfalsifiable). No testfixtures-leak rule is added,
per D15.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Documents the SQL adapter's pipeline (validate -> translate -> ColumnMapping
-> QueryBuilder -> PreparedStatement), the JsonNode-to-Java value-conversion
table with its date-time and BigInteger caveats, the vendor-defined LIKE
escaping vs. mandated identifier quoting distinction, how to write a vendor
QueryBuilder by extending AnsiQueryBuilder's 8 protected hooks, and the
adapter's known limitations (flat/single-table mappings, no custom
AnyExpansionStrategy). Linked from README.md and docs/filter-dsl.md.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Restructure dense paragraphs into scannable bullets and tables:
- ADR: tighter Context/Decision/Consequences with bullet-point breakdown
- SQL adapter: top-of-page nav, inline pipeline diagram, tables for LIKE/quoting
  and known limitations, consolidated caveats to avoid duplication

No content changes — all substantive information preserved, just reorganized
for better skimmability.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@bbartolome
bbartolome force-pushed the feature/multimodule-archunit-sql-adapter branch from c2a105c to 61b6445 Compare September 7, 2026 20:14
@sonarqubecloud

sonarqubecloud Bot commented Sep 7, 2026

Copy link
Copy Markdown

@bbartolome
bbartolome marked this pull request as ready for review September 16, 2026 02:48
@bbartolome
bbartolome added this pull request to the merge queue Sep 16, 2026
@bbartolome
bbartolome removed this pull request from the merge queue due to the queue being cleared Sep 16, 2026
@bbartolome
bbartolome merged commit 14c7a5d into main Sep 16, 2026
4 of 5 checks passed
@bbartolome
bbartolome deleted the feature/multimodule-archunit-sql-adapter branch September 16, 2026 03:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant