--- title: personal-blog / Buckpal — ArchUnit Lombok allowlist + direct @Transactional (CONTRARY EVIDENCE to ca-tmpl D3/D1) source_type: personal-blog url: https://github.com/thombergs/buckpal related_branches: - feature-architecture-enforcement-rules - feature-application-port-usecase-contract related_projects: [ca-skeleton] tags: [personal-blog, ca-skeleton, architecture, archunit, lombok, transaction, hexagonal, domain-purity] status: raw confidence: medium created: 2026-05-28 --- # personal-blog / Buckpal — ArchUnit Lombok allowlist + direct @Transactional (CONTRARY EVIDENCE to ca-tmpl D3/D1) > Layer: `raw/company-tech-blogs/` — 외부 자료(개인 블로그·책 공식 예제 코드) 원문 발췌·출처 기록. > **CONTRARY EVIDENCE 노트**: ca-tmpl 의 결정 D3 (domain-core Lombok 금지) 와 D1 (@Transactional 직접 import 금지) 과 **반대 방향**인 OSS 선례를 기록한다. > 이 자료는 ca-tmpl 결정을 reject 하기 위한 것이 아니라, ca-tmpl 이 "OSS 다수파 best practice" 가 아닌 **ca-tmpl 자체 stricter stance** 임을 솔직히 명시하기 위한 근거다. --- ## Parent / 활용 branch (필수, 최소 1개+) | Branch | 이 자료가 정당화하는 결정 | |---|---| | [[raw/branch-notes/feature-architecture-enforcement-rules]] | D3 CONTRARY evidence — Buckpal domain purity ArchUnit rule 이 `lombok..` 패키지를 명시적으로 allowlist 함으로써, "domain-core Lombok 금지" 가 OSS 공통 표준이 아니라 ca-tmpl 자체 stricter stance 임을 뒷받침 | | [[raw/branch-notes/feature-application-port-usecase-contract]] | D1 CONTRARY evidence — Buckpal application service 가 `@Transactional` 을 직접 클래스에 부착함으로써, "Spring @Transactional 직접 import 금지" 가 OSS 다수파가 아닌 ca-tmpl 소수파 결정임을 뒷받침 | --- ## 출처 / Source - 원본 URL (repo): https://github.com/thombergs/buckpal - DependencyRuleTests.java: https://raw.githubusercontent.com/thombergs/buckpal/master/src/test/java/io/reflectoring/buckpal/DependencyRuleTests.java - SendMoneyService.java: https://raw.githubusercontent.com/thombergs/buckpal/master/src/main/java/io/reflectoring/buckpal/application/domain/service/SendMoneyService.java - UseCase.java: https://raw.githubusercontent.com/thombergs/buckpal/master/src/main/java/io/reflectoring/buckpal/common/UseCase.java - 아카이브 URL: (미등록 — GitHub raw 직접 링크) - 저자: Tom Hombergs (Reflectoring.io, "Get Your Hands Dirty on Clean Architecture" 저자) - 자료 성격: 개인 블로그(reflectoring.io) + 책("Get Your Hands Dirty on Clean Architecture") 공식 예제 코드 - repo star: ≥2,500 (2026-05-28 확인 시점 기준, Hexagonal Architecture Java OSS 중 가장 영향력 있는 reference) - single-module 여부: repo root 에 `build.gradle` 1개, `settings.gradle` 부재 → 단일 Gradle 모듈 확인 (GitHub API tree 검증) - 마지막 확인일: 2026-05-28 --- ## 왜 저장했는지 / Why archived Buckpal 은 Hexagonal Architecture Java 구현의 사실상 가장 영향력 있는 OSS 예제다. 그런데 ca-tmpl 의 두 핵심 결정 — (1) domain-core 에서 Lombok annotation 금지, (2) application layer 에서 Spring `@Transactional` 직접 import 금지 — 과 **정반대 방향**을 택하고 있다. ca-tmpl 결정 문서에서 "우리가 OSS 다수파와 다르다" 는 사실을 솔직히 기록하기 위해 보관한다. 이 자료가 ca-tmpl 결정을 부정하는 것이 아니라, 결정이 "stricter / 소수파" 임을 명시하는 CONTRARY evidence 로 기능한다. --- ## 핵심 인용 / Key quotes (verbatim, self-grep 통과) > [DependencyRuleTests.java §domainModelDoesNotDependOnOutside] "void domainModelDoesNotDependOnOutside() { noClasses() .that() .resideInAPackage(\"io.reflectoring.buckpal.application.domain.model..\") .should() .dependOnClassesThat() .resideOutsideOfPackages( \"io.reflectoring.buckpal.application.domain.model..\", \"lombok..\", \"java..\" ) .check(new ClassFileImporter() .importPackages(\"io.reflectoring.buckpal..\")); }" > > — Source: DependencyRuleTests.java line 33–46. domain model 이 의존할 수 있는 외부 패키지를 `lombok..` 와 `java..` 로 명시적 allowlist 함. > [DependencyRuleTests.java §import] "import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses;" > > — Source: DependencyRuleTests.java line 7. ArchUnit `noClasses()` DSL 직접 사용 확인. > [SendMoneyService.java §class-declaration] "@RequiredArgsConstructor @UseCase @Transactional public class SendMoneyService implements SendMoneyUseCase {" > > — Source: SendMoneyService.java line 16–19. application service 에 `@UseCase` (= `@Component` meta-annotation) + `@Transactional` 직접 클래스 레벨 부착. Spring DI + transaction boundary 를 추상화 없이 직접 선언. > [SendMoneyService.java §import] "import jakarta.transaction.Transactional;" > > — Source: SendMoneyService.java line 13. `jakarta.transaction.Transactional` 직접 import. `org.springframework.transaction.annotation.Transactional` 이 아닌 Jakarta EE 표준 어노테이션 사용 (Spring 은 양쪽 모두 지원). > [UseCase.java §meta-annotation] "@Component public @interface UseCase { @AliasFor(annotation = Component.class) String value() default \"\"; }" > > — Source: UseCase.java line 14–19 (핵심 부분). `@UseCase` 는 `@Component` 의 meta-annotation. 즉 SendMoneyService 는 사실상 `@Component @Transactional` 직접 부착. --- ## Claims Extracted / 추출된 주장 > 이 자료가 **직접 말하는 것만** claim 으로 분리한다. ca-tmpl 에 적용한 해석은 여기 쓰지 않는다. | Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove | |---|---|---|---|---|---| | BUCKPAL-LOMBOK-C1 | Buckpal domain purity ArchUnit rule 은 domain model 이 `lombok..` 패키지에 의존하는 것을 **허용** (resideOutsideOfPackages allowlist 에 `"lombok.."` 포함) | [DependencyRuleTests.java §domainModelDoesNotDependOnOutside] `"lombok.."` (line 41 of fetched file) | `engineering-blog` (개인 블로그 + 책 예제 — Spring/ArchUnit 공식 아님) | Java Hexagonal Architecture 에서 domain-core 가 Lombok 에 의존하는 것이 기술적으로 가능하며 저명한 예제에서 채택됨을 보여주는 선례 | Lombok 사용이 "옳다" 또는 "권장된다" 는 것. 단지 "Buckpal 은 그렇게 결정했다" 만 증명. ca-tmpl 의 금지 결정을 부정하지 않음 | | BUCKPAL-LOMBOK-C2 | domain model 이 Lombok annotation 을 사용해도 domain purity ArchUnit rule 을 통과하도록 설계 가능하다 (rule 자체가 Lombok 을 외부 침해로 간주하지 않음) | [DependencyRuleTests.java §domainModelDoesNotDependOnOutside] `"lombok.."` 가 `resideOutsideOfPackages` 의 허용 목록에 포함됨 (line 41) | `engineering-blog` | Buckpal 설계 기준에서 Lombok = domain 내부 허용 도구. 이 선택이 책 시장에서 ≥2.5k star OSS 예제로 수용된 사실 | Lombok 이 "domain purity 에 영향을 주지 않는다" 는 일반 원칙. Buckpal 이 Lombok 사용의 장단점을 공식 분석했음을 증명하지 않음 | | BUCKPAL-TX-C1 | Buckpal SendMoneyService 는 `@Transactional` 어노테이션을 클래스 레벨에 직접 부착하여 transaction boundary 를 선언 | [SendMoneyService.java §class-declaration] `"@UseCase @Transactional public class SendMoneyService implements SendMoneyUseCase {"` (line 16–19) | `engineering-blog` | Hexagonal Architecture Java 에서 application service 가 Spring/Jakarta `@Transactional` 직접 선언하는 패턴의 저명한 구현 선례 | `@Transactional` 직접 부착이 "Hexagonal Architecture 의 표준" 이거나 "best practice" 임. 단지 "Buckpal 은 그렇게 구현했다" 만 증명 | | BUCKPAL-TX-C2 | Buckpal 에는 `TransactionPort` / `TransactionRunner` / `UnitOfWork` 같은 transaction abstraction 이 존재하지 않음 — Spring `@Transactional` 직접 사용 | [SendMoneyService.java §import] `"import jakarta.transaction.Transactional;"` (line 13) + class declaration (line 16–19). 별도 transaction port interface 파일 부재 (GitHub API tree 검증) | `engineering-blog` | Buckpal 설계에서 transaction abstraction layer 는 선택이 아닌 생략. 이 생략이 책 예제로 수용된 사실 | transaction abstraction 이 불필요하다는 일반 원칙. ca-tmpl 의 `TransactionPort` 결정이 잘못됐음을 증명하지 않음 | ### Strength 허용값 참고 - 본 자료의 모든 claim: `engineering-blog` — Tom Hombergs 개인 블로그 + 책 예제. Spring 공식/ArchUnit 공식 아님. - `company-case-study` 로 분류하지 않은 이유: Buckpal 은 기업 엔지니어링 블로그 출처가 아닌 개인 저자(Tom Hombergs)의 책 예제. --- ## Usage Boundaries / 적용 경계 ### 이 자료가 직접 증명하는 것 - `BUCKPAL-LOMBOK-C1`: Buckpal 이 domain purity rule 에서 `lombok..` 를 명시적으로 allowlist 한다는 코드 사실 - `BUCKPAL-LOMBOK-C2`: domain-core + Lombok 공존 설계가 저명한 OSS 예제에서 실제로 구현됨 - `BUCKPAL-TX-C1`: Buckpal 이 `@Transactional` 을 application service 클래스 레벨에 직접 부착함 - `BUCKPAL-TX-C2`: Buckpal 에 transaction abstraction 계층이 없음 ### 이 자료가 증명하지 않는 것 - Lombok 사용이 domain purity 원칙과 양립 가능하다는 일반 원칙 (단지 Buckpal 의 구현 결정) - `@Transactional` 직접 부착이 Hexagonal Architecture 의 "공식" 또는 "권장" 방식 (Spring 공식 문서는 `@Transactional` 지원을 명시하지만 Hexagonal Architecture 특정 배치 지침은 제공하지 않음) - ca-tmpl 의 D3 (Lombok 금지) 또는 D1 (`@Transactional` 금지) 결정이 잘못됐음 - Buckpal 패턴이 다른 프로젝트에 직접 이식 가능함 (Buckpal 은 single-module, ca-tmpl 은 multi-module) ### ca-tmpl 에 적용하려면 추가 확인이 필요한 것 - 이 자료는 CONTRARY evidence 로만 사용한다. ca-tmpl 결정 D3/D1 을 변경하려면 별도 Decision Review 필요 - Buckpal 의 single-module 구조 vs ca-tmpl 의 multi-module Gradle 구조 차이 — module boundary 가 강한 격리를 제공하는 multi-module 환경에서 Lombok classpath 포함 여부는 별도 평가 필요 --- ## 메모 / Notes - Buckpal 은 단일 Gradle 모듈 (`build.gradle` 1개, `settings.gradle` 부재). ca-tmpl 과 module 구조가 근본적으로 다름. domain purity rule 의 의미가 다를 수 있음. - Buckpal 의 `@Transactional` 은 `jakarta.transaction.Transactional` (Jakarta EE 표준). ca-tmpl 금지 대상인 `org.springframework.transaction.annotation.Transactional` 과 다른 import path — 하지만 Spring 은 양쪽 모두 처리하고, ca-tmpl ArchUnit rule 은 `jakarta.transaction.Transactional` 도 별도 금지 검토 대상으로 볼 수 있음. 이 세부 사항은 `feature-architecture-enforcement-rules` branch 에서 확인 필요. - `@UseCase` 는 `@Component` meta-annotation (UseCase.java 원문 확인). 즉 SendMoneyService 에서 `@UseCase @Transactional` = `@Component @Transactional`. ca-tmpl 은 `@Component`/`@Service` 를 application-core 에서 허용(D13)하고 `@Transactional` 만 금지. 이 분리는 Buckpal 과 다름. - 추가로 봐야 할 Buckpal 관련 자료: [[raw/company-tech-blogs/hexagonal-reflectoring-transactional-placement]] — 이미 보관된 Hombergs 블로그 글이 `@Transactional` 배치를 직접 다룸. BUCKPAL-TX-C1/C2 와 함께 읽으면 Hombergs 의 입장이 더 명확해짐. --- ## Decision Evidence Map / 결정-근거 매핑 > 이 raw source 가 각 parent branch 의 어떤 Decision ID 를 뒷받침(또는 반박)하는지 명시한다. > `CONTRARY` = 결정과 반대 방향의 evidence (결정 자체를 reject 하지 않음 — 결정이 소수파임을 기록). > `UNSUPPORTED_DECISION` = 이 자료만으로는 증명 불충분. ### Parent: feature-architecture-enforcement-rules | Decision ID | Decision (요약) | This source's role | Supporting Claim IDs | Evidence Strength | Notes | |---|---|---|---|---|---| | D3 | `domain-core` forbidden import rule (Lombok 포함) | **CONTRARY** — Buckpal 은 domain purity rule 에서 `lombok..` 를 allowlist. ca-tmpl 금지 결정과 반대 방향 | `BUCKPAL-LOMBOK-C1`, `BUCKPAL-LOMBOK-C2` | `engineering-blog` | D3 결정 자체를 override 하지 않음. "ca-tmpl D3 가 OSS 공통 표준이 아닌 자체 stricter stance" 임을 입증하는 CONTRARY evidence 로만 사용 | | D8 | application `@Transactional` 직접 import 금지 | **CONTRARY** (보조) — Buckpal application service 가 `@Transactional` 직접 부착. `feature-architecture-enforcement-rules` D8 이 `feature-application-port-usecase-contract` 를 근거로 인용하므로 간접 CONTRARY | `BUCKPAL-TX-C1`, `BUCKPAL-TX-C2` | `engineering-blog` | D8 원 근거는 `feature-application-port-usecase-contract`. 본 자료는 보조 CONTRARY evidence. D8 결정을 override 하지 않음 | | D1, D2, D4~D7, D9~D12 | 기타 결정 | **NOT APPLICABLE** — 이 자료는 ArchUnit DSL 사용 사실(Q3), domain purity allowlist(Q1/Q2) 만 증명. 나머지 결정(Gradle module boundary, shared-contract scope, sample-ticket 금지, ArchUnit fail mode 등)에 대한 직접 claim 없음 | — | — | UNSUPPORTED_DECISION 아님 — 이 자료의 범위 밖 결정들. 기존 cited sources 가 별도로 지원 | ### Parent: feature-application-port-usecase-contract | Decision ID | Decision (요약) | This source's role | Supporting Claim IDs | Evidence Strength | Notes | |---|---|---|---|---|---| | D3 | application use case 가 transaction boundary owner — but Spring `@Transactional` 직접 import 금지, `TransactionPort` 사용 | **CONTRARY** — Buckpal 은 `TransactionPort` abstraction 없이 `@Transactional` 직접 부착. 이 자료는 "다수파" 가 어떻게 구현하는지를 구체적 OSS 코드로 뒷받침 | `BUCKPAL-TX-C1`, `BUCKPAL-TX-C2` | `engineering-blog` | D3 자체는 `UNIL-TX-C1/C2`, `VSOUM-TX-C1/C2` 로 지원됨 (`company-case-study`). 이 자료는 그 결정이 소수파임을 보강하는 CONTRARY evidence. D3 를 UNSUPPORTED_DECISION 으로 격하하지 않음 | | D4 | `@Transactional` 직접 부착이 hexagonal 표준 다수파임을 인정 | **SUPPORTING (CONTRARY direction)** — D4 는 ca-tmpl 이 이미 인정한 "다수파" 사실. 이 자료의 BUCKPAL-TX-C1/C2 는 그 다수파의 구체적 저명 OSS 선례를 제공 | `BUCKPAL-TX-C1`, `BUCKPAL-TX-C2` | `engineering-blog` | D4 는 이미 `AT-TX-C1`, `HEX-REFL-C1/C5` 로 지원됨. 이 자료는 추가 corroborating evidence | | D1, D2, D5~D14 | 기타 결정 | **NOT APPLICABLE** — 이 자료는 Buckpal 의 `@Transactional` 직접 사용 패턴만 증명. naming convention, CQS 분리, TransactionTemplate, Arrow Kt, AOP interceptor, pool sizing, KEYED freeze 등에 대한 직접 claim 없음 | — | — | UNSUPPORTED_DECISION 아님 — 이 자료의 범위 밖 결정들 | ### UNSUPPORTED_DECISION 목록 (이 자료 기준) 이 raw source 단독으로 아래 진술을 지지하면 UNSUPPORTED_DECISION: | 진술 | 판정 | 이유 | |---|---|---| | "Lombok 사용이 domain purity 에 문제없다" | UNSUPPORTED_DECISION | BUCKPAL-LOMBOK-C1/C2 는 Buckpal 의 설계 결정만 증명. 일반 원칙으로 확대 불가 | | "`@Transactional` 직접 부착이 Hexagonal Architecture 의 권장 패턴이다" | UNSUPPORTED_DECISION | BUCKPAL-TX-C1/C2 는 Buckpal 선례만 증명. Spring 공식 또는 Hexagonal Architecture 명세가 이 배치를 "권장" 한다고 말하지 않음 | | "ca-tmpl D3 (Lombok 금지) 결정이 잘못됐다" | UNSUPPORTED_DECISION | 이 자료는 CONTRARY evidence. override 의도 아님. D3 변경은 별도 Decision Review 필요 | | "ca-tmpl D3 (TransactionPort) 결정이 잘못됐다" | UNSUPPORTED_DECISION | 동일 — CONTRARY evidence. `UNIL-TX-C1/C2`, `VSOUM-TX-C1/C2` 가 TransactionPort 선택 근거로 별도 지원됨 | --- ## Related / 관련 - [[raw/company-tech-blogs/hexagonal-reflectoring-transactional-placement]] — 동일 저자(Tom Hombergs)의 `@Transactional` 위치에 관한 블로그 글. BUCKPAL-TX-C1 의 맥락 보완 - [[raw/official-docs/at-transactional-spring-official]] — `@Transactional` 직접 부착의 Spring 공식 지원 근거. BUCKPAL-TX-C1 와 함께 "다수파" 를 구성하는 공식 근거 - [[raw/official-docs/lombok-builder-data-features-official]] — ca-tmpl D3 의 Lombok 금지 근거. BUCKPAL-LOMBOK-C1 의 반대 방향 공식 문서 - [[raw/branch-notes/feature-architecture-enforcement-rules]] — D3 결정 원문. BUCKPAL-LOMBOK-C1/C2 가 CONTRARY evidence 로 기재되어야 하는 Decision Evidence Map 위치 - [[raw/branch-notes/feature-application-port-usecase-contract]] — D1/D3/D4 결정 원문. BUCKPAL-TX-C1/C2 가 CONTRARY evidence 로 기재되어야 하는 Decision Evidence Map 위치