Entity 스펙 스키마
이 페이지는 Fascia의 Entity(비즈니스 객체) 스펙에 대한 전체 JSON Schema를 정의합니다. Entity는 Fascia의 핵심 비즈니스 객체 추상화로, 필드, 관계, 상태 머신(Status Machine), 불변식(Invariant)을 캡슐화합니다. 데이터베이스 저장 모델(테이블, 컬럼, 인덱스)은 Entity 스펙에서 자동으로 도출됩니다.
JSON Schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"required": ["name", "version", "description", "fields", "statusMachine"],
"properties": {
"name": {
"type": "string",
"pattern": "^[A-Z][a-zA-Z0-9]*$",
"description": "PascalCase entity name"
},
"version": {
"type": "integer",
"minimum": 1
},
"description": {
"type": "string"
},
"fields": {
"type": "object",
"minProperties": 1,
"additionalProperties": { "$ref": "#/$defs/Field" }
},
"relationships": {
"type": "object",
"additionalProperties": { "$ref": "#/$defs/Relationship" }
},
"statusMachine": { "$ref": "#/$defs/StatusMachine" },
"invariants": {
"type": "array",
"items": { "$ref": "#/$defs/Invariant" }
},
"rowLevelAccess": {
"type": "boolean",
"default": false
},
"ownerField": {
"type": "string"
}
}
}
최상위 속성
| 속성 | 타입 | 필수 | 설명 |
|---|---|---|---|
name | string | 예 | PascalCase Entity 이름 (예: Reservation, PaymentRecord). ^[A-Z][a-zA-Z0-9]*$ 패턴을 따라야 합니다. |
version | integer | 예 | 불변 버전 번호. 1부터 시작하며, 변경 시마다 증가합니다. |
description | string | 예 | Entity의 목적에 대한 사람이 읽을 수 있는 설명입니다. |
fields | object | 예 | Entity에 정의된 사용자 필드입니다. 최소 하나의 필드가 필요합니다. Field 참조. |
relationships | object | 아니오 | 다른 Entity와의 관계를 정의합니다. Relationship 참조. |
statusMachine | object | 예 | 허용된 상태와 전이를 정의하는 상태 머신입니다. StatusMachine 참조. |
invariants | array | 아니오 | 항상 참 이어야 하는 비즈니스 규칙입니다. Invariant 참조. |
rowLevelAccess | boolean | 아니오 | true로 설정 시, 관리자가 아닌 사용자는 자신이 소유한 행에만 접근할 수 있습니다. 기본값은 false입니다. |
ownerField | string | 아니오 | 소유자 User Entity를 참조하는 필드 이름입니다. rowLevelAccess가 true일 때 필수입니다. |
정의
Field
fields 객체의 각 항목은 Entity의 타입이 지정된 데이터 속성을 정의합니다.
{
"type": "object",
"required": ["type"],
"properties": {
"type": {
"enum": ["string", "number", "boolean", "date", "datetime", "enum", "uuid", "json", "reference"]
},
"required": { "type": "boolean", "default": false },
"unique": { "type": "boolean", "default": false },
"indexed": { "type": "boolean", "default": false },
"default": {},
"enumValues": {
"type": "array",
"items": { "type": "string" },
"description": "Required when type is 'enum'"
},
"referenceTo": {
"type": "string",
"description": "Target entity name. Required when type is 'reference'."
},
"description": { "type": "string" }
}
}
| 속성 | 타입 | 필수 | 설명 |
|---|---|---|---|
type | enum | 예 | string, number, boolean, date, datetime, enum, uuid, json, reference 중 하나입니다. |
required | boolean | 아니오 | 해당 필드에 값이 반드시 있어야 하는지 여부입니다. 기본값은 false입니다. |
unique | boolean | 아니오 | 유니크 제약 조건 적용 여부입니다. 기본값은 false입니다. |
indexed | boolean | 아니오 | 쿼리 성능을 위한 인덱스 적용 여부입니다. 기본값은 false입니다. |
default | any | 아니오 | 생성 시 필드 값이 제공되지 않을 때 적용되는 기본값입니다. |
enumValues | string[] | 조건부 | 허용되는 값 목록입니다. type이 "enum"일 때 필수입니다. |
referenceTo | string | 조건부 | 대상 Entity의 PascalCase 이름입니다. type이 "reference"일 때 필수입니다. |
description | string | 아니오 | 필드에 대한 사람이 읽을 수 있는 설명입니다. |
허용되는 필드 타입
| 타입 | 설명 | 데이터베이스 매핑 |
|---|---|---|
string | 텍스트 데이터 | TEXT 또는 VARCHAR |
number | 숫자 데이터 (정수 또는 소수) | NUMERIC |
boolean | 참/거짓 | BOOLEAN |
date | 시간 없는 날짜 | DATE |
datetime | 시간대가 포함된 날짜와 시간 | TIMESTAMPTZ |
enum | 고정된 문자열 값 집합 중 하나 | CHECK 제약 조건이 있는 TEXT |
uuid | UUID 식별자 | UUID |
json | 임의의 JSON 데이터 | JSONB |
reference | 다른 Entity에 대한 외래 키 | UUID (FK 제약 조건) |
Relationship
Entity 간의 관계를 정의합니다.
{
"type": "object",
"required": ["type", "target"],
"properties": {
"type": { "enum": ["hasOne", "hasMany", "belongsTo", "manyToMany"] },
"target": { "type": "string", "description": "Target entity name" },
"foreignKey": { "type": "string" },
"through": { "type": "string", "description": "Join table name for manyToMany" }
}
}
| 속성 | 타입 | 필수 | 설명 |
|---|---|---|---|
type | enum | 예 | hasOne, hasMany, belongsTo, manyToMany 중 하나입니다. |
target | string | 예 | 관련 Entity의 PascalCase 이름입니다. |
foreignKey | string | 아니오 | 외래 키 컬럼 이름입니다. 생략 시 자동으로 도출됩니다. |
through | string | 아니오 | 조인 테이블 이름입니다. manyToMany 관계에서 필수입니다. |
관계 유형
| 유형 | 카디널리티 | 외래 키 위치 |
|---|---|---|
hasOne | 1:1 | 대상 Entity에 위치 |
hasMany | 1:N | 대상 Entity에 위치 |
belongsTo | N:1 | 현재 Entity에 위치 |
manyToMany | N:M | 별도의 조인 테이블 (through로 지정) |
StatusMachine
모든 Entity는 상태 머신을 정의해야 합니다. 단순한 경우(예: active에서 deleted로)에도 마찬가지입니다. 상태 머신은 허용되는 상태 전이를 제어합니다.
{
"type": "object",
"required": ["states", "initialState", "transitions"],
"properties": {
"states": {
"type": "array",
"items": { "type": "string" },
"minItems": 2
},
"initialState": { "type": "string" },
"transitions": {
"type": "array",
"items": { "$ref": "#/$defs/Transition" },
"minItems": 1
}
}
}
| 속성 | 타입 | 필수 | 설명 |
|---|---|---|---|
states | string[] | 예 | 가능한 모든 상태입니다. 최소 2개의 상태가 필요합니다. lowercase_snake 네이밍을 사용합니다 (예: pending, in_progress). |
initialState | string | 예 | 새로 생성된 레코드에 할당되는 상태입니다. states 배열에 포함되어야 합니다. |
transitions | Transition[] | 예 | 허용되는 상태 전이 목록입니다. 최소 하나의 전이가 필요합니다. |
규칙:
- 모든 Entity는 상태 머신을 가져야 합니다.
- 전이는 명시적이어야 합니다 -- 와일드카드 전이는 허용되지 않습니다.
initialState는 정의된states중 하나여야 합니다.
Transition
상태 머신 내에서 허용되는 상태 변경을 정의합니다.
{
"type": "object",
"required": ["from", "to"],
"properties": {
"from": { "type": "string" },
"to": { "type": "string" },
"guard": {
"type": "string",
"description": "Value DSL boolean expression. Transition only allowed if true."
}
}
}
| 속성 | 타입 | 필수 | 설명 |
|---|---|---|---|
from | string | 예 | 출발 상태입니다. states 배열에 포함되어야 합니다. |
to | string | 예 | 도착 상태입니다. states 배열에 포함되어야 합니다. |
guard | string | 아니오 | Value DSL 불리언 표현식입니다. 이 표현식이 true로 평가될 때만 전이가 허용됩니다. |
Invariant
Entity에 대해 항상 참이어야 하는 비즈니스 규칙입니다. Invariant(불변식)는 Flow 실행 후, 트랜잭션 커밋 전에 검사됩니다 (Execution Contract의 6단계).
{
"type": "object",
"required": ["name", "expression"],
"properties": {
"name": { "type": "string", "description": "camelCase assertion-style name" },
"expression": { "type": "string", "description": "Value DSL boolean expression" },
"message": { "type": "string", "description": "Error message when violated" }
}
}
| 속성 | 타입 | 필수 | 설명 |
|---|---|---|---|
name | string | 예 | camelCase, 단언문 스타일 이름입니다 (예: orderTotalPositive, endDateAfterStart). |
expression | string | 예 | Value DSL 불리언 표현식입니다. Entity가 유효하려면 true로 평가되어야 합니다. |
message | string | 아니오 | Invariant가 위반되었을 때 반환되는 사람이 읽을 수 있는 에러 메시지입니다. |
시스템 필드
모든 Entity는 다음 시스템 필드를 자동으로 포함합니다. fields 객체에 이 필드들을 직접 정의하지 마십시오 -- 플랫폼이 자동으로 주입합니다.
| 필드 | 타입 | 설명 |
|---|---|---|
id | uuid | 기본 키. 생성 시 자동 생성됩니다. |
createdAt | datetime | 생성 시 자동으로 설정되는 타임스탬프입니다. |
updatedAt | datetime | 매 업데이트 시 자동으로 설정되는 타임스탬프입니다. |
deletedAt | datetime (nullable) | 레코드가 소프트 삭제될 때 설정됩니다. 활성 레코드의 경우 null입니다. |
version | integer | 낙관적 잠금 카운터입니다. 1에서 시작하며, 매 업데이트 시 증가합니다. |
예시
완전한 Reservation Entity 스펙입니다:
{
"name": "Reservation",
"version": 1,
"description": "A vehicle reservation made by a customer",
"fields": {
"customerId": {
"type": "reference",
"referenceTo": "Customer",
"required": true,
"indexed": true,
"description": "The customer who made the reservation"
},
"vehicleId": {
"type": "reference",
"referenceTo": "Vehicle",
"required": true,
"description": "The reserved vehicle"
},
"startDate": {
"type": "datetime",
"required": true,
"description": "Reservation start date and time"
},
"endDate": {
"type": "datetime",
"required": true,
"description": "Reservation end date and time"
},
"totalPrice": {
"type": "number",
"required": true,
"description": "Total price for the reservation period"
},
"notes": {
"type": "string",
"description": "Optional notes from the customer"
}
},
"relationships": {
"customer": {
"type": "belongsTo",
"target": "Customer",
"foreignKey": "customerId"
},
"vehicle": {
"type": "belongsTo",
"target": "Vehicle",
"foreignKey": "vehicleId"
},
"payments": {
"type": "hasMany",
"target": "Payment"
}
},
"statusMachine": {
"states": ["pending", "confirmed", "in_progress", "completed", "cancelled"],
"initialState": "pending",
"transitions": [
{ "from": "pending", "to": "confirmed" },
{ "from": "pending", "to": "cancelled" },
{ "from": "confirmed", "to": "in_progress", "guard": "now() >= startDate" },
{ "from": "confirmed", "to": "cancelled" },
{ "from": "in_progress", "to": "completed", "guard": "now() >= endDate" }
]
},
"invariants": [
{
"name": "endDateAfterStart",
"expression": "endDate > startDate",
"message": "End date must be after start date"
},
{
"name": "totalPricePositive",
"expression": "totalPrice > 0",
"message": "Total price must be positive"
}
],
"rowLevelAccess": true,
"ownerField": "customerId"
}
관련 문서
- Tool 스펙 스키마 -- Entity를 대상으로 동작하는 실행 가능한 서버 로직을 정의합니다
- Policy 스펙 스키마 -- 설계 시 적용되는 규칙을 정의합니다
- Risk 분류 규칙 -- Tool 스펙의 안전성 평가 방법을 설명합니다