Cohesive Systems logoCOHESIVE SYSTEMS

Capability Closure, Not Lowest Common Denominator

A common critique of any cross-provider abstraction is that it must flatten every target into the lowest common denominator.

The critique is valid when the abstraction is only a thin uniform wrapper. If PostgreSQL, Cosmos DB, Kafka, Elasticsearch, Temporal, Orleans, and cloud infrastructure are all hidden behind a single generic facade, the facade can only expose what all targets already share. Everything distinctive becomes unreachable or has to leak through undocumented side channels.

Cohesive is designed against that failure mode. It does not treat provider differences as details to erase. It treats them as capabilities to model, compose, diagnose, and project from a semantic definition.

Capability closure means Cohesive models the capabilities of supported targets as first-class facts, then realizes semantic requirements through native mappings, composition, diagnostics, overrides, and direct provider access when needed.

The goal is not the intersection of all backends. The goal is to decouple semantic structure from interpretation, then bind stable semantic constructs to the capability closure of modeled providers. Some mappings are native, some are composed from supporting facilities, some are unavailable and become diagnostics, and some require an intentional escape hatch into target-specific code.

The Lowest Common Denominator Trap

Lowest-common-denominator abstractions usually start with a reasonable goal: make application code portable across targets.

The problem is the definition of portability. If portability means "only use the features every target has in exactly the same way," the abstraction becomes smaller than every real target beneath it.

For databases, that usually means hiding indexes, partition keys, constraints, change feeds, transactional guarantees, query planners, native operators, consistency levels, and execution diagnostics behind a generic repository API.

For workflow and runtime targets, it means hiding durable timers, replay rules, activity retry policies, actor placement, message ordering, leases, queues, streams, supervision, and compensation behavior behind generic "background job" or "process" vocabulary.

For infrastructure, it means hiding regions, identity boundaries, deployment topology, network policy, scaling behavior, secrets, managed resources, and provider-specific service limits behind a flat deployment model.

That kind of abstraction reduces the developer's precision. It makes easy cases look tidy, but it gives the system no language for explaining why a target is a good fit, a partial fit, or a bad fit.

Cohesive takes the opposite stance: provider differences matter because they are part of the system's operational semantics.

Capabilities, Not Facades

Cohesive models backend targets through capabilities rather than through a single facade. A capability is a statement about what a target can do and under which conditions. A requirement is a statement about what the semantic model needs from a target.

PostgreSQL can do relational joins; a query that traverses a relation has a requirement for that capability. Cosmos DB cannot do relational joins natively, but Cohesive can compose the same result through an in-memory join over multiple Cosmos queries or through a change-feed projection that denormalizes the data ahead of time. That difference is not erased — it is modeled.

Matching requirements against capabilities makes the abstraction larger, not smaller. It can express the semantic need, native support, composed strategies, constrained support, target-specific extension points, and cases where the selected backend cannot satisfy the requirement without additional runtime support.

That is the sense in which Cohesive aims for capability closure. It is not "PostgreSQL and Cosmos both have feature X, therefore Cohesive exposes X." It is "the model requires behavior R; target A provides it natively, target B can realize it through strategy S plus constraint C, target C cannot realize it without an explicit extension."

Escape Hatches Are Part of the Model

Cohesive's bottom-up posture matters here. A Cohesive model is not meant to lock the developer away from PostgreSQL, Cosmos DB, Kafka, Elasticsearch, Temporal, Durable Task, Orleans, a cloud SDK, or a generated codebase.

Cohesive should first give developers normal extension points: capability declarations, compiler hooks, generated artifact overrides, target-specific options, and diagnostics. Those are not escape hatches. They are part of the abstraction's ordinary extension model.

The escape hatch is more basic: when the semantic model, compiler, and override surface are not enough, the underlying target client remains reachable.

A query compiler may cover the common and composed cases. It may allow a backend-specific operator, index hint, analyzer choice, scoring override, projection binding, or runtime option. But a developer should still be able to drop to the PostgreSQL connection, Cosmos container, Elasticsearch client, Azure SDK, Temporal client, or infrastructure provider API and work with the target directly.

