Cohesive Systems logoCOHESIVE SYSTEMS

Building Blocks

Cohesive.Relations

Mapping, joins, hydration, and queries as one model.

Core Idea

Application code is full of hidden relations. A DTO is derived from an entity. A search document is derived from several entities. A dashboard row is derived from filtered and aggregated facts. An integration message is derived from a domain model and reshaped into an external schema.

These relationships are usually implemented with separate tools: mapper profiles, repository calls, LINQ fragments, SQL strings, search index builders, GraphQL resolvers, and hand-written projection code.

Cohesive.Relations gives these relationships a first-class model.

var loadSearchDocument = Relation<LoadSearchDocument>
    .From<Load>()
    .Join<Carrier>(
        static (load, carrier) => load.CarrierId == carrier.Id)
    .Where(
        static (load, carrier) => load.Status != LoadStatus.Cancelled)
    .Select(static (load, carrier) => new LoadSearchDocument
    {
        LoadId = load.Id,
        ReferenceNumber = load.ReferenceNumber,
        CarrierName = carrier.LegalName,
        CarrierMcNumber = carrier.McNumber,
        PickupCity = load.Pickup.City,
        DeliveryCity = load.Delivery.City,
        TotalAmount = load.TotalAmount
    })
    .Materialize(SearchIndexes.Loads);

The code looks like a projection, but Cohesive treats it as a relation: an inspectable definition of the sources, joins, filters, fields, target shape, and materialization intent.

Why This Is More Than Mapping

A mapping framework usually answers one question:

Given this source object, how do I produce this target object?

Cohesive.Relations answers a larger set of questions:

  • What facts does this target shape depend on?
  • Which sources have to be joined?
  • Which fields must be read?
  • Which filters apply before or after projection?
  • Can this run in memory, through repositories, or inside a backend?
  • Can this projection be materialized, indexed, tested, or generated?

DTO mapping is still part of the model. It is the simplest case. Cohesive.Relations starts where DTO mapping becomes relational: when a target shape depends on multiple sources, joins, filters, derived fields, backend capabilities, and materialization choices.

Assignment from a source field to a target field is one node in a larger relation graph. A search document may combine a load, customer, carrier, stops, status history, and financial summary. An API response may need authorization-sensitive fields from related entities. A dashboard row may need filtering, grouping, aggregation, and derived metrics.

At that point, the problem is no longer just object-to-object mapping. It is a relation.

LINQ as an Authoring Layer

LINQ showed that query belongs in application code.

It gave developers a familiar way to filter, join, group, sort, and project data without dropping into strings for every query. That idea remains valuable. Cohesive.Relations can support LINQ-style syntax as one authoring layer for relation definitions.

But Cohesive does not treat provider translation as the whole abstraction. In LINQ, the same expression may run in memory, translate to SQL, fail at runtime, partially evaluate locally, or lose access to backend-specific capabilities such as full-text search, scoring, nested documents, vector search, graph traversal, or materialized projections.

Cohesive.Relations separates the authoring surface from the semantic model. A relation may be written with C# expressions, fluent builders, generated definitions, visual tools, or Ari-inferred mappings. Those authoring surfaces compile into a relation IR that explicitly represents sources, joins, filters, projections, derived fields, aggregations, materialization targets, required fields, and backend capabilities.

LINQ
  language-integrated query syntax over provider-specific execution
 
Cohesive.Relations
  relation IR with optional LINQ authoring, explicit capabilities,
  hydration planning, and multiple execution targets

LINQ made query language-integrated. Cohesive.Relations makes relations system-integrated.

Hydration Is Part of the Relation

DTO mappers usually assume the source object already exists.

That is often not enough. If a target shape depends on several entities, related collections, external references, search metadata, or authorization-sensitive fields, the runtime needs to know what must be loaded before the projection can run.

Hydration is therefore part of the relation. A relation should be able to expose:

  • Which source shapes are required.
  • Which fields are read.
  • Which joins or lookups are needed.
  • Which filters can be pushed down.
  • Which fields can be selected without hydrating a full object.
  • Which sources can be batched.
  • Which sources must be loaded from repositories.
  • Which sources can be handled by a backend query.
  • Which parts must execute in memory.

That means a relation definition can become a hydration plan. The plan can decide whether to read full entities, select only the fields used by the projection, batch related lookups, call repositories, use a search backend, or materialize the result ahead of time.

One Relation, Many Interpretations

A relation should preserve intent before choosing execution.

The same relation definition may be interpreted as:

  • An in-memory mapper.
  • A repository hydration plan.
  • Generated projection code.
  • A SQL query.
  • An Elastic or Azure AI Search projection.
  • A graph traversal.
  • A search document builder.
  • A materialized read model.
  • An API response contract.
  • A UI query surface.
  • A testable projection contract.
  • An Ari-style schema relation.

This is the Cohesive pattern: separate what the system means from how it is realized, while keeping the two systematically connected.

Meaning

Relation Definition

Sources
which facts participate
Joins
how facts are related
Filters
which facts qualify
Mappings
how output is shaped
Materialization
where intent should land

Systematic Connection

Analyze capabilities, choose execution, generate artifacts, and keep realization traceable to the same semantic relation.

Realizations

In-memory mapper
Repository hydration
SQL query
Search projection
Generated code
Read model
API contract
UI query surface
Ari inference
The Cohesive pattern separates what the system means from how it is realized, while keeping both sides connected through analysis, generation, execution, and traceability.

