npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@objectql/starter-enterprise

v1.8.4

Published

Enterprise-scale starter template for ObjectQL - Advanced metadata organization with modular architecture

Readme

Enterprise-Scale Metadata Organization

This example demonstrates best practices for organizing metadata in large-scale ObjectQL projects, suitable for enterprise applications with hundreds of objects and complex business domains.

📋 Problem Statement

When building large applications, poor metadata organization leads to:

  • Difficulty finding objects - scattered files with no clear structure
  • Merge conflicts - multiple teams editing the same files
  • Unclear ownership - no way to know which team owns which domain
  • Deployment risks - can't deploy modules independently
  • Maintenance burden - hard to understand relationships between objects

🎯 Solution: Domain-Driven Structure

This example shows a modular, domain-based organization pattern that scales to enterprise needs.

src/
├── core/                      # Shared/Foundation Layer
│   ├── objects/              # Base objects used across domains
│   │   ├── user.object.yml
│   │   ├── organization.object.yml
│   │   └── attachment.object.yml
│   ├── i18n/
│   │   ├── en/
│   │   └── zh-CN/
│   └── index.ts
│
├── modules/                   # Business Domain Modules
│   ├── crm/                  # Customer Relationship Module
│   │   ├── objects/
│   │   │   ├── crm_account.object.yml
│   │   │   ├── crm_contact.object.yml
│   │   │   ├── crm_opportunity.object.yml
│   │   │   └── crm_lead.object.yml
│   │   ├── actions/
│   │   │   └── convert-lead.action.ts
│   │   ├── hooks/
│   │   │   └── opportunity.hooks.ts
│   │   ├── i18n/
│   │   │   ├── en/
│   │   │   └── zh-CN/
│   │   ├── README.md
│   │   └── index.ts
│   │
│   ├── hr/                   # Human Resources Module
│   │   ├── objects/
│   │   │   ├── hr_employee.object.yml
│   │   │   ├── hr_department.object.yml
│   │   │   ├── hr_position.object.yml
│   │   │   └── hr_timesheet.object.yml
│   │   ├── actions/
│   │   ├── hooks/
│   │   ├── i18n/
│   │   ├── README.md
│   │   └── index.ts
│   │
│   ├── finance/              # Finance & Accounting Module
│   │   ├── objects/
│   │   │   ├── finance_invoice.object.yml
│   │   │   ├── finance_payment.object.yml
│   │   │   ├── finance_expense.object.yml
│   │   │   └── finance_budget.object.yml
│   │   ├── actions/
│   │   ├── hooks/
│   │   ├── i18n/
│   │   ├── README.md
│   │   └── index.ts
│   │
│   └── project/              # Project Management Module
│       ├── objects/
│       │   ├── project_project.object.yml
│       │   ├── project_task.object.yml
│       │   ├── project_milestone.object.yml
│       │   └── project_timesheet_entry.object.yml
│       ├── actions/
│       ├── hooks/
│       ├── i18n/
│       ├── README.md
│       └── index.ts
│
├── extensions/               # Custom Extensions/Overrides
│   ├── user.extension.object.yml
│   └── README.md
│
├── shared/                   # Shared Utilities
│   ├── constants.ts
│   ├── validators.ts
│   └── utils.ts
│
└── index.ts                  # Application Entry Point

🏗️ Architecture Principles

1. Separation of Concerns

Each module is self-contained with its own:

  • Object definitions (.object.yml)
  • Business logic (actions & hooks)
  • Translations (i18n)
  • Documentation

2. Clear Dependencies