That last-resort path should be intentional, visible, and reviewable. The model can record that a generated path has been replaced or supplemented by target-specific code, but it should not pretend that every useful provider operation must first be squeezed into Cohesive vocabulary.

An escape hatch is not a failure of abstraction. It is how a precise abstraction acknowledges that the target remains real, and that some target-specific work is best done with the target's own client.

Diagnostics Over Silent Degradation

The worst abstraction does not merely hide a capability. It silently degrades semantics.

A query that needs stable ordering should not quietly become an unordered scan. A transition that needs atomic uniqueness should not become a best-effort check. A workflow that needs durable recovery should not become an in-memory callback. A tenant isolation requirement should not become a naming convention.

Cohesive makes these mismatches diagnostic:

Cross-entity atomic transition

Possible Diagnostic
Selected target supports atomic writes only within one partition.
Resolution Path
Choose a different target, revise the partition model, or compile to a process with compensation.

Relational query with filtered joins

Possible Diagnostic
Document target cannot realize this shape without a projection or denormalized read model.
Resolution Path
Generate a projection, change the query shape, or route this query to a relational target.

Reliable external effect

Possible Diagnostic
Transition writes state and emits an integration message without a shared durability boundary.
Resolution Path
Compile an outbox, use a change feed, or bind the effect to a durable workflow.

History-sensitive entity behavior

Possible Diagnostic
Current-state persistence loses information required by the invariant.
Resolution Path
Emit durable events, use event sourcing, or persist the required historical observation explicitly.

Low-latency search over mutable data

Possible Diagnostic
Authoritative store can answer the query but does not satisfy latency or indexing requirements.
Resolution Path
Project to a search target and make read-model freshness part of the contract.

A useful abstraction should enable precision. It is not vague convenience. It gives the system a vocabulary for saying exactly what is required, exactly what is available, and exactly where a proof obligation has not been discharged.

Compiler and HAL Tactics

Cohesive is closer to a compiler or an operating-system hardware abstraction layer (HAL) than to a conventional library facade.

A compiler does not pretend every CPU architecture is identical. It has an intermediate representation, a target description, optimization passes, calling conventions, intrinsic functions, feature flags, and diagnostics. When a target has a native instruction, the compiler may use it. When it does not, the compiler may lower the operation into a sequence of simpler operations. When the target cannot support the required semantics, compilation fails or asks for a runtime helper.

An OS HAL follows a similar pattern. Hardware differences are not erased. They are organized behind contracts, capability discovery, driver hooks, interrupt models, memory models, and fallback paths. The kernel can target many devices because it has a disciplined way to separate portable semantics from target-specific control.

Cohesive can use the same tactics:

Intermediate representation

Compiler / HAL Analogy
IR separates source language from machine code.
Cohesive Interpretation
The semantic system graph separates domain requirements from concrete storage, runtime, API, and infrastructure targets.

Target descriptions

Compiler / HAL Analogy
Backends declare registers, instructions, memory rules, and calling conventions.
Cohesive Interpretation
Providers declare transaction scope, query operators, partition behavior, consistency, indexing, workflow durability, and deployment limits.

Lowering

Compiler / HAL Analogy
High-level operations become target-specific instruction sequences.
Cohesive Interpretation
A relation, transition, or effect becomes SQL, document queries, projections, outbox entries, workflow steps, actor messages, or generated API code.

Intrinsics

Compiler / HAL Analogy
Source code can request a target-native operation intentionally.
Cohesive Interpretation
A model or generated artifact can bind to PostgreSQL extensions, Cosmos change feed, provider SDKs, or target-specific runtime hooks.

Optimization

Compiler / HAL Analogy
The same program can be optimized differently per architecture.
Cohesive Interpretation
The same query or projection can choose indexes, denormalization, batching, caching, or materialization strategies per backend.

Diagnostics

Compiler / HAL Analogy
Unsupported instructions, unsafe casts, and ABI mismatches are compile-time errors.
Cohesive Interpretation
Capability mismatches become model diagnostics rather than runtime surprises.

