Relationships Between Core Concepts
One of the most powerful aspects of Open Register is how its core concepts interact with each other. Understanding these relationships is key to effectively designing and using the system.
Relationship Overview
The core entities in Open Register - Registers, Schemas, Objects, Files, Sources, and Events - form an interconnected system:
graph TD
Register[Registers] -->|contain| Object[Objects]
Register -->|support| Schema[Schemas]
Object -->|conform to| Schema
Register -->|stored in| Source[Sources]
Object -->|stored in| Source
Schema -->|stored in| Source
Object -->|relate to| Object
Object -->|has| File[Files]
File -->|stored in| Source
Schema -->|defines| File
Object -->|trigger| Event[Events]
Register -->|trigger| Event
Schema -->|trigger| Event
File -->|trigger| Event
Register-Schema Relationship
Registers and schemas have a many-to-many relationship:
How Registers Use Schemas
- A register defines which schemas it supports through its
schemas
property - This property contains an array of schema IDs
- Only objects conforming to one of these schemas can be stored in the register
Example
// Register definition
{
"id": "person-register",
"title": "Person Register",
"schemas": ["person", "address", "contact-details"],
// other properties...
}
This register supports three schemas: "person", "address", and "contact-details".
Design Considerations
When designing the relationship between registers and schemas:
- Group Related Schemas: Include schemas that logically belong together in the same register
- Consider Access Control: Schemas in the same register often share similar access patterns
- Balance Specificity and Flexibility: Too many schemas can make a register unfocused, while too few can limit its usefulness
Register-Object Relationship
Registers and objects have a one-to-many relationship:
How Registers Contain Objects
- Each object belongs to exactly one register (specified by its
register
property) - A register can contain many objects
- Objects in a register must conform to one of the register's supported schemas
Example
// Object definition
{
"id": "person-12345",
"register": "person-register",
"schema": "person",
// other properties...
}
This object belongs to the "person-register" and conforms to the "person" schema.
Design Considerations
When designing the relationship between registers and objects:
- Logical Grouping: Group objects that are commonly accessed together in the same register
- Performance: Consider query patterns when deciding which objects go in which register
- Scalability: Plan for how registers will grow over time
Schema-Object Relationship
Schemas and objects have a one-to-many relationship:
How Schemas Define Objects
- Each object conforms to exactly one schema (specified by its
schema
property) - A schema can be used by many objects
- The schema defines the structure, validation rules, and relationships for the object
Example
// Schema definition
{
"id": "person",
"properties": {
"firstName": { "type": "string" },
"lastName": { "type": "string" },
// other properties...
}
}
// Object data conforming to the schema
{
"firstName": "John",
"lastName": "Doe"
}
Schema Evolution and Objects
When schemas evolve, it affects objects:
- Minor Changes: Adding optional fields or relaxing constraints typically doesn't affect existing objects
- Major Changes: Adding required fields or changing field types may require updating existing objects
- Versioning: Schema versioning helps manage these changes over time
Object-File Relationship
Objects and files have a one-to-many relationship:
How Objects Contain Files
- Files are attached to objects through the object's
files
property - An object can have multiple files
- Files inherit permissions from their parent object
Example
// Object with files
{
"id": "contract-12345",
"files": [
{
"id": "file-67890",
"name": "contract.pdf",
"contentType": "application/pdf",
"size": 1245678,
"url": "/api/files/file-67890"
},
{
"id": "file-54321",
"name": "signature.jpg",
"contentType": "image/jpeg",
"size": 45678,
"url": "/api/files/file-54321"
}
],
// other properties...
}
Design Considerations
When designing the relationship between objects and files:
- File Organization: Consider how files should be organized and named
- Metadata: Define what metadata should be stored with files
- Versioning: Determine how file versions should be managed
- Access Control: Plan how file permissions should be handled
Schema-File Relationship
Schemas can define expectations for file attachments:
How Schemas Define Files
- Schemas can specify file properties like allowed types and maximum sizes
- Schemas can define required file attachments
- Schemas can specify validation rules for file metadata
Example
// Schema with file definitions
{
"id": "contract",
"properties": {
"title": { "type": "string" },
"files": {
"type": "array",
"items": {
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": ["contract", "signature", "attachment"]
},
"contentType": {
"type": "string",
"pattern": "^application/pdf|image/jpeg|image/png$"
},
"maxSize": {
"type": "integer",
"maximum": 10485760 // 10MB
}
}
},
"minItems": 1
}
},
"required": ["title", "files"]
}
Design Considerations
When designing the relationship between schemas and files:
- Validation Rules: Define appropriate validation rules for files
- Required Files: Determine which files should be required
- File Types: Specify allowed file types and formats
- Size Limits: Set appropriate size limits for different file types
Entity-Event Relationships
Events are triggered by and relate to other entities:
How Entities Trigger Events
- Changes to objects, registers, schemas, and files trigger events
- Events carry data about the entity and the change
- Events enable other components to react to changes
Example
// Event triggered by object creation
class ObjectCreatedEvent extends Event {
private ObjectEntity $object;
public function __construct(ObjectEntity $object) {
parent::__construct();
$this->object = $object;
}
public function getObject(): ObjectEntity {
return $this->object;
}
}
Design Considerations
When designing the relationship between entities and events:
- Event Granularity: Determine the right level of detail for events
- Event Data: Include enough data in events for listeners to be useful
- Event Naming: Use consistent naming conventions for events
- Event Documentation: Document when events are triggered and what data they contain
Source Relationships
Sources have relationships with all other entities:
How Sources Store Data
- Sources provide the storage backend for registers, schemas, objects, and files
- Different entities can use different sources
- Source configuration affects performance, scalability, and reliability
Example
// Register referencing a source
{
"id": "person-register",
"source": "primary-source",
// other properties...
}
// Source definition
{
"id": "primary-source",
"databaseUrl": "mongodb://localhost:27017/openregister",
"type": "mongodb",
// other properties...
}
Design Considerations
When designing source relationships:
- Performance: Match source types to access patterns
- Scalability: Plan for growth in data volume
- Reliability: Consider redundancy and backup requirements
- Security: Isolate sensitive data in separate sources if needed
Object-Object Relationships
Objects can have relationships with other objects:
How Objects Relate to Each Other
- Objects can reference other objects through their
relations
property - These relationships can cross register boundaries
- Relationships have a type that defines their meaning
Example
// Object with relationships
{
"id": "person-12345",
"relations": [
{
"type": "spouse",
"target": "person-67890"
},
{
"type": "employer",
"target": "organization-54321"
}
],
// other properties...
}
Types of Relationships
- Hierarchical: Parent-child relationships (e.g., department-employee)
- Associative: Peer relationships (e.g., spouse, colleague)
- Compositional: Whole-part relationships (e.g., product-component)
- Referential: Simple references (e.g., author of a document)
Design Considerations
When designing object relationships:
- Relationship Types: Define clear relationship types with specific meanings
- Bidirectionality: Consider whether relationships need to be navigable in both directions
- Integrity: Maintain referential integrity when objects are deleted
- Performance: Consider query patterns when designing relationships
Practical Examples
Example 1: Document Management System with Events
graph TD
DocumentRegister[Document Register] -->|supports| DocumentSchema[Document Schema]
DocumentRegister -->|contains| DocumentObject[Document Objects]
DocumentObject -->|conform to| DocumentSchema
DocumentObject -->|has| DocumentFile[Document Files]
DocumentFile -->|stored in| FileSource[File Source]
DocumentObject -->|has metadata| MetadataObject[Metadata Objects]
MetadataObject -->|conform to| MetadataSchema[Metadata Schema]
DocumentRegister -->|stored in| DocumentSource[Document Source]
DocumentObject -->|triggers| ObjectEvents[Object Events]
DocumentFile -->|triggers| FileEvents[File Events]
ObjectEvents -->|listened by| WorkflowSystem[Workflow System]
FileEvents -->|listened by| NotificationSystem[Notification System]
Example 2: Product Catalog System with Integration
graph TD
ProductRegister[Product Register] -->|supports| ProductSchema[Product Schema]
ProductRegister -->|supports| CategorySchema[Category Schema]
ProductRegister -->|contains| ProductObject[Product Objects]
ProductObject -->|conform to| ProductSchema
ProductObject -->|belongs to| CategoryObject[Category Objects]
CategoryObject -->|conform to| CategorySchema
ProductObject -->|has| ProductImage[Product Images]
ProductImage -->|stored in| MediaSource[Media Source]
ProductRegister -->|stored in| ProductSource[Product Source]
ProductObject -->|triggers| ProductEvents[Product Events]
ProductEvents -->|listened by| ExternalSystem[External System]
ProductEvents -->|listened by| SearchIndex[Search Index]
Best Practices for Managing Relationships
- Document Relationships: Clearly document the relationships between entities
- Consistent Naming: Use consistent naming conventions for relationship types
- Validate Relationships: Ensure relationships reference valid entities
- Consider Performance: Design relationships with query performance in mind
- Plan for Evolution: Design relationships that can evolve over time
- Maintain Integrity: Implement processes to maintain referential integrity
- File Organization: Develop clear strategies for organizing and managing files
- Event Design: Design events to carry sufficient context for listeners
- Listener Independence: Ensure event listeners can operate independently
Conclusion
The relationships between Open Register's core concepts create a flexible yet structured system for managing data. By understanding these relationships, you can design effective data models that leverage the full power of the system while maintaining data quality and performance.