clotho/CLAUDE.md
2026-05-15 00:09:01 +02:00

9.9 KiB

Clotho

A Java OGM (Object-Graph Mapper) for ArcadeDB, built on Apache TinkerPop/Gremlin.

Clotho is not a 1:1 node-to-object mapper. A "Clotho object" is a subgraph — a root vertex plus a declared set of edges and adjacent vertices spanning any number of hops. Complex graph traversals and analytics remain the developer's responsibility, written via @Query on repository interfaces. Clotho handles bounded object loading and persistence.


Module Structure

clotho/
  clotho-core/              # Zero Spring dependency. All mapping logic.
  clotho-spring-boot-starter/ # Autoconfigure, @Transactional bridge, Spring beans.
  clotho-dev-app/           # Minimal Spring Boot app for development testing against
                            # a live ArcadeDB instance. Not a production artifact.

Three Tiers of Graph-to-Java Mapping

1. Vertex Type

A single ArcadeDB vertex mapped to a Java class. Must extend ClothoVertex.

@VertexType("Employee")
public class Employee extends ClothoVertex {
    @Property("name") String name;

    @Include @Via("HAS_ADDRESS")
    Address address;  // eagerly loaded as part of this object's boundary
}

2. Edge Type

A single ArcadeDB edge mapped to a Java class. Must extend ClothoEdge.

@EdgeType("WORKS_AT")
public class WorksAt extends ClothoEdge {
    @OutVertex Employee employee;
    @InVertex Company company;  // omit @InVertex for edge-only inclusion
    LocalDate startDate;
}

3. Subgraph Type

A composite Java class assembled from multiple vertices and edges via a @Query traversal. Has no ARID. Does NOT extend ClothoVertex or ClothoEdge. save() delegates to all component ClothoEntity fields individually.

@SubgraphType
public class EmployeeProfile {
    Employee employee;      // field name matches Gremlin as("employee")
    Manager manager;
    @Alias("dept") Department department;
}

Technology Stack

Concern Library
Language Java 21
Build Maven (multi-module)
Graph connection Apache TinkerPop 3.x — DriverRemoteConnection over WebSocket
Null safety JSpecify (org.jspecify.annotations)
Boilerplate reduction Lombok
Testing JUnit 5
Spring integration Spring Boot 3.x autoconfigure

Key Design Decisions

  • Schema-first: Clotho assumes the ArcadeDB schema already exists. No DDL generation. The developer manages the schema (e.g., using a Flyway-based migration tool).
  • Optional lazy loading: @Include fields default to FetchType.EAGER (always loaded). Declare @Include(fetchType = FetchType.LAZY) to skip a field during populate(). Lazy fields remain null/empty until the developer calls session.loadField(vertex, "fieldName") explicitly. No proxy or bytecode enhancement is used.
  • Single-query boundary loading: All @Include fields for a vertex are fetched in a single Gremlin project() traversal. This eliminates N+1 queries per object boundary and prevents race conditions from sequential round-trips. For nested boundaries, each vertex gets its own single-project query (one query per vertex, not one per field).
  • Dirty tracking: ClothoEntity tracks which fields were mutated via notifyFieldChanged() (called from manual setters). On save, NEW entities write all @Property fields; MANAGED entities write only dirty fields and skip the update entirely if nothing changed. ClothoSession.close() auto-saves all MANAGED entities (vertices and edges) that carry dirty fields.
  • Optimistic locking: Declare @Version Long version on an entity field. Clotho reads ArcadeDB's built-in @version property on load and includes a .has("@version", version) filter on every update. A version mismatch throws OptimisticLockException. Both vertices and edges support @Version.
  • Edges are first-class citizens: ClothoEdge entities participate in the same EntityState lifecycle, dirty tracking, @Version OCC, identity map caching, and auto-save-on-close as ClothoVertex entities. Edges can be loaded by ID via session.load(EdgeType.class, id).
  • Result mapping: Clotho always instantiates the most specific registered class matching the vertex/edge label. Declared return types are upper bounds only.
  • Inheritance: Java class hierarchy is the primary mechanism. additionalParents on @VertexType handles ArcadeDB's multi-parent (diamond) inheritance where Java's single inheritance is insufficient.
  • No automatic deletion: Removing an element from an @Include collection does not delete it from the graph. Call session.delete() explicitly.

See docs/ for detailed specifications.


Package Structure

com.binarygolem.clotho
  .core
    .model       # ClothoEntity, ClothoVertex, ClothoEdge, ARID
    .annotation  # All annotations (@VertexType, @EdgeType, @SubgraphType, etc.)
    .session     # ClothoSession, ClothoSessionFactory
    .registry    # TypeRegistry
    .mapping     # ObjectMapper, SubgraphLoader, SubgraphPersistor, SubgraphTypeAssembler
    .query       # QueryExecutor, @Query proxy infrastructure
    .exception   # UnknownTypeException, ClothoException hierarchy
  .spring
    .config      # ClothoAutoConfiguration
    .tx          # Transaction bridge

