| title | Development Standards |
|---|---|
| description | Best practices and coding standards for developing enterprise management software applications using the ObjectStack Protocol |
本文档定义了基于ObjectStack协议开发企业管理软件应用市场的最佳方案和编写规范。 This document defines best practices and coding standards for developing enterprise management software applications using the ObjectStack Protocol.
app-name/
├── docs/ # 📚 Documentation
│ ├── README.md # Master guide
│ ├── 01-data-modeling.md # Data design standards
│ ├── 02-business-logic.md # Business rules
│ ├── 03-ui-design.md # Interface design
│ ├── 04-analytics.md # Reports & dashboards
│ ├── 05-security.md # Security model
│ ├── 06-automation.md # Workflows & flows
│ ├── 07-integration.md # External systems
│ └── 08-ai-capabilities.md # AI & ML features
├── src/
│ ├── domains/ # 📊 Domain Objects (DDD)
│ │ ├── sales/ # Sales domain
│ │ │ ├── *.object.ts # Object definitions
│ │ │ └── *.hook.ts # Event hooks
│ │ ├── service/ # Service domain
│ │ ├── marketing/ # Marketing domain
│ │ ├── products/ # Product catalog
│ │ └── analytics/ # Analytics & BI
│ ├── ui/ # 🎨 User Interface
│ │ ├── apps.ts # Application definitions
│ │ ├── views.ts # List & form views
│ │ ├── actions.ts # Custom actions
│ │ ├── dashboards.ts # Dashboard configs
│ │ └── reports.ts # Report definitions
│ ├── security/ # 🔒 Security
│ │ ├── profiles.ts # User profiles
│ │ ├── permission-sets.ts # Permission sets
│ │ └── sharing-rules.ts # Sharing model
│ ├── automation/ # ⚡ Automation
│ │ ├── flows.ts # Visual flows
│ │ ├── workflows.ts # Workflow rules
│ │ └── triggers.ts # Database triggers
│ ├── ai/ # 🤖 AI & Machine Learning
│ │ ├── agents.ts # AI agents
│ │ ├── rag-pipelines.ts # RAG configs
│ │ └── models.ts # ML models
│ ├── integration/ # 🔗 Integration
│ │ ├── connectors.ts # External connectors
│ │ ├── webhooks.ts # Webhook handlers
│ │ └── apis.ts # Custom APIs
│ └── server/ # 🖥️ Backend
│ ├── apis.ts # REST/GraphQL APIs
│ └── index.ts # Server entry
├── tests/ # 🧪 Tests
│ ├── unit/ # Unit tests
│ ├── integration/ # Integration tests
│ └── e2e/ # End-to-end tests
├── objectstack.config.ts # ⚙️ Main configuration
├── package.json
└── README.md # Project overview
- Domain-Driven Design: Organize objects by business domain, not by type
- Separation of Concerns: Keep data, UI, security, and automation separate
- Clear Naming: Use descriptive folder and file names
- Documentation-First: Comprehensive docs for every module
import { ObjectSchema, Field } from '@objectstack/spec/data';
export const MyObject = ObjectSchema.create({
// ✅ Metadata (Required)
name: 'my_object', // Machine name (snake_case)
label: 'My Object', // Display name
pluralLabel: 'My Objects', // Plural form
icon: 'icon-name', // Icon identifier
description: '...', // Help text
// ✅ Display Configuration
titleFormat: '{field1} - {field2}',
compactLayout: ['field1', 'field2', 'field3'],
// ✅ Fields Definition
fields: {
// Use Field.* helpers
field_name: Field.text({
label: 'Field Label',
required: true,
}),
},
// ✅ Performance
indexes: [
{ fields: ['field_name'], unique: false },
],
// ✅ Capabilities
enable: {
trackHistory: true,
searchable: true,
apiEnabled: true,
files: true,
feeds: true,
},
// ✅ Business Rules
validations: [...],
});
// Note: record-triggered automation is NOT an ObjectSchema field.
// Author it as a lifecycle hook (src/objects/<name>.hook.ts) or a
// top-level `record_change` flow — `workflows: [...]` on the object
// schema is rejected at parse time.| Data Type | Field Type | Use Case |
|---|---|---|
| Short text | Field.text() |
Names, codes, short strings |
| Long text | Field.textarea() |
Descriptions, notes |
| Rich content | Field.markdown() |
Formatted text with links |
| Number | Field.number() |
Quantities, counts |
| Money | Field.currency() |
Prices, revenues |
| Percentage | Field.percent() |
Rates, ratios |
| Date | Field.date() |
Birthdays, deadlines |
| Date+Time | Field.datetime() |
Timestamps, appointments |
| Yes/No | Field.boolean() |
Flags, toggles |
| Choice | Field.select() |
Status, category, type |
| Multiple choice | Field.select({ multiple: true }) |
Tags, skills |
| Reference | Field.lookup() |
Relationships |
| Location | Field.location() |
GPS coordinates |
| Address | Field.address() |
Mailing addresses |
| Color | Field.color() |
Brand colors, themes |
| Auto-number | Field.autonumber() |
Sequential IDs |
| Calculated | Field.formula() |
Computed values |
// ✅ Simple Lookup (Many-to-One)
account: Field.lookup('account', {
label: 'Account',
required: true,
})
// ✅ Filtered Lookup
contact: Field.lookup('contact', {
label: 'Contact',
referenceFilters: {
account: '{account}', // Same account
is_active: true,
}
})
// ✅ Self-Referencing (Hierarchy)
parent_account: Field.lookup('account', {
label: 'Parent Account',
})import { App } from '@objectstack/spec/ui';
App.create({
name: 'app_name',
label: 'App Name',
icon: 'icon-name',
branding: {
primaryColor: '#4169E1',
logo: '/assets/logo.png',
favicon: '/assets/favicon.ico',
},
navigation: [
{
id: 'group_sales',
type: 'group',
label: 'Sales',
children: [
{ id: 'nav_account', type: 'object', objectName: 'account', label: 'Accounts' },
{ id: 'nav_dashboard', type: 'dashboard', dashboardName: 'sales_dashboard' },
]
}
]
});// ✅ Use meaningful names
name: 'sales_dashboard',
// ✅ Group related metrics
widgets: [
// Row 1: KPIs
{ type: 'metric', ... },
// Row 2: Charts
{ type: 'bar', ... },
// Row 3: Lists
{ type: 'table', ... },
]
// ✅ Use grid layout
layout: { x: 0, y: 0, w: 3, h: 2 }A profile is just a PermissionSet with isProfile: true. There is no separate
Profile type — both share the same PermissionSetSchema.
import { definePermissionSet } from '@objectstack/spec/security';
export const MyProfile = definePermissionSet({
name: 'profile_name',
label: 'Profile Label',
isProfile: true,
// Object permissions: <object_name> -> permissions
objects: {
account: {
allowCreate: true,
allowRead: true,
allowEdit: true,
allowDelete: false,
viewAllRecords: false,
modifyAllRecords: false,
},
},
// Field-level security: <object>.<field> -> permissions
fields: {
'account.sensitive_field': { readable: false, editable: false },
},
// App/tab visibility
tabPermissions: {
app_crm: 'default_on', // Shown by default
app_sales: 'visible', // Visible
app_admin: 'hidden', // Hidden
},
});import { defineSharingRule } from '@objectstack/spec/security';
export const MySharingRule = defineSharingRule({
name: 'rule_name',
object: 'object_name',
type: 'criteria', // 'criteria' | 'owner'
// CEL predicate — records matching it are shared
condition: 'record.department == "Sales"',
// Recipient: one of user | group | role | role_and_subordinates | guest
sharedWith: {
type: 'role',
value: 'role_name',
},
accessLevel: 'edit', // 'read' | 'edit' | 'full'
});import { defineFlow } from '@objectstack/spec/automation';
export const MyFlow = defineFlow({
name: 'flow_name',
label: 'Flow Label',
description: 'What this flow does',
type: 'autolaunched', // 'autolaunched' | 'record_change' | 'schedule' | 'screen' | 'api'
status: 'active', // 'draft' | 'active' | 'obsolete' | 'invalid'
variables: [...],
// A flow is a graph of nodes connected by edges (not a linear `steps` list)
nodes: [
{ id: 'start', type: 'start', label: 'Start' },
{ id: 'get_record', type: 'get_record', label: 'Get Record' },
{ id: 'end', type: 'end', label: 'End' },
],
edges: [
{ id: 'e1', source: 'start', target: 'get_record' },
{ id: 'e2', source: 'get_record', target: 'end' },
],
});flows: [
{
name: 'flow_name',
label: 'Flow Name',
type: 'record_change',
status: 'active',
nodes: [
{ id: 'start', type: 'start', label: 'Start' },
{ id: 'update_record', type: 'update_record', label: 'Update Record' },
{ id: 'end', type: 'end', label: 'End' },
],
edges: [
{ id: 'e1', source: 'start', target: 'update_record' },
{ id: 'e2', source: 'update_record', target: 'end' },
],
}
]import type { Agent } from '@objectstack/spec/ai';
export const MyAgent: Agent = {
name: 'agent_name',
label: 'Agent Label',
// Free-form persona string (e.g. "Senior Support Engineer"), not an enum
role: 'Sales Assistant',
instructions: `Clear instructions for the AI...`,
model: {
provider: 'openai', // 'openai' | 'azure_openai' | 'anthropic' | 'local'
model: 'gpt-4',
temperature: 0.7,
maxTokens: 2000,
},
// Agent -> Skill -> Tool architecture; `tools` is the legacy inline fallback
skills: ['case_management'],
tools: [...],
knowledge: { topics: [...], indexes: [...] },
};The standalone
RagPipelineschema was removed and collapsed into the embedding / knowledge-source schemas (packages/spec/src/ai/embedding.zod.ts,knowledge-source.zod.ts). RAG is now configured by declaring knowledge sources that adapters index, then attaching them to an agent viaAgent.knowledge.
import type { KnowledgeSource } from '@objectstack/spec/ai';
export const MyKnowledgeSource: KnowledgeSource = {
id: 'support_docs',
label: 'Support Docs',
adapter: 'adapter_id',
source: {
kind: 'object', // 'object' | 'file' | 'http'
object: 'article',
contentFields: ['title', 'body'],
},
embedding: { /* EmbeddingModel ref */ },
vectorStore: { /* VectorStore ref */ },
};✅ Use camelCase for all TypeScript property names:
maxLength: 255
referenceFilters: {...}
defaultValue: true
primaryColor: '#4169E1'✅ Use snake_case for all data identifiers:
name: 'account_number'
objectName: 'opportunity'
field: 'close_date'
status: 'in_progress'✅ Use snake_case, singular, descriptive:
'account' // ✅ Good
'contact' // ✅ Good
'opportunity' // ✅ Good
'Account' // ❌ Bad (PascalCase)
'accounts' // ❌ Bad (plural)
'opp' // ❌ Bad (abbreviation)✅ Use snake_case, descriptive, no suffixes:
'first_name' // ✅ Good
'annual_revenue' // ✅ Good
'is_active' // ✅ Good (boolean prefix)
'account' // ✅ Good (lookup without _id)
'firstName' // ❌ Bad (camelCase)
'revenue' // ❌ Bad (not descriptive)
'active' // ❌ Bad (boolean without prefix)
'account_id' // ❌ Bad (lookup with _id suffix)Every application must include:
- README.md - Project overview, quick start, features
- docs/data-modeling - Object and field design
- docs/business-logic - Validations, workflows
- docs/security - Security model
- docs/ai-capabilities - AI features (if applicable)
# [Module Name]
Brief description of what this module does.
## Table of Contents
1. [Section 1](#section-1)
2. [Section 2](#section-2)
---
## Section 1
Content with examples...
### Subsection
```typescript
// Code example✅ DO:
- Item 1
- Item 2
❌ DON'T:
- Item 1
- Item 2
---
## 🧪 9. Testing Standards
### Test Structure
tests/ ├── unit/ │ ├── objects/ │ │ └── account.test.ts │ ├── validations/ │ └── workflows/ ├── integration/ │ ├── flows/ │ └── apis/ └── e2e/ └── scenarios/
### Test Pattern
```typescript
import { describe, test, expect } from 'vitest';
describe('Account Object', () => {
test('should create account with valid data', async () => {
const account = await createRecord('account', {
name: 'Test Company',
type: 'prospect',
});
expect(account.name).toBe('Test Company');
});
test('should validate required fields', async () => {
await expect(
createRecord('account', {})
).rejects.toThrow('Name is required');
});
});
- [ ] All objects use
ObjectSchema.create() - [ ] Field names are
snake_case - [ ] Config keys are
camelCase - [ ] Lookups don't have
_idsuffix (useid) - [ ] All validations have clear messages
- [ ] Documentation is up to date
- [ ] Tests pass
- [ ] No security vulnerabilities
- [ ] Code follows conventions
- [ ] All objects have proper indexes
- [ ] Security profiles configured
- [ ] Sharing rules tested
- [ ] AI agents tested
- [ ] Flows tested end-to-end
- [ ] Performance benchmarks met
- [ ] Documentation complete
- [ ] User training materials ready
Follow these standards to build:
- Maintainable code that's easy to understand
- Scalable applications that grow with your business
- Secure systems with enterprise-grade protection
- Intelligent solutions powered by AI
- Well-documented projects that teams can collaborate on
For more details, refer to individual documentation files in the docs/ folder.