Skip to content

Martini Data Model Best Practices

Overview

Following data model best practices helps you create well-structured, maintainable, and efficient data representations in your Martini applications. These practices encompass design principles, naming conventions, structural patterns, and organizational strategies that reduce duplication, improve maintainability, and ensure consistent data handling across your applications.

Applying these best practices becomes critical when building scalable applications where multiple services need to share common data structures or when integrating with databases and external systems that require specific organizational patterns.

What You Will Learn

  • How to apply effective naming conventions for Martini data models and properties
  • How to design atomic properties that represent single pieces of information
  • How to create focused models that represent single business concepts
  • How to use model references to eliminate property duplication
  • How to avoid circular references and handle self-referencing scenarios
  • How to balance database mirroring with API-friendly model design

When To Use This

Apply data model best practices when you need to:

  • Remove duplicate properties shared across models
  • Build maintainable structures that can evolve safely
  • Ensure models work efficiently with databases and APIs
  • Organize complex relationships and avoid circular dependencies
  • Create shared canonical models used across multiple integrations (see Normalizing Data Models)

Prerequisites

Best Practices for Canonical Integration Models

If your data models are shared across integrations (for example, a canonical Customer model used by multiple systems), apply these additional guidelines:

  • Keep canonical models stable and business-oriented: Prefer additive changes (new fields) over renames/removals. Treat the model as a contract shared across teams and services.
  • Preserve source identity: Include fields such as source and lastUpdated, and consider storing a stable cross-system identifier strategy (for example, a canonical id plus a source-specific externalId).
  • Standardize vocabularies: Normalize platform-specific values into a canonical vocabulary (for example, map enabled/disabled and active/inactive into a single standard).
  • Separate “source models” from “canonical models”: Keep platform-specific payload models (inputs/outputs) distinct from canonical entities. Avoid mixing platform-specific fields into canonical models.
  • Prefer reusable reference models for shared structures: Use dedicated models for repeated structures like Address, Money, and ContactDetails rather than duplicating fields across entities.

For implementation steps (creating canonical models, mapping, persistence, and APIs), see Normalizing Data Models.

Data Model Naming Conventions

Consistent naming conventions form the foundation of well-designed data models. While multiple standards exist, following consistent patterns makes your models more maintainable and easier for teams to work with.

Follow these conventions for consistency across Martini applications:

  1. Data Models (in the Navigator view):

    • Naming: PascalCase
    • Examples: UserAccount, OrderHistory, CompanyDetails
  2. Model Properties:

    • Naming: camelCase
    • Examples: firstName, emailAddress, createdDate
  3. Models in Services/Workflows:

    • Naming: camelCase
    • Examples: userAccount, orderHistory, companyDetails

Choosing Descriptive Model Names

Model names should clearly and concisely describe what the model represents:

Good Examples:

  • CustomerOrder - clearly represents an order placed by a customer
  • PaymentTransaction - specific to payment-related transactions
  • UserPreferences - focused on user configuration settings

Avoid Vague Names:

  • Data - too generic, could represent anything
  • Info - doesn't indicate what information
  • Object - provides no context about purpose

The level of specificity depends on your project scope. Consider whether you have distinct entities that need clear differentiation or related models that fall under similar domains.

Structuring Data with Atomic Properties

Atomic properties represent single pieces of information, making your data easier to filter, search, validate, and manipulate. This principle prevents complex data from being stored in single fields where individual components can't be accessed efficiently.

Creating Atomic Properties

Structure properties so each represents one discrete piece of information:

Avoid: Composite Data

1
2
// Multiple data points in one property
fullAddress = "123 Main St, Springfield, IL 12345"

Better: Atomic Structure

1
2
3
4
street = "123 Main St"
city = "Springfield"  
state = "IL"
zipCode = "12345"

Benefits of Atomic Design

Atomic properties provide several operational advantages:

  • Filtering: Query by specific address components (all orders from "IL")
  • Validation: Apply field-specific rules (zipCode format validation)
  • Searching: Enable precise searches (find customers on "Main St")
  • Sorting: Order by individual components (sort by state, then city)
  • Integration: Map individual fields to external systems

Balancing Atomicity with Use Cases

While atomicity is important, consider your specific requirements:

Display-Oriented Models may benefit from computed properties:

1
2
3
4
UserDisplayModel
- firstName
- lastName
- fullName ← computed from firstName + lastName for UI display

Processing-Oriented Models prioritize atomic access:

1
2
3
4
5
UserProcessingModel
- firstName
- lastName
- middleInitial
// No computed fields - raw data for processing

Designing Focused Models

Each data model should represent a single business entity or concept. This focused approach makes models easier to understand, maintain, and evolve as your application requirements change.

Single Responsibility Principle for Models

