init: 클린 아키텍처 백엔드

This commit is contained in:
DongHyeonka
2026-07-24 14:29:36 +09:00
parent 9eed16d097
commit 821fe00c32
971 changed files with 74769 additions and 1 deletions
+38
View File
@@ -0,0 +1,38 @@
# domain-core — pure domain layer
## Registered identity
- Module ID: `domain-core`
- Gradle path: `:domain-core`
- Focused test: `./gradlew :domain-core:test --console=plain`
- Runtime baseline: Java 21; repository framework baseline: Spring Boot 4.0.0.
- Registry SSOT: `.harness/project/modules.yaml`.
Package root: `dev.caskeleton.domain`.
코드 주석에서 덜어낸 **설계 결정의 근거**는 [README.md](README.md) 가 모아둔다 (이 문서는 모듈 규칙 SSOT).
## Responsibility
- Business concepts, entities, value objects, enums, domain events, domain services.
- Repository ports where this reference implementation still keeps domain-owned persistence contracts.
- Domain invariants and core rules.
## Allowed
- Java standard library.
- Value-only shared operational types only if truly needed.
## Forbidden
- Spring annotations.
- JPA annotations.
- Servlet/HTTP types.
- Adapter, application, bootstrap, or presentation DTO dependencies.
## Test
```bash
cd src
./gradlew :domain-core:test
```
+96
View File
@@ -0,0 +1,96 @@
# domain-core — 설계 결정 참조
순수 도메인 계층 모듈. 패키지 루트: `dev.caskeleton.domain`.
허용/금지 의존, 책임 범위, 테스트 명령 같은 **모듈 규칙**은 [CLAUDE.md](CLAUDE.md) 가 SSOT 다.
이 문서는 코드 주석에서 덜어낸 **설계 결정의 근거**를 모아둔 참조용 기록이다 — 코드를 읽다
"왜 이렇게 했나"가 궁금할 때 본다. 플랜/스펙 추적용 ID는
더 이상 코드 주석에 두지 않고, 그 근거를 아래에 쉬운 말로 풀어 둔다.
여기서 자주 나오는 "ArchUnit 규칙"이란, 빌드할 때 코드가 정해진 구조를 어겼는지 자동으로
검사하는 아키텍처 테스트다. 즉 아래의 "이런 건 금지"라는 결정들은 말로만 정한 약속이 아니라
빌드가 실제로 강제한다.
---
## identifier — 리소스 식별자
### `ResourceId` — 모든 식별자가 구현하는 마커 인터페이스
시스템의 모든 리소스 식별자 값 객체(예: `WorkLogId`)가 구현하는 공통 인터페이스다.
식별자의 실제 문자열 값은 `value()` 하나로 노출한다.
- **왜 `sealed` 로 닫지 않았나.** 원래 레퍼런스 설계는 `permits WorkLogId` 처럼 허용 구현을
못 박는 닫힌 집합(`sealed`)으로 만들려 했다. 그런데 `WorkLogId` 같은 실제 식별자는
`sample-portfolio`(샘플 코드)에 있고, 이 마커는 재사용 가능한 `domain-core`에 있다.
`permits` 절을 쓰는 순간 `domain-core``sample-portfolio`를 의존하게 되는데, 이건
모듈 의존성 규칙과 `production_code_does_not_depend_on_sample_portfolio` ArchUnit 규칙이
둘 다 금지하는 방향이다. 그래서 `sealed`을 포기했다.
- **그럼 "닫힌 집합" 보장은 어디서 하나.** `sealed`이 줬을 보장(아무 타입이나 식별자로
쓰지 못하게 하는 것)은 대신 빌드 타임의 `no_long_id_pk` ArchUnit 규칙(D17)이 강제한다 —
도메인의 모든 `id` 필드는 반드시 `ResourceId` 구현이어야 하고, 날것의 `Long` PK 는 금지된다.
- **`<SELF>` 타입 파라미터(F-bounded generic).** 제네릭이 자기 자신을 다시 가리키는
재귀 구조다. 구현 타입이 "나는 나 자신 타입의 `ResourceId`다"라고 선언하게 해서, 자기
타입을 그대로 돌려주는 self-typed API 를 타입 안전하게 만들 수 있다.
- **`value()` 계약.** 36자 canonical UUID(RFC 9562 UUIDv7) 문자열을 반환한다(D2/D3).
### `IdFactory` — 식별자 생성 포트
새 식별자를 발급하는 도메인 포트(인터페이스)다.
- **"발급할 책임"과 "실제 생성 행위"를 분리한다.** 식별자를 발급할 *책임* 은 도메인(이 포트)이
소유한다. 하지만 실제로 식별자를 *만드는 행위* 는 인프라 어댑터(예: UUIDv7 생성기)가
수행하고, 애플리케이션 유스케이스가 그 둘을 조율한다. 이렇게 나누면 도메인은 구체적인
난수·시계 같은 소스를 전혀 알지 못한 채, 식별자에 대한 계약만 소유한 순수한 상태로 남는다.
---
## stereotype — 도메인 모델링 스테레오타입 마커
`@ValueObject`, `@AggregateRoot`, `@DomainEvent` 세 개의 마커 애너테이션 묶음이다.
- **왜 이름 규칙이 아니라 마커 애너테이션인가.** 이 애너테이션들은 동작도, 프레임워크 의존도
없는 순수 POJO 마커다. 아키텍처 테스트가 "클래스 이름이 `~VO`로 끝나는가" 같은 깨지기 쉬운
네이밍 규칙 대신, `@ValueObject` 처럼 **명시적이고 의도가 드러나는 선언**을 기준으로 모델링
가드레일을 걸 수 있게 한다.
- **왜 프레임워크 중립으로 두나.** 일부러 어떤 프레임워크에도 의존하지 않게 만들어서
`domain-core`가 순수 라이브러리로 남게 한다(`domain_is_pure` 가드레일).
### `@ValueObject` — 값 객체
불변이고, 개념적 식별자가 없으며, 자기 불변식을 스스로 검증하는 값 타입을 표시한다(D5/D6).
- **불변식 검증은 유일한 생성 경로에서만.** 값 객체는 record canonical 생성자 또는 팩토리라는
단 하나의 생성 경로에서 자기 불변식을 검증한다.
- **강제 규칙 — public 무인자 생성자 금지.** `@ValueObject` 타입과 `..domain.vo..` 아래 모든
타입은 public 무인자 생성자를 노출하면 안 된다. 빈 생성자는 검증을 건너뛰고 객체를 만들 수
있는 "불변식 우회 뒷문"이기 때문이다(`value_objects_have_no_public_no_arg_constructor`).
- **왜 `RUNTIME` 리텐션인가.** ArchUnit(바이트코드 검사)과 리플렉션 기반 테스트가 둘 다
이 애너테이션을 읽을 수 있어야 해서 런타임까지 유지한다.
### `@AggregateRoot` — 애그리거트 루트
애그리거트의 일관성 경계이자, 그 상태를 바꿀 수 있는 유일한 진입점인 타입을 표시한다
(D7, Vernon "Effective Aggregate Design").
- **상태 변경은 의도가 드러나는 메서드로만.** 상태 변경은 불변식을 강제하는, 의도가 분명한
애그리거트 메서드를 통해서만 일어나야 한다. 날것의 `setXxx` 세터를 public 으로 열어두면 안 된다.
- **강제 규칙 — public 세터 금지.** `@AggregateRoot` 타입의 모든 `set*` 메서드는
package-private 또는 protected 여야 한다(`aggregate_root_setters_are_not_public`). 이
가시성은 Vernon Option A(ORM 외부에서 매핑하는 방식)로 객체를 재구성할 때 의존하는 지점이다.
- **이 규칙의 한계.** ArchUnit 은 정적으로 `set*` 라는 이름 패턴까지만 잡을 수 있다.
`applyXxx` 처럼 이름이 다른 상태 변경 메서드는 자동 검사가 닿지 않으므로 코드 리뷰가 잡아야
할 몫으로 남는다(spec §4 PRE-DECISION).
### `@DomainEvent` — 도메인 이벤트
도메인에서 일어난 사실을 담은, 불변이고 전송수단에 독립적인(transport-free) 타입을 표시한다
(D4/D8).
- **"transport-free"가 무슨 뜻인가.** 이벤트는 도메인 데이터만 담고, 메시지 브로커·와이어
포맷·HTTP 같은 전송 계층 타입을 절대 참조하지 않는다는 뜻이다. 도메인 이벤트를 통합 이벤트나
와이어 이벤트로 번역하는 일은 애플리케이션/인프라 경계의 책임이지 도메인의 책임이 아니다.
- **강제 규칙 두 가지.** 모든 `@DomainEvent` 타입은 (1) `record` 여야 하고
(`domain_events_are_records`, 불변성 보장), (2) 어떤 전송 패키지(`org.apache.kafka..`,
`org.springframework.http..`, `jakarta.ws.rs..`)도 의존하면 안 된다
(`domain_events_are_transport_free`).
+3
View File
@@ -0,0 +1,3 @@
// Pure domain layer. No Spring, no infra dependencies.
dependencies {
}
+151
View File
@@ -0,0 +1,151 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.
biz.aQute.bnd:biz.aQute.bnd.annotation:7.1.0=testCompileClasspath
ch.qos.logback:logback-classic:1.5.21=testCompileClasspath,testRuntimeClasspath
ch.qos.logback:logback-core:1.5.21=testCompileClasspath,testRuntimeClasspath
com.fasterxml.jackson.core:jackson-annotations:2.20=testCompileClasspath,testRuntimeClasspath
com.github.ben-manes.caffeine:caffeine:3.2.3=annotationProcessor,testAnnotationProcessor
com.github.kevinstern:software-and-algorithms:1.0=annotationProcessor,testAnnotationProcessor
com.github.spotbugs:spotbugs-annotations:4.10.2=spotbugs
com.github.spotbugs:spotbugs-annotations:4.8.6=testCompileClasspath
com.github.spotbugs:spotbugs:4.10.2=spotbugs
com.github.stephenc.jcip:jcip-annotations:1.0-1=spotbugs
com.google.auto.service:auto-service-annotations:1.0.1=annotationProcessor,testAnnotationProcessor
com.google.auto.value:auto-value-annotations:1.9=annotationProcessor,testAnnotationProcessor
com.google.auto:auto-common:1.2.2=annotationProcessor,testAnnotationProcessor
com.google.code.findbugs:jsr305:3.0.2=checkstyle,spotbugs,testCompileClasspath
com.google.code.gson:gson:2.13.2=spotbugs
com.google.errorprone:error_prone_annotation:2.49.0=annotationProcessor,testAnnotationProcessor
com.google.errorprone:error_prone_annotations:2.38.0=testCompileClasspath
com.google.errorprone:error_prone_annotations:2.41.0=spotbugs
com.google.errorprone:error_prone_annotations:2.47.0=checkstyle
com.google.errorprone:error_prone_annotations:2.49.0=annotationProcessor,testAnnotationProcessor
com.google.errorprone:error_prone_check_api:2.49.0=annotationProcessor,testAnnotationProcessor
com.google.errorprone:error_prone_core:2.49.0=annotationProcessor,testAnnotationProcessor
com.google.googlejavaformat:google-java-format:1.35.0=annotationProcessor,testAnnotationProcessor
com.google.guava:failureaccess:1.0.3=annotationProcessor,checkstyle,testAnnotationProcessor
com.google.guava:guava:33.5.0-jre=annotationProcessor,testAnnotationProcessor
com.google.guava:guava:33.6.0-jre=checkstyle
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava=annotationProcessor,checkstyle,testAnnotationProcessor
com.google.j2objc:j2objc-annotations:3.1=annotationProcessor,checkstyle,testAnnotationProcessor
com.google.protobuf:protobuf-java:4.33.2=annotationProcessor,testAnnotationProcessor
com.h3xstream.findsecbugs:findsecbugs-plugin:1.14.0=spotbugsPlugins
com.jayway.jsonpath:json-path:2.9.0=testCompileClasspath,testRuntimeClasspath
com.puppycrawl.tools:checkstyle:13.5.0=checkstyle
com.vaadin.external.google:android-json:0.0.20131108.vaadin1=testCompileClasspath,testRuntimeClasspath
commons-beanutils:commons-beanutils:1.11.0=checkstyle
commons-collections:commons-collections:3.2.2=checkstyle
commons-io:commons-io:2.21.0=spotbugs
commons-logging:commons-logging:1.3.5=testCompileClasspath,testRuntimeClasspath
info.picocli:picocli:4.7.7=checkstyle
io.github.eisop:dataflow-errorprone:3.41.0-eisop1=annotationProcessor,testAnnotationProcessor
io.github.java-diff-utils:java-diff-utils:4.12=annotationProcessor,testAnnotationProcessor
io.micrometer:micrometer-commons:1.16.0=testCompileClasspath,testRuntimeClasspath
io.micrometer:micrometer-observation:1.16.0=testCompileClasspath,testRuntimeClasspath
jakarta.activation:jakarta.activation-api:2.1.4=testCompileClasspath,testRuntimeClasspath
jakarta.annotation:jakarta.annotation-api:3.0.0=testCompileClasspath,testRuntimeClasspath
jakarta.xml.bind:jakarta.xml.bind-api:4.0.4=testCompileClasspath,testRuntimeClasspath
javax.inject:javax.inject:1=annotationProcessor,testAnnotationProcessor
jaxen:jaxen:2.0.0=spotbugs
net.bytebuddy:byte-buddy-agent:1.17.8=testCompileClasspath,testRuntimeClasspath
net.bytebuddy:byte-buddy:1.17.8=testCompileClasspath,testRuntimeClasspath
net.minidev:accessors-smart:2.6.0=testCompileClasspath,testRuntimeClasspath
net.minidev:json-smart:2.6.0=testCompileClasspath,testRuntimeClasspath
net.sf.saxon:Saxon-HE:12.9=checkstyle,spotbugs
org.antlr:antlr4-runtime:4.13.2=checkstyle
org.apache.bcel:bcel:6.12.0=spotbugs
org.apache.commons:commons-lang3:3.20.0=checkstyle,spotbugs
org.apache.commons:commons-text:1.15.0=spotbugs
org.apache.commons:commons-text:1.3=checkstyle
org.apache.httpcomponents:httpclient:4.5.13=checkstyle
org.apache.httpcomponents:httpcore:4.4.16=checkstyle
org.apache.logging.log4j:log4j-api:2.25.2=spotbugs,testCompileClasspath,testRuntimeClasspath
org.apache.logging.log4j:log4j-core:2.25.2=spotbugs
org.apache.logging.log4j:log4j-to-slf4j:2.25.2=testCompileClasspath,testRuntimeClasspath
org.apache.maven.doxia:doxia-core:1.12.0=checkstyle
org.apache.maven.doxia:doxia-logging-api:1.12.0=checkstyle
org.apache.maven.doxia:doxia-module-xdoc:1.12.0=checkstyle
org.apache.maven.doxia:doxia-sink-api:1.12.0=checkstyle
org.apache.tomcat.embed:tomcat-embed-core:11.0.14=testCompileClasspath,testRuntimeClasspath
org.apache.tomcat.embed:tomcat-embed-el:11.0.14=testCompileClasspath,testRuntimeClasspath
org.apache.tomcat.embed:tomcat-embed-websocket:11.0.14=testCompileClasspath,testRuntimeClasspath
org.apache.xbean:xbean-reflect:3.7=checkstyle
org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath
org.assertj:assertj-core:3.27.6=testCompileClasspath,testRuntimeClasspath
org.awaitility:awaitility:4.3.0=testCompileClasspath,testRuntimeClasspath
org.codehaus.plexus:plexus-classworlds:2.6.0=checkstyle
org.codehaus.plexus:plexus-component-annotations:2.1.0=checkstyle
org.codehaus.plexus:plexus-container-default:2.1.0=checkstyle
org.codehaus.plexus:plexus-utils:3.3.0=checkstyle
org.dom4j:dom4j:2.2.0=spotbugs
org.hamcrest:hamcrest:3.0=testCompileClasspath,testRuntimeClasspath
org.javassist:javassist:3.28.0-GA=checkstyle
org.jspecify:jspecify:1.0.0=annotationProcessor,checkstyle,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath
org.junit.jupiter:junit-jupiter-api:6.0.1=testCompileClasspath,testRuntimeClasspath
org.junit.jupiter:junit-jupiter-engine:6.0.1=testRuntimeClasspath
org.junit.jupiter:junit-jupiter-params:6.0.1=testCompileClasspath,testRuntimeClasspath
org.junit.jupiter:junit-jupiter:6.0.1=testCompileClasspath,testRuntimeClasspath
org.junit.platform:junit-platform-commons:6.0.1=testCompileClasspath,testRuntimeClasspath
org.junit.platform:junit-platform-engine:6.0.1=testRuntimeClasspath
org.junit.platform:junit-platform-launcher:6.0.1=testRuntimeClasspath
org.junit:junit-bom:6.0.1=testCompileClasspath,testRuntimeClasspath
org.junit:junit-bom:6.1.0=spotbugs
org.mockito:mockito-core:5.20.0=testCompileClasspath,testRuntimeClasspath
org.mockito:mockito-junit-jupiter:5.20.0=testCompileClasspath,testRuntimeClasspath
org.objenesis:objenesis:3.3=testRuntimeClasspath
org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath
org.osgi:org.osgi.annotation.bundle:2.0.0=testCompileClasspath
org.osgi:org.osgi.annotation.versioning:1.1.2=testCompileClasspath
org.osgi:org.osgi.resource:1.0.0=testCompileClasspath
org.osgi:org.osgi.service.serviceloader:1.0.0=testCompileClasspath
org.ow2.asm:asm-analysis:9.10.1=spotbugs
org.ow2.asm:asm-commons:9.10.1=spotbugs
org.ow2.asm:asm-tree:9.10.1=spotbugs
org.ow2.asm:asm-util:9.10.1=spotbugs
org.ow2.asm:asm:9.10.1=spotbugs
org.ow2.asm:asm:9.7.1=testCompileClasspath,testRuntimeClasspath
org.pcollections:pcollections:4.0.1=annotationProcessor,testAnnotationProcessor
org.reflections:reflections:0.10.2=checkstyle
org.skyscreamer:jsonassert:1.5.3=testCompileClasspath,testRuntimeClasspath
org.slf4j:jul-to-slf4j:2.0.17=testCompileClasspath,testRuntimeClasspath
org.slf4j:slf4j-api:2.0.17=spotbugs,spotbugsSlf4j,testCompileClasspath,testRuntimeClasspath
org.slf4j:slf4j-simple:2.0.17=checkstyle,spotbugsSlf4j
org.springframework.boot:spring-boot-autoconfigure:4.0.0=testCompileClasspath,testRuntimeClasspath
org.springframework.boot:spring-boot-http-client:4.0.0=testCompileClasspath,testRuntimeClasspath
org.springframework.boot:spring-boot-http-converter:4.0.0=testCompileClasspath,testRuntimeClasspath
org.springframework.boot:spring-boot-jackson:4.0.0=testCompileClasspath,testRuntimeClasspath
org.springframework.boot:spring-boot-restclient:4.0.0=testCompileClasspath,testRuntimeClasspath
org.springframework.boot:spring-boot-resttestclient:4.0.0=testCompileClasspath,testRuntimeClasspath
org.springframework.boot:spring-boot-servlet:4.0.0=testCompileClasspath,testRuntimeClasspath
org.springframework.boot:spring-boot-starter-jackson-test:4.0.0=testCompileClasspath,testRuntimeClasspath
org.springframework.boot:spring-boot-starter-jackson:4.0.0=testCompileClasspath,testRuntimeClasspath
org.springframework.boot:spring-boot-starter-logging:4.0.0=testCompileClasspath,testRuntimeClasspath
org.springframework.boot:spring-boot-starter-test:4.0.0=testCompileClasspath,testRuntimeClasspath
org.springframework.boot:spring-boot-starter-tomcat-runtime:4.0.0=testCompileClasspath,testRuntimeClasspath
org.springframework.boot:spring-boot-starter-tomcat:4.0.0=testCompileClasspath,testRuntimeClasspath
org.springframework.boot:spring-boot-starter-webmvc-test:4.0.0=testCompileClasspath,testRuntimeClasspath
org.springframework.boot:spring-boot-starter-webmvc:4.0.0=testCompileClasspath,testRuntimeClasspath
org.springframework.boot:spring-boot-starter:4.0.0=testCompileClasspath,testRuntimeClasspath
org.springframework.boot:spring-boot-test-autoconfigure:4.0.0=testCompileClasspath,testRuntimeClasspath
org.springframework.boot:spring-boot-test:4.0.0=testCompileClasspath,testRuntimeClasspath
org.springframework.boot:spring-boot-tomcat:4.0.0=testCompileClasspath,testRuntimeClasspath
org.springframework.boot:spring-boot-web-server:4.0.0=testCompileClasspath,testRuntimeClasspath
org.springframework.boot:spring-boot-webmvc-test:4.0.0=testCompileClasspath,testRuntimeClasspath
org.springframework.boot:spring-boot-webmvc:4.0.0=testCompileClasspath,testRuntimeClasspath
org.springframework.boot:spring-boot:4.0.0=testCompileClasspath,testRuntimeClasspath
org.springframework:spring-aop:7.0.1=testCompileClasspath,testRuntimeClasspath
org.springframework:spring-beans:7.0.1=testCompileClasspath,testRuntimeClasspath
org.springframework:spring-context:7.0.1=testCompileClasspath,testRuntimeClasspath
org.springframework:spring-core:7.0.1=testCompileClasspath,testRuntimeClasspath
org.springframework:spring-expression:7.0.1=testCompileClasspath,testRuntimeClasspath
org.springframework:spring-test:7.0.1=testCompileClasspath,testRuntimeClasspath
org.springframework:spring-web:7.0.1=testCompileClasspath,testRuntimeClasspath
org.springframework:spring-webmvc:7.0.1=testCompileClasspath,testRuntimeClasspath
org.xmlresolver:xmlresolver:5.3.3=checkstyle,spotbugs
org.xmlunit:xmlunit-core:2.10.4=testCompileClasspath,testRuntimeClasspath
org.yaml:snakeyaml:2.5=testCompileClasspath,testRuntimeClasspath
tools.jackson.core:jackson-core:3.0.2=testCompileClasspath,testRuntimeClasspath
tools.jackson.core:jackson-databind:3.0.2=testCompileClasspath,testRuntimeClasspath
tools.jackson:jackson-bom:3.0.2=testCompileClasspath,testRuntimeClasspath
empty=compileClasspath,runtimeClasspath
@@ -0,0 +1,15 @@
package dev.caskeleton.domain.identifier;
/**
* Domain port for server-assigned resource identifier generation.
*
* <p>The domain owns the identity contract; the act of generation is performed by an infrastructure
* adapter and orchestrated by an application use case. Design rationale in the module README.
*
* @param <T> the resource identifier type produced by this factory
*/
public interface IdFactory<T extends ResourceId<?>> {
/** Mints a fresh, never-before-used identifier. */
T newId();
}
@@ -0,0 +1,15 @@
package dev.caskeleton.domain.identifier;
/**
* Marker for every resource identifier value object in the system.
*
* <p>Deliberately <strong>not</strong> {@code sealed} (the closed set is enforced by the {@code
* no_long_id_pk} ArchUnit rule instead). Design rationale in the module README.
*
* @param <SELF> the implementing identifier type (F-bounded for self-typed APIs)
*/
public interface ResourceId<SELF extends ResourceId<SELF>> {
/** The canonical 36-character UUID string (RFC 9562 UUIDv7). */
String value();
}
@@ -0,0 +1,2 @@
/** Domain layer anchor for project-owned business concepts. */
package dev.caskeleton.domain;
@@ -0,0 +1,19 @@
package dev.caskeleton.domain.stereotype;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Marks a type as an <strong>aggregate root</strong> — the single consistency boundary and the only
* entry point through which the aggregate's state may change.
*
* <p>Pure marker, {@code RUNTIME} retention. Design rationale (Vernon "Effective Aggregate Design",
* enforced guardrails, the {@code set*} static-analysis limit) in the module README.
*/
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface AggregateRoot {}
@@ -0,0 +1,19 @@
package dev.caskeleton.domain.stereotype;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Marks a type as a <strong>domain event</strong> — an immutable, transport-free fact about
* something that happened in the domain.
*
* <p>Pure marker, {@code RUNTIME} retention. Design rationale (transport-free meaning, enforced
* guardrails) in the module README.
*/
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface DomainEvent {}
@@ -0,0 +1,19 @@
package dev.caskeleton.domain.stereotype;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Marks a type as a domain <strong>value object</strong> — immutable, no conceptual identity,
* self-validating on its sole construction path (canonical record constructor or factory).
*
* <p>Pure marker, {@code RUNTIME} retention. Design rationale (enforced guardrails) in the module
* README.
*/
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ValueObject {}
@@ -0,0 +1,15 @@
/**
* Domain modeling stereotype markers — pure, framework-neutral POJO markers that let the
* architecture suite key modeling guardrails off an explicit declaration rather than a brittle
* naming convention. Design rationale in the module README.
*
* <ul>
* <li>{@link dev.caskeleton.domain.stereotype.ValueObject} — immutable, self-validating value
* type.
* <li>{@link dev.caskeleton.domain.stereotype.AggregateRoot} — consistency boundary whose state
* mutates only through its own methods.
* <li>{@link dev.caskeleton.domain.stereotype.DomainEvent} — transport-free fact emitted by the
* domain.
* </ul>
*/
package dev.caskeleton.domain.stereotype;