Capability-Aware Execution

A relation should not assume every backend can do everything.

SQL databases, document databases, search engines, graph databases, in-memory collections, repositories, and stream processors all support different operations. Some support joins. Some support nested fields. Some support full-text search. Some support scoring. Some support aggregations. Some support graph traversal. Some support only a subset of filtering and sorting.

Cohesive.Relations makes those capabilities explicit.

var plan = relation.AnalyzeAgainst(SearchBackends.Elastic);
 
if (!plan.IsSupported)
{
    // Unsupported: relational join must be hydrated before indexing.
    // Supported alternative: repository hydration + generated projection.
}

The question is not only:

Can this expression be translated?

The better question is:

Which parts of this relation can run on this backend, which parts require hydration, and which parts must be materialized or executed elsewhere?

That gives teams earlier diagnostics, safer portability, and more honest execution plans.

Use Cases

Cohesive.Relations is useful when a projection spans more than one source, when mapping intent needs to be inspectable or testable, or when the same relation feeds API, UI, search, reporting, or integration surfaces.

Immediate use cases include:

  • DTO mapping.
  • In-memory joins followed by DTO mapping.
  • Query builders.
  • Search index document generation.
  • Read-model projection.
  • Integration schema mapping.
  • Aggregations and dashboard projections.
  • API response shaping.
  • UI query surfaces.
  • Ari-assisted schema relation discovery.

Use a simple mapper when the mapping is local, one-source, stable, and has no need for hydration, query lowering, materialization, or reuse. Use Cohesive.Relations when the relationship itself is important enough to become part of the system model.

Relation Lifecycle

Inputs

Shapes

Stable source and target shapes describe the facts a relation can observe and produce.

source identitytarget structurefield vocabulary

Meaning

Relation Definition

The semantic relation records sources, joins, filters, mappings, aggregations, and materialization intent.

joinsfiltersmappingsaggregation

Planning

Analysis

The relation is checked against capabilities before choosing where each part can run.

required fieldshydrationcapabilitiesunsupported operations

Realization

Execution

Runtimes lower the supported plan into repositories, SQL, search, graph, generated code, or materialized views.

in-memoryrepositoriesSQL/searchgenerated code
The lifecycle keeps semantic intent intact while each runtime makes a concrete execution choice from the same relation model.

The relation lifecycle keeps semantic intent intact while allowing different runtimes to make different execution choices. A backend-specific compiler can lower the supported parts. A repository runtime can hydrate the required fields. A code generator can produce a fast mapper. A projection engine can materialize a read model. Ari can infer candidate relations and hand them to developers for review.

The Relation IR

The relation IR is the lower-level model that makes the page-level promise practical. It should appear after the reader understands the problem, not before.

At the core is RelationDefinition, the canonical model for:

  • Stable relation identity and naming.
  • Semantic sources with aliases and cardinality.
  • Join definitions that preserve source identity and join intent.
  • Filters over the relation rowset.
  • Field mappings, derived values, and target projections.
  • Aggregation and grouping semantics.
  • Materialization targets for persisted read models or indexes.
  • Required fields and hydration metadata.
  • Backend capability requirements.
  • Determinism, code generation eligibility, and invariants.

Around that IR are typed authoring APIs, query APIs, execution services, observation mappers, lineage records, serialization support, and adapter-specific interpreters.

DTO mappers

What it contributes
Fast object-to-object assignment and convention mapping.
Where Cohesive.Relations starts
The mapping becomes a relation when target fields depend on joins, filters, hydration, lineage, materialization, or reuse.

LINQ

What it contributes
Language-integrated filtering, joining, grouping, sorting, and projection.
Where Cohesive.Relations starts
The query becomes a relation IR that can be inspected, planned, lowered, materialized, or rejected before execution.

Query builders

What it contributes
Composable query construction for a specific backend or dialect.
Where Cohesive.Relations starts
The relation model stays above one backend and records which capabilities each execution target must provide.

GraphQL

What it contributes
Client-facing graph selection, resolver composition, and schema-based transport.
Where Cohesive.Relations starts
Relations describe backend semantic projections that can inform APIs, UI surfaces, storage, search, and generated contracts.

Ari

What it contributes
Schema matching and candidate relationship discovery.
Where Cohesive.Relations starts
Ari can infer candidate relations; Cohesive.Relations can preserve, validate, execute, and generate from accepted relations.

Why It Matters

Most systems accumulate mapping code, query code, API response shaping, UI filter models, search documents, reporting SQL, integration transforms, and storage-specific adapters as separate artifacts. Each artifact embeds part of the same semantic relationship, usually as duplicated strings and one-off logic.

Cohesive.Relations makes those relationships first-class.

A mapper can execute. A relation can be inspected, hydrated, optimized, lowered, materialized, tested, and reused.

That gives higher-level Cohesive components one relation definition for:

  • Which shapes participate in a projection.
  • Which fields are selected, renamed, aggregated, or derived.
  • Which joins and predicates are semantically required.
  • Which source data must be hydrated before projection.
  • Which backend capabilities an execution target must support.
  • Which generated API, frontend, search, integration, or materialized artifacts should stay aligned.

The result is a foundation for semantic system definition: define the relation once, execute it in memory, hydrate it from repositories, lower it into backend queries, materialize it as a read model, or use it as the basis for schema matching.