본문으로 건너뛰기
ElastiCORE 3.0 은 릴리스 후보(RC)입니다: Maven Central 의 3.0.261022-rc1. 2.2 라인 프로젝트는 2.2 문서를 보세요.
버전: 3.0

규칙 API 레퍼런스

모든 타입은 io.elasticore.base.model.pub.rules 패키지에 있습니다. 규칙 모듈은 elasticore-base 를 compileOnly 로 받습니다(첫 룰셋). 여기에는 팀 규칙에 쓰는 것만 모았습니다. 나머지 공개 메서드는 ElastiCORE 자신의 규칙이 쓰는 것이라 버전 사이에 바뀔 수 있습니다.

import io.elasticore.base.model.pub.rules.*;
import static io.elasticore.base.model.pub.rules.Ctx.ctx;

@Rule 과 CodeRules​

CodeRules 는 구현할 메서드가 없는 표식 인터페이스입니다. @Rule 이 붙은 메서드 하나가 규칙 하나입니다. 메서드는 void 이고 접근 제어자는 없어도 됩니다. 클래스에는 인자 없는 생성자가 있어야 합니다.

속성기본값뜻
value(필수)규칙 id. -PlistRules 에 나오고 suppress 에 씁니다. 팀 접두사를 붙이세요 (ps-…, acme-…)
target"entity"기여하는 파일 종류 (대상 파일)
order0같은 단계 안에서 작은 것부터. 같으면 등록 순서
phaseHEAD파일 단위 규칙이 필드 규칙보다 먼저(HEAD)인지 나중(TAIL)인지. 필드 규칙에는 의미 없음

메서드 모양이 불리는 때를 정합니다​

파라미터불리는 때target 예
(EntitySpec e)그 엔티티의 파일을 만들 때마다 한 번entity, dto, srchdto, repository, control …
(EntitySpec e, FieldSpec f)그 파일의 필드마다 한 번entity, dto, srchdto
(DomainSpec d)도메인 단위 파일마다 한 번mapper, apiErrors, 내가 만든 도메인 파일
(EnumSpec e)열거형마다 한 번enum
(PortSpec p)외부 연동 포트마다 한 번port
(ProtoServiceSpec p)proto 포트마다 한 번proto

한 파일 안의 순서는 HEAD 파일 규칙 → 필드 규칙(필드마다) → TAIL 파일 규칙입니다. 팀 규칙은 각 단계에서 ElastiCORE 규칙이 모두 끝난 뒤에 돌기 때문에, 기본 규칙이 붙인 것을 빼거나 고칠 수 있고 order 는 팀 규칙끼리의 순서가 필요할 때만 씁니다.

모델 읽기​

EntitySpec - 엔티티, dto.yml 의 DTO, 검색 DTO​

메서드DSL 에서예 (펫스토어)
getName()엔티티 이름"PurchaseOrder"
domainName(), domainClassName()도메인"petstore", "Petstore"
hasMeta("x"), metaValue("x")meta: 의 @x, @x(값) 의 값metaValue("expose") → "20"
metaValues("x")같은 단어가 여러 번일 때 모두
metaText("access")@access(read=public, update=owner) 처럼 짝으로 된 값의 원문
aggregateRoot()루트인가 (멤버·값 객체면 false)PurchaseOrder → true
containedIn()멤버면 소속 루트 이름OrderLine → "PurchaseOrder"
isSplitForBehavior(e.getName())두 파일로 나뉘는 루트인가 (XxxBase + 개발자 클래스)
fields(), allFields(), dtoFields(), pkFields()자기 필드 / 상속 포함 / DTO 쪽 / 식별자
events(), eventClassName(ev), eventPackage()@event(...) 이름들, 생성된 이벤트 클래스 이름·패키지[Placed, Shipped, Cancelled]
commands() → name(), params()commands: 의 업무 명령
queries() → QuerySpecrepository.yml 의 메서드 (target repository)
pkg("dto")env.yml 네임스페이스 키의 패키지
config("ps.maxPageSize")그 도메인 env.yml 의 elasticore.config.ps.maxPageSize, 없으면 null
tablePrefix(), apiPath()config.table.prefix, config.apiPath"ps_", "/api"