Design models that serve one clear purpose:

Good: Focused Models

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
CustomerModel
- id
- firstName
- lastName
- email

OrderModel
- id
- customerId
- orderDate
- totalAmount

Avoid: Mixed-Purpose Models

1
2
3
4
5
6
7
// Combines customer and order fields
CustomerOrderModel
- customerId
- customerName
- orderId
- orderDate
- totalAmount

Note: In some cases, a model may legitimately represent a combination of entities, such as a join table or linking entity used for relationships between two concepts. This is acceptable only when the model truly serves a distinct purpose, not as a shortcut to merge unrelated models. Avoid creating a combined model just to simplify usage; focus on modeling each entity clearly.

Identifying Model Boundaries

Use these guidelines to determine proper model scope:

  1. Entity Test: Does this model represent one clear business entity?
  2. Change Test: Do all properties change together, or do some change independently?
  3. Usage Test: Are all properties needed together in most use cases?
  4. Ownership Test: Does one business process or domain own all these properties?

If you answer "no" to any of these questions, consider splitting the model into separate, focused models.

Eliminating Duplication with Model References

Model references allow you to share common data structures across multiple models while maintaining a single source of truth. This eliminates duplication and ensures consistency when shared data changes.

Converting Duplicated Properties to References

Transform duplicated properties into referenced models:

Before: Duplicated Properties

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
EmployeeModel
- id
- firstName
- lastName  
- companyName       ← duplicated
- companyAddress    ← duplicated
- companyPhone      ← duplicated

ContractorModel
- id
- firstName
- lastName
- skillLevel
- companyName       ← duplicated
- companyAddress    ← duplicated  
- companyPhone      ← duplicated

After: Normalized with References

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
CompanyModel
- id
- name
- address
- phone

EmployeeModel
- id
- firstName
- lastName
- company       ← references CompanyModel
  - id          ← inherited
  - name        ← inherited  
  - address     ← inherited
  - phone       ← inherited

ContractorModel
- id
- firstName
- lastName
- skillLevel
- company       ← references CompanyModel
  - id          ← inherited
  - name        ← inherited
  - address     ← inherited
  - phone       ← inherited

Benefits of Model References

Referenced models provide several advantages:

  • Maintainability: Changes to CompanyModel automatically apply to all referencing models
  • Data Integrity: Ensures all models use the same company data structure
  • Reusability: CompanyModel can be referenced by additional models as needed

Preventing Circular References

Circular references occur when models reference each other in a loop, creating infinite dependency chains that can cause stack overflow errors and make models impossible to evolve safely.

Identifying Circular Reference Problems

Problematic: Direct Circular Reference

1
2
3
4
5
6
7
8
9
UserModel
- id
- name  
- department ← references DepartmentModel

DepartmentModel
- id
- name
- manager ← references UserModel (creates circle)

This creates an infinite loop: User → Department → User → Department...

Resolving Circular Dependencies

Solution 1: Remove One Direction

1
2
3
4
5
6
7
8
9
UserModel
- id
- name
- departmentId ← simple ID reference, not full model

DepartmentModel  
- id
- name
- managerId ← simple ID reference, not full model

Solution 2: Create a Junction Model

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
UserModel
- id
- name

DepartmentModel
- id  
- name

UserDepartmentAssignmentModel
- userId
- departmentId  
- role
- isManager

Solution 3: Separate Concerns

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
UserModel
- id
- name
- departmentId

DepartmentModel
- id
- name

ManagerAssignmentModel
- departmentId
- managerId
- assignedDate

Validation Strategy for Circular References

Before implementing model references, trace the dependency chain:

  1. List all models that reference each other
  2. Draw the reference relationships
  3. Check for any loops in the diagram
  4. Redesign relationships to eliminate loops

Managing Self-Referencing Models

Self-referencing models are appropriate for hierarchical data where entities can contain or relate to other entities of the same type. Unlike circular references between different models, self-references are intentional and useful for specific data structures.

Appropriate Self-Reference Scenarios

Hierarchical Comments/Replies:

1
2
3
4
5
CommentModel
- id
- text
- authorId
- replies[] ← model array that references CommentModel

Organizational Structure:

1
2
3
4
DepartmentModel
- id
- name
- subDepartments[] ← model array that references DepartmentModel

Category Hierarchy:

1
2
3
4
CategoryModel
- id
- name  
- childCategories[] ← model array that references CategoryModel

Self-Reference Implementation Guidelines

When working with self-referencing models:

  1. Handle Root Elements: Account for null or missing parent references to support top-level entities.
  2. Control Traversal Depth: Define and enforce maximum depth limits to prevent runaway processing or stack overflows.
  3. Ensure Clear Exit Conditions: Always define termination rules when traversing hierarchies.

