Apache Ossie 시맨틱 모델 (ossie-export)
Apache Ossie(incubating)는 분석·BI·AI 플랫폼 사이에 시맨틱 메타데이터 -
dataset(테이블), field(컬럼), relationship(조인), metric(지표) - 를 교환하는 ASF 의 벤더 중립
표준입니다. ElastiCORE blueprint 는 dataset 쪽이 요구하는 것을 이미 다 말하고 있으므로, 같은 내용을 두 번 선언하지 않고
내보냅니다. core spec 0.2.0.dev0(core-spec/spec.md, ossie-schema.json) 기준입니다.
데이터의 뜻을 모르는 에이전트와 BI 도구가 같은 DSL 에서 조인, PLC 같은 코드의 뜻, "매출"이 무엇인지를 받습니다.
켜는 법
build.gradle 에서 - 번들은 그대로, 내보내기만 더함:
elasticore {
architecture = 'hexagonal'
rules = ['ossie-export']
}
또는 도메인의 env.yml 에서 번들과 함께:
publisher:
rules: [hexagonal-springboot, ossie-export]
생성할 때마다 blueprint 루트에 src/main/resources/blueprint/semantic_model.yml 이 다시 만들어집니다. 프로젝트
전체가 한 모델이라 도메인을 넘는 관계가 끊기지 않습니다. schema.yml 과 같은 성격의 사이드카로 늘 다시 생성되고
손으로 고치지 않습니다. 내보내기가 실패해도 빌드 가 아니라 내보내기만 저하됩니다. 스펙이 아직 dev 버전이라 바뀔 수
있습니다.
내보내는 것
| DSL | Ossie | 비고 |
|---|---|---|
엔티티 (@abstract·@embeddable 제외) | datasets[] | source = 테이블 (@table 또는 table.prefix + 이름) |
-- 라벨 | description | |
@id 필드 (복합 키 포함) | primary_key | |
@unique, @Index(unique="true") | unique_keys | |
| 값 필드 | fields[] + expression(컬럼, OSSIE_SQL_2026) + datatype | 아래 타입 표 |
@formula(sql) | expression = 그 SQL | 그 언어 안의 식이면 OSSIE_SQL_2026, 서브쿼리가 들어 있으면 ANSI_SQL 항목 하나 - 아래 dialect |
@extend(Parent) 의 필드 | 자식 dataset 의 fields | 상속된 컬럼도 그 테이블에 있으므로 |
@Embedded 값 객체 | 그 컬럼들 (deliveryAddress.city) | @embedded(prefix) 면 prefix_city |
| enum 필드 | datatype(글자 코드나 이름이면 String, 순번이면 Integer) + ai_context: "Codes: PLC = Placed, ..." | 테이블에는 코드만 있으므로 뜻을 함께 |
| 다른 엔티티 참조 | relationships[] + 조인 컬럼 필드 | 실제 조인 컬럼 |
@password, @writeonly | 내보내지 않음 | 애플리케이션도 읽어 주지 않는 값. omitted_columns 에 기록 |
states:, @access, @owner, @event | custom_extensions (ELASTICORE) | Ossie 에 해당 어휘가 없음 |
@compute, @snapshot, money / quantity | 필드의 custom_extensions | 파생 규칙과 값 타입 |
metrics: | metrics[] (OSSIE_SQL_2026, datatype, description) | 선언한 것만 - 업무 지표 |
@terms(...) (엔티티·필드·지표) | ai_context.synonyms | 업무 용어 |
DataType
Ossie 의 DataType 은 10개입니다: String, Integer, Decimal, Float, Boolean, Date, Time, DateTime(시간대
없음), DateTimeTz(한 순간), Opaque(어휘 밖).
| DSL | Ossie | DSL | Ossie |
|---|---|---|---|
string, text, clob | String | float, double | Float |
int, long, biginteger, quantity | Integer | boolean | Boolean |
decimal, bigdecimal, money | Decimal | date / time | Date / Time |
| enum | 글자 코드나 이름이면 String, 순번이면 Integer (코드는 ai_context) | datetime | DateTime |
json, blob, Object | Opaque | timestamp | DateTimeTz |
is_time 은 쓰지 않습니다 - 스펙이 datatype 에서 기본값을 정하고, 그 답이 이 모델의 답입니다.
dialect
스펙이 등록한 dialect 이름은 열두 개지만 의미를 정의한 것은 OSSIE_SQL_2026 하나입니다(core-spec/expression_language.md:
컬럼 참조, 산술·비교·논리 연산, CASE WHEN, 집계, COALESCE·NULLIF·CAST). ANSI_SQL 을 비롯한 나머지는 벤더
언어 이름이라 소비자가 무시해도 됩니다. 그래서 필드 식(컬럼 이름)과 metrics: 에서 만든 지표 식은 모두
OSSIE_SQL_2026 으로 내보냅니다. @formula 는 식에 따라 갈립니다: unit_price * quantity 처럼 그 언어 안의 식이면
OSSIE_SQL_2026, (select count(*) from ...) 처럼 서브쿼리가 들어 있으면 그 언어로 쓸 수 없으므로(SELECT·FROM·JOIN,
서브쿼리, CTE 는 식에서 지원하지 않음) ANSI_SQL 항목 하나로 둡니다 - 그 SQL 을 이해하는 쪽은 쓰고, 아니면 건너뜁니다.
@formula 필드의 custom_extensions 에는 "formula": "database" 가 붙습니다.
내보내지 않는 것
선언하지 않은 지표: 엔티티 필드에서 매출 공식을 지어내는 것은 추측을 내보내는 일입니다.
실제 출력 - Pet Store
# Apache Ossie (incubating) semantic model - core spec 0.2.0.dev0
# Generated by ElastiCORE from the blueprint DSL - do not edit;
# regenerate with the ossie-export rule set.
version: 0.2.0.dev0
name: petstore
description: "Domain model of petstore, as its ElastiCORE DSL declares it"
ai_context:
instructions: "Each dataset is a table the generated application owns. An enumeration column holds a code; the field's ai_context lists every code and its label. Amounts (money) are decimals with their scale; a quantity is a count that is never negative. Password and write-only columns are left out on purpose. Lifecycles, access rules and derived values are in the ELASTICORE custom extensions."
datasets:
- name: PurchaseOrder
source: ps_purchase_order
primary_key: [order_id]
description: "주문"
ai_context:
synonyms: ["order", "purchase", "구매"]
fields:
- name: status
expression:
dialects:
- dialect: OSSIE_SQL_2026
expression: "status"
datatype: String
ai_context: "Codes: PLC = Placed, SHP = Shipped, CAN = Cancelled"
custom_extensions:
- vendor_name: ELASTICORE
data: "{\"lifecycle\": true, \"enumeration\": \"OrderStatus\"}"
- name: city
expression:
dialects:
- dialect: OSSIE_SQL_2026
expression: "city"
datatype: String
description: "deliveryAddress.city"
- name: total_without_vat
expression:
dialects:
- dialect: OSSIE_SQL_2026
expression: "total_without_vat"
datatype: Decimal
custom_extensions:
- vendor_name: ELASTICORE
data: "{\"compute\": \"sum(lines.subTotal)\", \"value_type\": \"money\"}"
# ...
custom_extensions:
- vendor_name: ELASTICORE
data: "{\"entity\": \"PurchaseOrder\", \"domain\": \"petstore\", \"owner\": \"customerId\", \"access\": {\"*\": \"admin\", \"cancel\": \"owner\", \"create\": \"user\", \"read\": \"owner\"}, \"states\": {\"status\": {\"initial\": \"PLACED\", \"ship\": \"PLACED -> SHIPPED\", \"cancel\": \"PLACED -> CANCELLED\", \"delete\": \"CANCELLED\"}}, \"events\": \"Placed(total), Shipped, Cancelled\", \"omitted_columns\": [\"credit_card_number\"]}"
relationships:
- name: PurchaseOrder_customer
from: PurchaseOrder
to: Customer
from_columns: [customer_id]
to_columns: [customer_id]
metrics:
- name: averageOrder
expression:
dialects:
- dialect: OSSIE_SQL_2026
expression: "(SUM(CASE WHEN PurchaseOrder.status <> 'CAN' THEN PurchaseOrder.total END)) / NULLIF((COUNT(CASE WHEN PurchaseOrder.status <> 'CAN' THEN 1 END)), 0)"
description: "주문 1건 평균 금액"
datatype: Decimal
ai_context:
synonyms: ["AOV", "객단가"]
CreditCard 의 카드 번호(@writeonly)는 dataset 의 필드가 아니라 omitted_columns 에 이름만 남습니다. Pet Store 의
referenceColumn: short 덕분에 조인 컬럼이 customer_id 입니다.
어떻게 검증하나
SemanticModelTest(Pet Store 와elasticore-template-ddd), 빌드마다: 내보낸 모든 테이블·컬럼·기본 키·조인 컬럼이 실제 DB 에 있는지 확인합니다. DB 에 없는 컬럼을 말하는 시맨틱 모델은 누군가의 BI 도구에서 실패하는 쿼리입니다.MetricsTest(Pet Store): 주문을 접수·배송·취소한 뒤, 내보낸 지표마다 그 SQL 을 실제 DB 에서 실행하고(dataset 이름을 테이블로 바꿔서) 엔티티로 따로 계산한 값과 비교합니다 - 매출, 주문 수, 객단가, 배송, 취소, 고객 수.- 두 파일을 Ossie 의
ossie-schema.json으로 검증한 결과는 오류 0 이었습니다(2026-10-03, 직접 실행 - 빌드 테스트에는 없음). elasticore-base의 단위 테스트:OssieExportTest,OssieMetricsTest,OssieSqlTest,OssieFormulaDialectTest.
파일을 쓰는 쪽
semantic_model.yml 은 Ossie 를 읽는 도구를 위한 것입니다: BI 도구는 dataset·조인·지표를 다시 모델링하지 않고 받고,
AI 에이전트는 ai_context(코드와 라벨, 동의어, 모델의 안내)와 지표 정의를 읽어 "지난달 매출" 을 업무의 정의 그대로
PurchaseOrder 와 revenue 로 찾습니다. 시간 축은 order_date 같은 날짜 필드에서 옵니다. 생명주기·권한·파생 값은
ELASTICORE custom extension 으로 함께 갑니다.
이력
- 첫 공개 릴리스(3.0.261015-alpha1) 전 - 스펙에 맞췄습니다: 전에는
semantic_model:배열,expression없음,money를String으로, enum· 임베디드 컬럼 누락, 2.x 식 조인 컬럼, 비밀번호 컬럼 포함이었습니다. - 3.0.261022-rc1 - 식을
OSSIE_SQL_2026으로 내보냅니다(전에는 모두ANSI_SQL이라 읽는 쪽이 전부 건너뛰어도 됐음). 서브쿼리@formula만ANSI_SQL,"formula": "database"추가.count(엔티티)가 bareCOUNT(*)대신 키 컬럼을 셉니다(COUNT(Customer.customer_id)).