FieldSpec - 필드 한 줄​

메서드DSL 에서예
name(), capitalName()필드 이름, 접근자용"email", "Email"
typeName(), coreItemType()선언 타입 / List<X> 의 X
required()!
hasDsl("x"), hasDslAny("x", "y")@x 가 있나 (대소문자 무시)hasDsl("pii")
dslValue("x"), dslValueAny(...)@x(값) 의 값dslValue("search") → "like"
dslProperty("onetomany", "mappedBy")@OneToMany(mappedBy="parent") 처럼 이름 붙은 속성
maxLength()string(100) 또는 @length (없으면 0)
label(), description()-- 설명, @label
dbColumnName()@column 또는 snake_case 이름
isList(), isEntityRef(), isBaseType(), isStringType()컬렉션 / 다른 엔티티 참조 / 기본 타입 / 문자열
isMoney(), isDate(), isDateTime()money, date, datetime

DomainSpec - 도메인 하나​

메서드뜻
getName(), className()"petstore", "Petstore"
entities()도메인의 모든 엔티티
namespace("event")namespace 키의 패키지 (직접 더한 키 포함), 없으면 null
config("a.b"), isOn("a.b")env.yml 설정 / "true" 인가

EnumSpec - enum.yml​

메서드뜻
getName()"OrderStatus"
constants() → name(), values()값들과 각 값의 열 (["DRF", "초안"])
fields()fields: 의 열 이름

PortSpec - port.yml​

메서드뜻
getName(), protocol(), url()CreditCheckPort, "http"/"dbms", @url(...)
operations() → name(), httpMethod(), url(), returnType(), params()methods: 의 호출들

QuerySpec - repository.yml 의 메서드​

메서드뜻
name()id:
sql()query: 문자열. 메서드 이름으로 만드는 쿼리면 null
isSelect(), isNative()조회인가 / nativeQuery: true 인가
returnType(), params()return:, params:

코드 쓰기​

SourceCode - ctx(spec) 이 돌려주는 지금 만드는 파일​

ctx(...) 에는 규칙이 받은 것을 그대로 넣습니다: ctx(e), ctx(d), ctx(enumeration), ctx(port).

메서드하는 일
ensureClassAnnotation(anno)클래스 어노테이션. 이미 있으면 두 번 붙이지 않음. import 자동
removeClassAnnotation(fqcn)클래스 어노테이션 빼기 (기본 규칙이 붙인 것도)
field(f) → FieldCode그 필드의 선언
removeField(f)그 필드를 이 파일에서 뺌
method("이름") → ensure(anno)이름이 같은 메서드 모두에 어노테이션 (repository, port …). 없는 이름이면 빌드 로그에 알림
addMethod(source)클래스 끝에 손으로 쓴 메서드를 더함 (어노테이션과 메서드)
excludeMethod("deleteById", …)생성되는 엔드포인트 메서드를 뺌
addImport(fqcn)import 등록 (실제로 쓰는 것만 렌더)
dontMake()이 파일을 만들지 않음 (로그에 SKIP(dontMake))
var(k, v), var(k, fmt, args…)스켈레톤의 ${k} 를 채움. null 은 무시됨 - 빈 값은 ""
varLine(k, fmt, args…), varLines(k, list)${k} 자리에 줄을 더함
syntax()이 파일의 언어 (Syntax.JAVA, Syntax.KOTLIN, Syntax.TYPESCRIPT)
requireSyntaxAnyOf(ruleId, Syntax…)다른 언어 생성에 쓰이면 조용히 틀린 파일 대신 그 파일을 실패로
artifactName()도메인 단위 파일 중 어느 것인지 (ContributedArtifact 의 클래스 이름)
render(), unfilledSlots()테스트에서: 파일 전체 문자열 / 채워지지 않은 ${…} 이름

