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

7.5 KiB

Architecture

Module Overview

clotho-core
  Zero Spring dependency.
  All mapping, session, registry, and query logic.

clotho-spring-boot-starter
  Spring Boot 3.x autoconfiguration.
  Thin layer: creates beans, bridges @Transactional, scans @ClothoRepository.
  Depends on clotho-core.

clotho-dev-app
  Minimal Spring Boot application.
  Connected to a live ArcadeDB instance.
  Used throughout development for real-DB testing.
  Not a production artifact; not published.

clotho-core Component Map

ClothoSessionFactory  (entry point)
  │  Holds: DriverRemoteConnection, TypeRegistry, global config
  │
  ├── TypeRegistry
  │     Built once at startup from classpath scan.
  │     Maps: ArcadeDB label → Class<? extends ClothoEntity>
  │     Knows: Java + ArcadeDB inheritance hierarchy, additionalParents
  │
  └── ClothoSession  (unit of work, one per transaction/request)
        │  Holds: identity map (ARID → ClothoEntity instance)
        │
        ├── load(Class<T>, ARID) → T
        │     └── SubgraphLoader
        │           Uses: TypeRegistry, ObjectMapper
        │           Executes Gremlin traversal for root vertex + @Include boundary
        │           Cycle detection via visited-ARID set
        │
        ├── save(entity)
        │     └── SubgraphPersistor
        │           Upserts root vertex/edge + all @Include elements recursively
        │           Uses ArcadeDB @version for optimistic locking
        │           @SubgraphType: iterates all ClothoEntity fields, saves each
        │
        ├── delete(entity)
        │     Removes root vertex/edge from graph
        │     Does NOT cascade deletion to @Include elements
        │
        └── query(Class<T>, queryName, params) → List<T>
              └── QueryExecutor
                    Resolves named @Query, binds params, executes, maps results
                    Also drives @ClothoRepository proxy methods

ObjectMapper  (used by SubgraphLoader and QueryExecutor)
  Gremlin Vertex/Edge → ClothoEntity subclass
  Always uses most-specific registered class (TypeRegistry lookup by label)
  Sets @Property fields, @OutVertex/@InVertex fields on edges

SubgraphTypeAssembler  (used by QueryExecutor for @SubgraphType returns)
  Maps Gremlin select() result map → @SubgraphType fields
  Binding: field name or @Alias → select key
  Each bound value is mapped via ObjectMapper

Data Flow: Loading a Composite Object

Developer:  session.load(Order.class, arid)
    │
    ▼
SubgraphLoader
  1. g.V(arid.asString())                → TinkerPop Vertex (Order)
  2. ObjectMapper.map(vertex)            → Order instance (most-specific class)
  3. Register in identity map
  4. For each @Include field on Order:
       @Include @Via("HAS_LINE") List<OrderLine> lines
       ┌──────────────────────────────────────────┐
       │ g.V(arid).out("HAS_LINE")               │
       │   → [TinkerPop Vertex, Vertex, ...]      │
       │   for each: ObjectMapper.map(v)          │
       │     → OrderLine (or subtype by label)    │
       │   recurse: load OrderLine @Include fields│
       └──────────────────────────────────────────┘
  5. Assemble Order.lines = [OrderLine, ...]
  6. Return fully assembled Order

Data Flow: Saving a Composite Object

Developer:  session.save(order)
    │
    ▼
SubgraphPersistor
  1. Upsert root Order vertex
       if order.getId() == null → create vertex with label "Order"
       else                    → update vertex, check @version (optimistic lock)
  2. For each @Include field on Order:
       Field type is @VertexType (e.g., List<OrderLine>):
         for each element → recursively save (SubgraphPersistor.save(orderLine))
         ensure edge "HAS_LINE" exists from Order to OrderLine
       Field type is @EdgeType (e.g., List<WorksAt>):
         save edge properties; ensure @OutVertex and @InVertex are already persisted
  3. Identity map deduplication: elements already saved in this call are skipped
  4. Removed elements: NOT deleted automatically

Data Flow: @Query on a @ClothoRepository

Developer:  employeeQueries.findByDepartment("Engineering")
    │
    ▼
ClothoRepository proxy (generated by QueryExecutor at startup)
  1. Read @Query gremlin string from method annotation
  2. Bind @Param values: replace :dept with "Engineering"
  3. Execute: g.V().hasLabel('Employee').has('dept','Engineering')
  4. For each result vertex:
       ObjectMapper.map(vertex) → most-specific Employee subclass
  5. Return List<Employee>

For @SubgraphType return:

  3. Execute query with select() → List of Maps
  4. For each Map:
       SubgraphTypeAssembler.assemble(EmployeeProfile.class, map)
         → bind map keys to fields by name / @Alias
         → ObjectMapper.map() each value
  5. Return List<EmployeeProfile>

Session Identity Map

The identity map is the session's in-memory cache of loaded vertices and edges.

  • Key: ARID
  • Value: ClothoEntity instance

Rules:

  • First load of an ARID → fetch from DB, map to Java, store in map, return
  • Subsequent load of same ARID → return existing instance (no DB call)
  • save() does NOT clear the identity map; the instance is mutated in place
  • close() clears the map and releases the Gremlin connection

The map operates at the individual vertex/edge level. Multiple composite objects (e.g., an Order and an EmployeeProfile) may both hold references to the same Address instance if their traversals reached the same ARID.


TypeRegistry Startup Validation

At ClothoSessionFactory.build() time, the TypeRegistry validates:

  • Every @VertexType class extends ClothoVertex
  • Every @EdgeType class extends ClothoEdge
  • No two classes claim the same label
  • additionalParents labels are resolvable to registered classes
  • parentLabel values are resolvable to registered classes
  • Every @Include field is paired with @Via
  • @OutVertex / @InVertex field types are @VertexType classes

Startup failure with a descriptive message is strongly preferred over silent misconfiguration that produces confusing runtime errors.


Package Structure

com.binarygolem.clotho
  .model
    ClothoEntity
    ClothoVertex
    ClothoEdge
    ARID
    UnknownTypeBehavior

  .annotation
    VertexType
    EdgeType
    SubgraphType
    RID
    Property
    Include
    Via
    Direction
    OutVertex
    InVertex
    Alias
    Query
    Queries          (container for @Repeatable @Query)
    Param
    ClothoRepository

  .session
    ClothoSession
    ClothoSessionFactory
    ClothoSessionFactoryBuilder

  .registry
    TypeRegistry
    TypeRegistryBuilder
    TypeDescriptor     (holds metadata for one registered type)

  .mapping
    ObjectMapper
    SubgraphLoader
    SubgraphPersistor
    SubgraphTypeAssembler

  .query
    QueryExecutor
    QueryDescriptor    (holds parsed @Query metadata for one method)
    ParameterBinder

  .exception
    ClothoException
    UnknownTypeException
    OptimisticLockException
    ClothoConfigurationException

// clotho-spring-boot-starter
com.binarygolem.clotho.spring
  .config
    ClothoAutoConfiguration
    ClothoProperties
  .tx
    ClothoTransactionManager
    ClothoTransactionObject
  .repository
    ClothoRepositoryScanner
    ClothoRepositoryProxyFactory