remodel-core

Conceptual Model

The Entity-Relationship structures at the heart of remodel-core — entities, attributes, relationships, specializations, and unions.

The conceptual model (models::conceptual) represents an Entity-Relationship diagram as an in-memory graph. All elements are referenced by strongly-typed u32 handles (IDs) rather than pointers or Rc, which keeps the model easy to serialize, clone, and mutate without lifetime issues.

ConceptualModel

The root container. Call ConceptualModel::new to create one.

let mut m = ConceptualModel::new("e_commerce");
FieldTypeDescription
nameStringDiagram name (project or schema name)
entitiesIndexMap<EntityId, Entity>All entities, in insertion order
attributesIndexMap<AttributeId, Attribute>All attributes (entity and relationship)
relationshipsIndexMap<RelationshipId, Relationship>All relationships
specializationsIndexMap<SpecializationId, Specialization>IS-A hierarchies
unionsIndexMap<UnionId, Union>Category / union constructs
associative_entitiesIndexMap<AssociativeEntityId, AssociativeEntity>Associative entities wrapping a relationship

Entities

Adding entities

let customer = m.add_entity("Customer");           // regular entity
let order_item = m.add_weak_entity("OrderItem");   // weak entity (requires identifying relationship)

Weak entities are flagged with entity.weak = true. The conversion pipeline requires them to have a partial-key attribute and an identifying (1:N mandatory) relationship.

Entity fields

FieldTypeDescription
idEntityIdUnique handle within the model
nameStringDisplay name
noteStringFree-form annotation
attributesVec<AttributeId>IDs of owned attributes, in order
weakbooltrue for weak entities

Attributes

Attributes can be owned by an entity or by a relationship (descriptive attributes).

Adding attributes

use remodel_core::models::conceptual::AttributeOwner;

// Primary key
let pk = m.add_primary_attribute(customer, "id", DataType::Integer)?;

// Regular attribute on entity
m.add_attribute(AttributeOwner::Entity(customer), "email", DataType::Varchar(120))?;

// Descriptive attribute on a relationship
m.add_attribute(AttributeOwner::Relationship(rel_id), "since", DataType::Date)?;

Attribute fields

FieldTypeDescription
idAttributeIdUnique handle
nameStringColumn name after conversion
data_typeDataTypeSee Data Types
is_primaryboolPart of the entity's primary identifier
is_partial_keyboolPartial key of a weak entity
is_optionalboolAllows NULL
kindAttributeKindSimple, Composite, Multivalued, or Derived
childrenVec<AttributeId>Sub-attributes for Composite kind

AttributeKind

pub enum AttributeKind {
    Simple,                           // plain single-valued attribute
    Composite,                        // contains sub-attributes (children)
    Multivalued { min: u32, max: Option<u32> }, // 0..N values per entity
    Derived,                          // computed from other attributes (shown underlined)
}

Multivalued attributes are converted to a separate table by default. Use ConvertOptions::complex_attribute to change this to Flatten.

Relationships

Building a relationship

Use the fluent relate builder:

// Binary 1:N
m.relate("places", customer, Cardinality::OneToMany)
 .with(order, Cardinality::ZeroToMany);

// Self-referencing
m.relate("manages", employee, Cardinality::ZeroToOne)
 .with(employee, Cardinality::ZeroToMany);

// With role labels
m.relate("supervises", employee, Cardinality::ZeroToOne)
 .with_role("manager")
 .with(employee, Cardinality::ZeroToMany)
 .with_role("subordinate");

// Ternary
m.relate("supplies", supplier, Cardinality::OneToMany)
 .with(product,  Cardinality::OneToMany)
 .with(project,  Cardinality::OneToMany);

// Descriptive attribute on the relationship itself
m.relate("enrollment", student, Cardinality::ZeroToMany)
 .with(course, Cardinality::ZeroToMany)
 .carry("grade", DataType::Real);

Relationship fields

FieldTypeDescription
idRelationshipIdUnique handle
nameStringDisplay name
endpointsVec<RelationshipEndpoint>At least 2; binary is the common case
attributesVec<AttributeId>Descriptive attributes

RelationshipEndpoint

FieldTypeDescription
entityEntityIdThe participating entity
cardinalityCardinalitySee Cardinality
roleOption<String>Role label (brModelo's Papel)

Cardinality in remodel-core follows the look-here convention from Heuser's textbook: the cardinality written next to entity E describes how many tuples of the other entity participate per tuple of E.

Relationship helpers

let rel = m.relationship(rel_id)?;

rel.is_binary(); // true if exactly 2 endpoints
rel.is_self();   // true if all endpoints share the same entity
rel.is_nary();   // true if 3+ distinct endpoints

Specializations

IS-A hierarchies (generalization / specialization). A specialization links one parent entity to two or more child entities.

let vehicle = m.add_entity("Vehicle");
let car     = m.add_entity("Car");
let truck   = m.add_entity("Truck");

m.add_specialization(
    "kind",
    vehicle,              // parent (supertype)
    vec![car, truck],     // children (subtypes) — at least 2
    SpecializationKind::TOTAL_DISJOINT,
)?;

SpecializationKind

ConstanttotaloverlappingMeaning
PARTIAL_DISJOINTfalsefalseSome parent instances are in no child; each is in at most one
TOTAL_DISJOINTtruefalseEvery parent is in exactly one child
PARTIAL_OVERLAPPINGfalsetrueSome parent instances are in no child; each may be in several
TOTAL_OVERLAPPINGtruetrueEvery parent is in at least one child; may be in several

The choice of SpecializationKind affects which SpecializationStrategy variants are valid during conversion.

Unions

A union (brModelo's Uniao) represents a category entity that is the union of two or more parent entities.

let person    = m.add_entity("Person");
let company   = m.add_entity("Company");
let owner     = m.add_entity("Owner");   // the category

m.add_union("owner_union", vec![person, company], owner)?;

Associative entities

Wraps an existing relationship so it can itself participate in further relationships:

let works_on = m.relate("works_on", employee, Cardinality::ZeroToMany)
                .with(project, Cardinality::ZeroToMany)
                .id();

let assoc = m.add_associative_entity(works_on)?;

Serialization

ConceptualModel derives serde::Serialize and serde::Deserialize, so you can round-trip it through JSON:

let json = serde_json::to_string_pretty(&m)?;
let back: ConceptualModel = serde_json::from_str(&json)?;

This is exactly the format used by .remodel files. See File Format for the full envelope schema.

On this page