Guideline: Self-referencing models are safe and effective when traversal rules are explicit and bounded.

Database Mirroring Strategies

The database mirroring approach creates models that exactly match your database schema structure. While this provides direct mapping benefits, it also introduces challenges when these models are used in APIs or user interfaces.

Database Mirroring Benefits

Advantages of Direct Schema Matching:

  • Direct Mapping from Database Outputs: Because model properties match database table columns, outputs from SQL services and SQL database nodes can be mapped directly to the models without extra transformation.
  • Fewer Models to Manage: One model per table reduces the total number of models in the system, simplifying maintenance and oversight.

Database Mirroring Challenges

Problems with Direct Exposure:

1
2
3
4
5
6
7
8
Database Table: user_accounts  
- user_id
- first_name
- last_name
- internal_risk_score       ← internal business logic
- legacy_migration_flag     ← technical debt
- password_hash             ← security sensitive
- created_timestamp         ← technical field

Direct Mirror Problems:

  • Security Issues: Internal fields exposed in APIs (internal_risk_score, password_hash)
  • User Confusion: Technical fields confuse API consumers (legacy_migration_flag)
  • Breaking Changes: Database changes become API breaking changes
  • Poor User Experience: Database naming conventions don't match user expectations

Hybrid Mirroring Strategy

Use database-mirrored models for internal operations and create clean API models for external use:

Internal/Persistence Model (Database Mirror):

1
2
3
4
5
6
7
8
UserAccountRecord
- user_id
- first_name  
- last_name
- internal_risk_score
- legacy_migration_flag
- password_hash
- created_timestamp

External API Model (User-Friendly):

1
2
3
4
5
6
UserProfile
- id            ← cleaner naming (maps from user_id)
- firstName     ← camelCase (maps from first_name)  
- lastName      ← camelCase (maps from last_name)
- memberSince   ← meaningful name (maps from created_timestamp)
// Security and internal fields excluded

When to Use Each Approach

Use Direct Mirroring When:

  • Building internal administrative tools
  • Creating data processing workflows
  • Security and field exposure aren't concerns

Use Hybrid Strategy When:

  • Building public APIs
  • Working with legacy database schemas
  • Security and data privacy are important
  • User experience and clear naming matter
  • Database structure may change independently

Adding Model Documentation

Comprehensive documentation helps team members or external developers understand model purposes, relationships, and usage patterns. Martini supports rich documentation directly within model definitions.

Using the Comment Meta-Property

Add documentation to models and properties using the Comment meta-property:

Model-Level Documentation:

1
2
3
4
5
6
7
8
9
CompanyModel
Comment: "Represents a business organization that employs users or contracts services. 
Used across HR, billing, and reporting systems."

Properties:
- id (Comment: "Unique identifier, auto-generated by system")
- name (Comment: "Legal business name as registered")  
- address (Comment: "Primary business address for correspondence")
- taxId (Comment: "Federal tax identification number, format: XX-XXXXXXX")

Markdown Support for Structured Documentation

The Comment meta-property supports Markdown formatting for complex documentation:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
## UserAccountModel

**Purpose**: Manages user authentication and profile information

**Usage Contexts**:
- Authentication workflows
- User profile management  
- API response models

**Related Models**:
- `CompanyModel` (via company reference)
- `UserPreferencesModel` (one-to-one relationship)

**Validation Rules**:
- `email`: Must be unique across system
- `username`: 3-50 characters, alphanumeric only
- `status`: One of: active, suspended, pending

Documentation Best Practices

Recommended Information:

The following details are not required but are often useful for improving clarity, onboarding, and long-term maintainability, especially for shared or complex models.

  • Purpose: What the model represents and why it exists
  • Usage Context: Where and how the model is used
  • Relationships: How it connects to other models
  • Validation Rules: Important constraints and business rules
  • Examples: Sample data or usage patterns

Keep Documentation Current:

  • Update comments when model structure changes
  • Review documentation during code reviews
  • Include version information for significant changes
  • Document breaking changes and migration notes

Use Consistent Style:

  • Follow the same documentation format across all models
  • Use clear, concise language
  • Include both technical and business context
  • Link to related documentation when relevant

Versioning and Change Management

Shared data models (especially canonical entities used across integrations and APIs) should be treated as contracts.

  • Prefer additive changes: Add new properties rather than renaming or removing existing ones.
  • Deprecate intentionally: If a property must be retired, check its Deprecated meta-property (checkbox) and add a comment in the Comment meta-property. Keep deprecated properties available for a defined migration period.
  • Avoid silent semantic changes: If the meaning of a property changes, treat that as a breaking change and consider introducing a new property or a versioned model/API.
  • Document changes in comments: For significant changes, add a short change note (what changed, why, and the recommended migration path).

Helpful Resources