FieldCode - 필드 하나​

메서드하는 일
ensure(anno)같은 어노테이션이 없을 때만 붙임 (보통 이것)
add(anno)무조건 붙임 (반복 가능한 어노테이션)
merge(anno)이미 붙은 같은 어노테이션에 속성만 더함, 없으면 붙임
replace(anno)같은 어노테이션을 이것으로 바꿈
removeAnnotation(fqcn)빼기
type("String"), rename("x"), initial("0")선언 타입 / 이름 / 초기값
comment(fmt, …), before(fmt, …), after(fmt, …)주석 / 선언 위 줄 / 선언 뒤 멤버

AnnoCode - 어노테이션​

어노테이션을 문자열로 이어 붙이지 마세요. AnnoCode 가 Java({})와 Kotlin([]) 문법, import, 따옴표를 맡습니다.

코드결과
AnnoCode.of("jakarta.persistence.Column")@Column + import
AnnoCode.of("org.jmolecules.ddd.annotation.%s", "Identity")포맷으로 이름 만들기
.val("name", "user_id")name = "user_id" (문자열 - 따옴표 붙음)
.val("hidden", true), .val("min", 2)hidden = true, min = 2
.valRaw("access", "JsonProperty.Access.WRITE_ONLY")그대로 - access = JsonProperty.Access.WRITE_ONLY
.valRaw("length", "%d", 200)포맷
.value("x"), .valueRaw("A.B")이름 없는 속성 - @X("x"), @X(A.B)
.qualified()전체 이름으로 (jakarta.persistence.Entity 와 이름이 겹칠 때)
AnnoCode.AnnoList.of(...)indexes = {...} 같은 어노테이션 목록

ModelCheck - 검사​

메서드결과
ModelCheck.warn(msg)경고만. 같은 메시지는 한 번
ModelCheck.fail(msg)생성을 끝까지 한 뒤 모든 실패를 모아 보여 주고 빌드 실패 (elcore3 종료 코드 1)
ModelCheck.failures(), ModelCheck.reset()테스트에서 결과 확인 / 테스트 사이 초기화

규칙에서 예외를 던지지 마세요. 그 규칙이 쓰던 파일 하나만 실패로 남아 원인을 찾기 어렵습니다.

새 파일​

타입하는 일
ArtifactContributor → artifactsFor(EntitySpec e)엔티티마다 파일을 선언
DomainArtifactContributor → artifactsFor(DomainSpec d)도메인마다 파일을 선언
ContributedArtifact.of(target, namespaceKey, className)새 target 이름(= 스켈레톤 이름), 패키지 키, 클래스 이름
.orNamespace("entity")그 패키지 키가 없는 프로젝트에서 쓸 키

스켈레톤은 규칙 모듈의 src/main/resources/elasticore-template/v3/<target>.tmpl (Java), elasticore-template/kt/<target>.tmpl (Kotlin). 자세한 것은 새 파일 만들기.

Syntax - 언어 이음새​

같은 규칙이 Java 와 Kotlin 을 모두 만듭니다. 어노테이션·import 는 생성기가 언어에 맞게 쓰므로 팀 규칙에는 보통 언어 분기가 필요 없습니다. 코드 조각을 직접 쓸 때(새 파일의 줄, addMethod)만 ctx(e).syntax() 로 나누거나 Syntax 에 묻습니다:

호출JavaKotlin
s.statement("return x")return x;return x
s.getter("dto", "name")dto.getName()dto.name
s.setter("to", "name", "v")to.setName(v)to.name = v
s.localVar("T", "x", "init")T x = init;val x = init
s.newInstance("X", "")new X()X()
s.nullableType("String")StringString?
s.classLiteral("X")X.classX::class
s.escapeName("in")in`in`

TypeScript 는 JVM 개념(classLiteral, sam, overriding …)에 답하지 않고 예외를 던집니다. 팀 규칙은 서버(Java·Kotlin) 생성에만 더해지고 TypeScript 클라이언트에는 들어가지 않습니다.