Application Layer (modules/*)
        ↓
  Foundation Layer (core/*)
        ↓
   External Plugins

3. Team Ownership

Each module can be owned by a different team:

  • modules/crm/ → Sales Team
  • modules/hr/ → HR Team
  • modules/finance/ → Finance Team

4. Independent Deployment

Modules can be:

  • Developed in parallel
  • Tested independently
  • Deployed as feature flags
  • Extracted to separate packages

📦 Module Structure

Each module follows this pattern:

modules/[domain]/
├── objects/              # Domain object definitions
├── actions/              # Custom actions (*.action.ts)
├── hooks/                # Lifecycle hooks (*.hooks.ts)
├── i18n/                 # Module-specific translations
│   ├── en/
│   └── zh-CN/
├── README.md             # Module documentation
└── index.ts              # Module exports

🔗 Object Naming Conventions

Prefixing Strategy

For large projects, consider prefixing object names:

# ❌ Bad: Name collision risk
name: task

# ✅ Good: Clear module ownership
name: project_task

When to prefix:

  • ✅ When multiple modules might have similar concepts
  • ✅ In multi-tenant or plugin architectures
  • ❌ When it's clearly a core shared object (e.g., user, organization)

File Naming

[object_name].object.yml     # Object definition
[object_name].action.ts      # Actions for this object
[object_name].hooks.ts       # Hooks for this object
[object_name].data.yml       # Seed data (optional)

🌐 Internationalization at Scale

Three-Layer Strategy

  1. Core Layer (core/i18n/)

    • Shared objects (user, organization)
    • Common UI labels
  2. Module Layer (modules/[domain]/i18n/)

    • Domain-specific objects
    • Business terminology
  3. Extension Layer (extensions/i18n/)

    • Customer-specific customizations
    • Regional variants

Example Structure

core/i18n/
  en/
    user.json
    organization.json
  zh-CN/
    user.json
    organization.json

modules/crm/i18n/
  en/
    account.json
    opportunity.json
  zh-CN/
    account.json
    opportunity.json

🔐 Index & Performance Strategy

Field-Level Indexes (Simple)

For single-column lookups:

fields:
  email:
    type: text
    unique: true    # Creates unique index
  status:
    type: select
    index: true     # Creates regular index

Composite Indexes (Advanced)

Define at object root for multi-column queries:

indexes:
  # For query: WHERE status = 'open' ORDER BY created_at DESC
  status_created_idx:
    fields: [status, created_at]
  
  # For unique constraint: UNIQUE(company_id, email)
  company_email_unique:
    fields: [company_id, email]
    unique: true

Index Strategy by Module

High-Traffic Modules (CRM, Finance):

  • Add indexes on every filter field
  • Use composite indexes for common query patterns
  • Monitor query performance regularly

Low-Traffic Modules (HR, Admin):

  • Basic indexes on primary lookup fields
  • Add more as needed based on usage

🧩 Extension Pattern

Use extensions to customize objects without modifying core definitions:

Core Definition (core/objects/user.object.yml):

name: user
fields:
  name: { type: text }
  email: { type: text }

Extension (extensions/user.extension.object.yml):

name: user  # Same name triggers merge
fields:
  # Add custom field
  employee_id:
    type: text
    label: Employee ID
  
  # Override existing field
  email:
    required: true
    unique: true

🧪 Testing Strategy

Unit Tests

Test individual object schemas:

// modules/crm/objects/__tests__/account.test.ts
describe('Account Object', () => {
  it('should have required fields', () => {
    const account = loadObject('account');
    expect(account.fields.name.required).toBe(true);
  });
});

Integration Tests

Test module interactions:

// modules/crm/__tests__/integration.test.ts
describe('CRM Module', () => {
  it('should convert lead to opportunity', async () => {
    // Test cross-object logic
  });
});

📊 Real-World Size Reference

| Project Size | Objects | Modules | Teams | Structure | |--------------|---------|---------|-------|-----------| | Small (Startup) | 10-30 | 1-2 | 1 | Flat /objects/ | | Medium (Scale-up) | 30-100 | 3-5 | 2-3 | /modules/ by domain | | Large (Enterprise) | 100-500 | 8-15 | 5-10 | /modules/ + /plugins/ | | Very Large (Platform) | 500+ | 15+ | 10+ | Monorepo with packages |

🚀 Migration Path

From Flat to Modular

  1. Analyze - Group objects by business domain
  2. Create - Create module directories
  3. Move - Relocate objects to appropriate modules
  4. Test - Verify imports and references still work
  5. Document - Update README files

Gradual Approach

You don't have to reorganize everything at once:

src/
├── objects/          # Legacy flat structure (deprecated)
├── modules/          # New modular structure
│   └── crm/          # Start with one module
└── index.ts          # Loads from both

💡 Pro Tips

  1. Start Simple - Don't over-engineer for 10 objects. Use modules when you hit 30-50 objects.

  2. Document Boundaries - Each module README should explain:

    • What business domain it covers
    • Key objects and relationships
    • Team ownership
    • Dependencies on other modules
  3. Avoid Circular Dependencies - Use shared objects in core/ to break cycles.

  4. Version Control - Use .gitignore to exclude generated files:

    dist/
    *.generated.ts
    node_modules/
  5. Code Generation - Run objectql generate to create TypeScript types for each module separately.

📚 See Also

🤝 Contributing

This is a living example. If you have suggestions for enterprise-scale patterns, please open an issue or PR!