Coding Conventions

Nullability

Every parameter, return type, and field must carry @NonNull or @Nullable from JSpecify. Default assumption is @NonNull; annotate only what can be null.

import org.jspecify.annotations.NonNull;
import org.jspecify.annotations.Nullable;

Lombok

Use Lombok where it reduces noise without obscuring intent:

  • @Getter on domain classes
  • @Builder on factory/config classes
  • @RequiredArgsConstructor / @AllArgsConstructor on service classes
  • @Value is NOT compatible with Clotho-managed domain objects (requires mutable fields)

@Setter restriction on entity classes: Do NOT use Lombok @Setter on any field annotated with @Property, @Include, @OutVertex, @InVertex, or @Version on a ClothoVertex or ClothoEdge subclass. Write a manual setter that calls notifyFieldChanged(fieldName) so the session can track the change:

// Correct
public void setName(String name) {
    this.name = name;
    notifyFieldChanged("name");
}

// Wrong — dirty tracking does not fire
@Setter String name;

Lombok @Setter remains allowed only for framework-managed fields (id, outVertexId, inVertexId) because Clotho sets those itself and they are not part of dirty tracking.

Comments

Write no comments unless the WHY is non-obvious. One line maximum. Never write doc comments that restate what the method name already says.

Naming

  • ArcadeDB type names (labels) use the @VertexType / @EdgeType annotation value. Class names need not match label names exactly.
  • Edge label constants: define as public static final String LABEL = "WORKS_AT" on the edge class to avoid magic strings at call sites.

Development Setup

Prerequisites

  • Java 21
  • Maven 3.9+
  • A running ArcadeDB instance (for integration tests and clotho-dev-app)

Build

mvn clean install

Run clotho-dev-app

mvn -pl clotho-dev-app spring-boot:run

Connection (local ArcadeDB default)

clotho.gremlin.url=ws://localhost:8182/gremlin
clotho.gremlin.username=root
clotho.gremlin.password=playwithdata
clotho.gremlin.traversal-source=g

Implementation Status

Phase Description Status
1 Project housekeeping — parent POM, module scaffold, package rename Done
2 Base types and annotations — ARID, ClothoEntity, ClothoVertex, ClothoEdge, all annotations, exception hierarchy Done
3 clotho-dev-app — Gremlin connection wiring, smoke test against live ArcadeDB Done
4 TypeRegistry — classpath scan, label→class map, hierarchy inference, additionalParents/parentLabel Done
5 ElementMapper — Gremlin Vertex/Edge → Java; always most-specific class by label Done
6 SubgraphLoader — traverse @Include/@Via, multi-hop, cycle detection, edge-only; SubgraphTypeAssembler Done
7 SubgraphPersistor — upsert, @version optimistic locking, cascade through boundary Done
8 ClothoSession / ClothoSessionFactory — identity map, load/save/delete/query, connection lifecycle Done
9 ClothoRepository<T> — abstract base class; g(), execute(), findAll(), findById(), etc.; dropped @Query/@Param Done
10 Spring Boot Starter — ClothoAutoConfiguration, ClothoProperties, @ClothoRepository bean scanning Done
11 Dirty tracking (notifyFieldChanged, EntityState), SessionContext, single-query project() loading, @Version OCC, FetchType.LAZY, FieldMetadataCache, expanded type coercion, populateAll() batch loading, auto-save on close, edge first-class parity Done
12 Transaction management — openTransactionalSession(), borrow counter, rollback(), getCurrentSession(), ClothoTransactionManager, Spring @Transactional bridge, ClothoAutoConfiguration tx bean Done

Documentation Index

Document Contents
docs/TYPE_SYSTEM.md ClothoEntity hierarchy, @VertexType, @EdgeType, @SubgraphType, inheritance, diamond inheritance, result mapping rule
docs/OBJECT_BOUNDARY.md @Include/@Via, multi-hop traversal, edge-only inclusion, cycle detection
docs/ANNOTATIONS.md Complete annotation reference with all attributes and examples
docs/QUERY_MODEL.md @Query, @ClothoRepository, @SubgraphType assembly, result mapping, parameter binding
docs/SPRING_INTEGRATION.md Autoconfigure, @Transactional, application.properties reference
docs/ARCHITECTURE.md Module breakdown, key components, data flow, session lifecycle
docs/TRANSACTIONS.md Transactional sessions, Spring @Transactional, nested propagation, @Version interaction, limitations