The abstraction is therefore not a ceiling. It is a coordination layer between semantic intent and target-specific realization.

SQL as a Precedent

SQL is a useful precedent because it is both portable and target-specific.

The relational model gives developers a shared language for selecting, joining, filtering, grouping, constraining, and updating data. That shared language did not prevent PostgreSQL, SQL Server, Oracle, MySQL, SQLite, and other engines from building distinct planners, indexes, isolation behavior, extensions, execution strategies, and operational profiles.

The strongest SQL systems are not successful because they expose only the features common to every engine. They are successful because the common relational core is valuable, the optimizer has target-specific freedom, and providers expose extensions when the standard vocabulary is not enough.

Cohesive adopts the same lesson:

  • Define precise shared semantics for the parts of the domain that carry operational meaning.
  • Let each target optimize, specialize, or override how those semantics are realized.
  • Model target-specific capabilities explicitly through first-class capability declarations and diagnostics.
  • Keep generated artifacts inspectable and extensible.

That is a higher bar than a generic repository or universal adapter. It also preserves more power.

Example: Search Query Migration

Consider a migration from Azure Search to Elasticsearch.

In practice, such a migration often means rewriting every query, filter, sort, facet, and aggregation against the Elasticsearch client. Azure Search and Elasticsearch both support search, but their query surfaces are not the same: Azure Search combines OData filters, Lucene-style search syntax, facets, and scoring profiles; Elasticsearch exposes a richer query DSL, aggregation model, and analyzer configuration.

A lowest-common-denominator abstraction would expose only the operations both targets share in the same way. That is not enough for a non-trivial scenario.

A Cohesive.Relations query can keep query requirements explicit. A query definition can represent text search, field predicates, ordering, field selection, aggregations, pagination, and scoring intent without committing up front to either backend. The Azure Search compiler can lower structured predicates to OData filters and text search to Lucene-style clauses. The Elasticsearch compiler can lower the same semantic query to the Elasticsearch SDK. Capability checks decide which parts are native, which parts are composed, which parts need an override, and which parts should fail with a diagnostic.

The tabs below show this capability-requirement pattern applied to search queries, database queries, and database updates.

Search query requirements mapped to target capabilities

Azure Search capability OData filters + Lucene query syntax maps to requirement Full-text search as native support. Azure Search capability Sorting + continuation maps to requirement Sorting & pagination as constrained support: No PIT or hierarchical sort. Azure Search capability Facets maps to requirement Aggregations as constrained support: Facets only. Elasticsearch capability Elasticsearch query DSL maps to requirement Full-text search as native support. Elasticsearch capability PIT search + hierarchical sorting maps to requirement Sorting & pagination as native support. Elasticsearch capability Rich aggregations DSL maps to requirement Aggregations as native support.

Native

Directly supplied by the target.

Composed

Compiled strategy assembled from target facilities.

Constrained

Supported only inside a declared target boundary.

A fuller Cohesive.Relations example can extend this same pattern to the source-data queries that populate the index.

Practical Design Implications

The practical proposal is to make capability modeling part of the product surface, not an internal implementation detail.

  • Capability graph per target. A developer can ask: what does this target support natively, what does Cohesive synthesize, what requires an auxiliary runtime, and what remains unavailable?

  • Generated artifacts carry provenance. A SQL query, document projection, API route, workflow, actor, index, or infrastructure resource points back to the semantic requirement that produced it.

  • Overrides are explicit and local. If a developer replaces a generated query with target-specific SQL, the model knows that this projection has a native override rather than treating the change as a forked artifact outside governance.

  • Target selection is explainable. Cohesive can say why PostgreSQL is a better realization for one relation, why Cosmos is a better realization for one entity family, why Elasticsearch is a projection rather than an authoritative store, or why a transition needs a durable workflow instead of a simple request handler.

  • Failure is precise. The right answer is sometimes "this model cannot be realized on this target with these guarantees." That is not a limitation of Cohesive. It is the abstraction doing useful work.

That is not lowest common denominator. It is capability closure: semantic requirements matched against explicit target capabilities, with native mappings, composition, diagnostics, overrides, and direct target access.